sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls)
8864         {
8865             if(this.getStyle("position") == "static"){
8866                 this.setStyle("position", "relative");
8867             }
8868             if(!this._mask){
8869                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8870             }
8871             this.addClass("x-masked");
8872             this._mask.setDisplayed(true);
8873             
8874             // we wander
8875             var z = 0;
8876             var dom = this.dom
8877             while (dom && dom.style) {
8878                 if (!isNaN(parseInt(dom.style.zIndex))) {
8879                     z = Math.max(z, parseInt(dom.style.zIndex));
8880                 }
8881                 dom = dom.parentNode;
8882             }
8883             // if we are masking the body - then it hides everything..
8884             if (this.dom == document.body) {
8885                 z = 1000000;
8886                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8887                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8888             }
8889            
8890             if(typeof msg == 'string'){
8891                 if(!this._maskMsg){
8892                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8893                 }
8894                 var mm = this._maskMsg;
8895                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8896                 mm.dom.firstChild.innerHTML = msg;
8897                 mm.setDisplayed(true);
8898                 mm.center(this);
8899                 mm.setStyle('z-index', z + 102);
8900             }
8901             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8902                 this._mask.setHeight(this.getHeight());
8903             }
8904             this._mask.setStyle('z-index', z + 100);
8905             
8906             return this._mask;
8907         },
8908
8909         /**
8910          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8911          * it is cached for reuse.
8912          */
8913         unmask : function(removeEl){
8914             if(this._mask){
8915                 if(removeEl === true){
8916                     this._mask.remove();
8917                     delete this._mask;
8918                     if(this._maskMsg){
8919                         this._maskMsg.remove();
8920                         delete this._maskMsg;
8921                     }
8922                 }else{
8923                     this._mask.setDisplayed(false);
8924                     if(this._maskMsg){
8925                         this._maskMsg.setDisplayed(false);
8926                     }
8927                 }
8928             }
8929             this.removeClass("x-masked");
8930         },
8931
8932         /**
8933          * Returns true if this element is masked
8934          * @return {Boolean}
8935          */
8936         isMasked : function(){
8937             return this._mask && this._mask.isVisible();
8938         },
8939
8940         /**
8941          * Creates an iframe shim for this element to keep selects and other windowed objects from
8942          * showing through.
8943          * @return {Roo.Element} The new shim element
8944          */
8945         createShim : function(){
8946             var el = document.createElement('iframe');
8947             el.frameBorder = 'no';
8948             el.className = 'roo-shim';
8949             if(Roo.isIE && Roo.isSecure){
8950                 el.src = Roo.SSL_SECURE_URL;
8951             }
8952             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8953             shim.autoBoxAdjust = false;
8954             return shim;
8955         },
8956
8957         /**
8958          * Removes this element from the DOM and deletes it from the cache
8959          */
8960         remove : function(){
8961             if(this.dom.parentNode){
8962                 this.dom.parentNode.removeChild(this.dom);
8963             }
8964             delete El.cache[this.dom.id];
8965         },
8966
8967         /**
8968          * Sets up event handlers to add and remove a css class when the mouse is over this element
8969          * @param {String} className
8970          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8971          * mouseout events for children elements
8972          * @return {Roo.Element} this
8973          */
8974         addClassOnOver : function(className, preventFlicker){
8975             this.on("mouseover", function(){
8976                 Roo.fly(this, '_internal').addClass(className);
8977             }, this.dom);
8978             var removeFn = function(e){
8979                 if(preventFlicker !== true || !e.within(this, true)){
8980                     Roo.fly(this, '_internal').removeClass(className);
8981                 }
8982             };
8983             this.on("mouseout", removeFn, this.dom);
8984             return this;
8985         },
8986
8987         /**
8988          * Sets up event handlers to add and remove a css class when this element has the focus
8989          * @param {String} className
8990          * @return {Roo.Element} this
8991          */
8992         addClassOnFocus : function(className){
8993             this.on("focus", function(){
8994                 Roo.fly(this, '_internal').addClass(className);
8995             }, this.dom);
8996             this.on("blur", function(){
8997                 Roo.fly(this, '_internal').removeClass(className);
8998             }, this.dom);
8999             return this;
9000         },
9001         /**
9002          * 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)
9003          * @param {String} className
9004          * @return {Roo.Element} this
9005          */
9006         addClassOnClick : function(className){
9007             var dom = this.dom;
9008             this.on("mousedown", function(){
9009                 Roo.fly(dom, '_internal').addClass(className);
9010                 var d = Roo.get(document);
9011                 var fn = function(){
9012                     Roo.fly(dom, '_internal').removeClass(className);
9013                     d.removeListener("mouseup", fn);
9014                 };
9015                 d.on("mouseup", fn);
9016             });
9017             return this;
9018         },
9019
9020         /**
9021          * Stops the specified event from bubbling and optionally prevents the default action
9022          * @param {String} eventName
9023          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9024          * @return {Roo.Element} this
9025          */
9026         swallowEvent : function(eventName, preventDefault){
9027             var fn = function(e){
9028                 e.stopPropagation();
9029                 if(preventDefault){
9030                     e.preventDefault();
9031                 }
9032             };
9033             if(eventName instanceof Array){
9034                 for(var i = 0, len = eventName.length; i < len; i++){
9035                      this.on(eventName[i], fn);
9036                 }
9037                 return this;
9038             }
9039             this.on(eventName, fn);
9040             return this;
9041         },
9042
9043         /**
9044          * @private
9045          */
9046       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9047
9048         /**
9049          * Sizes this element to its parent element's dimensions performing
9050          * neccessary box adjustments.
9051          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9052          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9053          * @return {Roo.Element} this
9054          */
9055         fitToParent : function(monitorResize, targetParent) {
9056           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9057           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9058           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9059             return;
9060           }
9061           var p = Roo.get(targetParent || this.dom.parentNode);
9062           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9063           if (monitorResize === true) {
9064             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9065             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9066           }
9067           return this;
9068         },
9069
9070         /**
9071          * Gets the next sibling, skipping text nodes
9072          * @return {HTMLElement} The next sibling or null
9073          */
9074         getNextSibling : function(){
9075             var n = this.dom.nextSibling;
9076             while(n && n.nodeType != 1){
9077                 n = n.nextSibling;
9078             }
9079             return n;
9080         },
9081
9082         /**
9083          * Gets the previous sibling, skipping text nodes
9084          * @return {HTMLElement} The previous sibling or null
9085          */
9086         getPrevSibling : function(){
9087             var n = this.dom.previousSibling;
9088             while(n && n.nodeType != 1){
9089                 n = n.previousSibling;
9090             }
9091             return n;
9092         },
9093
9094
9095         /**
9096          * Appends the passed element(s) to this element
9097          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9098          * @return {Roo.Element} this
9099          */
9100         appendChild: function(el){
9101             el = Roo.get(el);
9102             el.appendTo(this);
9103             return this;
9104         },
9105
9106         /**
9107          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9108          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9109          * automatically generated with the specified attributes.
9110          * @param {HTMLElement} insertBefore (optional) a child element of this element
9111          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9112          * @return {Roo.Element} The new child element
9113          */
9114         createChild: function(config, insertBefore, returnDom){
9115             config = config || {tag:'div'};
9116             if(insertBefore){
9117                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9118             }
9119             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9120         },
9121
9122         /**
9123          * Appends this element to the passed element
9124          * @param {String/HTMLElement/Element} el The new parent element
9125          * @return {Roo.Element} this
9126          */
9127         appendTo: function(el){
9128             el = Roo.getDom(el);
9129             el.appendChild(this.dom);
9130             return this;
9131         },
9132
9133         /**
9134          * Inserts this element before the passed element in the DOM
9135          * @param {String/HTMLElement/Element} el The element to insert before
9136          * @return {Roo.Element} this
9137          */
9138         insertBefore: function(el){
9139             el = Roo.getDom(el);
9140             el.parentNode.insertBefore(this.dom, el);
9141             return this;
9142         },
9143
9144         /**
9145          * Inserts this element after the passed element in the DOM
9146          * @param {String/HTMLElement/Element} el The element to insert after
9147          * @return {Roo.Element} this
9148          */
9149         insertAfter: function(el){
9150             el = Roo.getDom(el);
9151             el.parentNode.insertBefore(this.dom, el.nextSibling);
9152             return this;
9153         },
9154
9155         /**
9156          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9157          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9158          * @return {Roo.Element} The new child
9159          */
9160         insertFirst: function(el, returnDom){
9161             el = el || {};
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 return this.createChild(el, this.dom.firstChild, returnDom);
9164             }else{
9165                 el = Roo.getDom(el);
9166                 this.dom.insertBefore(el, this.dom.firstChild);
9167                 return !returnDom ? Roo.get(el) : el;
9168             }
9169         },
9170
9171         /**
9172          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9173          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9174          * @param {String} where (optional) 'before' or 'after' defaults to before
9175          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9176          * @return {Roo.Element} the inserted Element
9177          */
9178         insertSibling: function(el, where, returnDom){
9179             where = where ? where.toLowerCase() : 'before';
9180             el = el || {};
9181             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9182
9183             if(typeof el == 'object' && !el.nodeType){ // dh config
9184                 if(where == 'after' && !this.dom.nextSibling){
9185                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9186                 }else{
9187                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9188                 }
9189
9190             }else{
9191                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9192                             where == 'before' ? this.dom : this.dom.nextSibling);
9193                 if(!returnDom){
9194                     rt = Roo.get(rt);
9195                 }
9196             }
9197             return rt;
9198         },
9199
9200         /**
9201          * Creates and wraps this element with another element
9202          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9203          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9204          * @return {HTMLElement/Element} The newly created wrapper element
9205          */
9206         wrap: function(config, returnDom){
9207             if(!config){
9208                 config = {tag: "div"};
9209             }
9210             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9211             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9212             return newEl;
9213         },
9214
9215         /**
9216          * Replaces the passed element with this element
9217          * @param {String/HTMLElement/Element} el The element to replace
9218          * @return {Roo.Element} this
9219          */
9220         replace: function(el){
9221             el = Roo.get(el);
9222             this.insertBefore(el);
9223             el.remove();
9224             return this;
9225         },
9226
9227         /**
9228          * Inserts an html fragment into this element
9229          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9230          * @param {String} html The HTML fragment
9231          * @param {Boolean} returnEl True to return an Roo.Element
9232          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9233          */
9234         insertHtml : function(where, html, returnEl){
9235             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9236             return returnEl ? Roo.get(el) : el;
9237         },
9238
9239         /**
9240          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9241          * @param {Object} o The object with the attributes
9242          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9243          * @return {Roo.Element} this
9244          */
9245         set : function(o, useSet){
9246             var el = this.dom;
9247             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9248             for(var attr in o){
9249                 if(attr == "style" || typeof o[attr] == "function") continue;
9250                 if(attr=="cls"){
9251                     el.className = o["cls"];
9252                 }else{
9253                     if(useSet) el.setAttribute(attr, o[attr]);
9254                     else el[attr] = o[attr];
9255                 }
9256             }
9257             if(o.style){
9258                 Roo.DomHelper.applyStyles(el, o.style);
9259             }
9260             return this;
9261         },
9262
9263         /**
9264          * Convenience method for constructing a KeyMap
9265          * @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:
9266          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9267          * @param {Function} fn The function to call
9268          * @param {Object} scope (optional) The scope of the function
9269          * @return {Roo.KeyMap} The KeyMap created
9270          */
9271         addKeyListener : function(key, fn, scope){
9272             var config;
9273             if(typeof key != "object" || key instanceof Array){
9274                 config = {
9275                     key: key,
9276                     fn: fn,
9277                     scope: scope
9278                 };
9279             }else{
9280                 config = {
9281                     key : key.key,
9282                     shift : key.shift,
9283                     ctrl : key.ctrl,
9284                     alt : key.alt,
9285                     fn: fn,
9286                     scope: scope
9287                 };
9288             }
9289             return new Roo.KeyMap(this, config);
9290         },
9291
9292         /**
9293          * Creates a KeyMap for this element
9294          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9295          * @return {Roo.KeyMap} The KeyMap created
9296          */
9297         addKeyMap : function(config){
9298             return new Roo.KeyMap(this, config);
9299         },
9300
9301         /**
9302          * Returns true if this element is scrollable.
9303          * @return {Boolean}
9304          */
9305          isScrollable : function(){
9306             var dom = this.dom;
9307             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9308         },
9309
9310         /**
9311          * 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().
9312          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9313          * @param {Number} value The new scroll value
9314          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9315          * @return {Element} this
9316          */
9317
9318         scrollTo : function(side, value, animate){
9319             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9320             if(!animate || !A){
9321                 this.dom[prop] = value;
9322             }else{
9323                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9324                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9325             }
9326             return this;
9327         },
9328
9329         /**
9330          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9331          * within this element's scrollable range.
9332          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9333          * @param {Number} distance How far to scroll the element in pixels
9334          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9335          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9336          * was scrolled as far as it could go.
9337          */
9338          scroll : function(direction, distance, animate){
9339              if(!this.isScrollable()){
9340                  return;
9341              }
9342              var el = this.dom;
9343              var l = el.scrollLeft, t = el.scrollTop;
9344              var w = el.scrollWidth, h = el.scrollHeight;
9345              var cw = el.clientWidth, ch = el.clientHeight;
9346              direction = direction.toLowerCase();
9347              var scrolled = false;
9348              var a = this.preanim(arguments, 2);
9349              switch(direction){
9350                  case "l":
9351                  case "left":
9352                      if(w - l > cw){
9353                          var v = Math.min(l + distance, w-cw);
9354                          this.scrollTo("left", v, a);
9355                          scrolled = true;
9356                      }
9357                      break;
9358                 case "r":
9359                 case "right":
9360                      if(l > 0){
9361                          var v = Math.max(l - distance, 0);
9362                          this.scrollTo("left", v, a);
9363                          scrolled = true;
9364                      }
9365                      break;
9366                 case "t":
9367                 case "top":
9368                 case "up":
9369                      if(t > 0){
9370                          var v = Math.max(t - distance, 0);
9371                          this.scrollTo("top", v, a);
9372                          scrolled = true;
9373                      }
9374                      break;
9375                 case "b":
9376                 case "bottom":
9377                 case "down":
9378                      if(h - t > ch){
9379                          var v = Math.min(t + distance, h-ch);
9380                          this.scrollTo("top", v, a);
9381                          scrolled = true;
9382                      }
9383                      break;
9384              }
9385              return scrolled;
9386         },
9387
9388         /**
9389          * Translates the passed page coordinates into left/top css values for this element
9390          * @param {Number/Array} x The page x or an array containing [x, y]
9391          * @param {Number} y The page y
9392          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9393          */
9394         translatePoints : function(x, y){
9395             if(typeof x == 'object' || x instanceof Array){
9396                 y = x[1]; x = x[0];
9397             }
9398             var p = this.getStyle('position');
9399             var o = this.getXY();
9400
9401             var l = parseInt(this.getStyle('left'), 10);
9402             var t = parseInt(this.getStyle('top'), 10);
9403
9404             if(isNaN(l)){
9405                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9406             }
9407             if(isNaN(t)){
9408                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9409             }
9410
9411             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9412         },
9413
9414         /**
9415          * Returns the current scroll position of the element.
9416          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9417          */
9418         getScroll : function(){
9419             var d = this.dom, doc = document;
9420             if(d == doc || d == doc.body){
9421                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9422                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9423                 return {left: l, top: t};
9424             }else{
9425                 return {left: d.scrollLeft, top: d.scrollTop};
9426             }
9427         },
9428
9429         /**
9430          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9431          * are convert to standard 6 digit hex color.
9432          * @param {String} attr The css attribute
9433          * @param {String} defaultValue The default value to use when a valid color isn't found
9434          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9435          * YUI color anims.
9436          */
9437         getColor : function(attr, defaultValue, prefix){
9438             var v = this.getStyle(attr);
9439             if(!v || v == "transparent" || v == "inherit") {
9440                 return defaultValue;
9441             }
9442             var color = typeof prefix == "undefined" ? "#" : prefix;
9443             if(v.substr(0, 4) == "rgb("){
9444                 var rvs = v.slice(4, v.length -1).split(",");
9445                 for(var i = 0; i < 3; i++){
9446                     var h = parseInt(rvs[i]).toString(16);
9447                     if(h < 16){
9448                         h = "0" + h;
9449                     }
9450                     color += h;
9451                 }
9452             } else {
9453                 if(v.substr(0, 1) == "#"){
9454                     if(v.length == 4) {
9455                         for(var i = 1; i < 4; i++){
9456                             var c = v.charAt(i);
9457                             color +=  c + c;
9458                         }
9459                     }else if(v.length == 7){
9460                         color += v.substr(1);
9461                     }
9462                 }
9463             }
9464             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9465         },
9466
9467         /**
9468          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9469          * gradient background, rounded corners and a 4-way shadow.
9470          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9471          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9472          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9473          * @return {Roo.Element} this
9474          */
9475         boxWrap : function(cls){
9476             cls = cls || 'x-box';
9477             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9478             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9479             return el;
9480         },
9481
9482         /**
9483          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9484          * @param {String} namespace The namespace in which to look for the attribute
9485          * @param {String} name The attribute name
9486          * @return {String} The attribute value
9487          */
9488         getAttributeNS : Roo.isIE ? function(ns, name){
9489             var d = this.dom;
9490             var type = typeof d[ns+":"+name];
9491             if(type != 'undefined' && type != 'unknown'){
9492                 return d[ns+":"+name];
9493             }
9494             return d[name];
9495         } : function(ns, name){
9496             var d = this.dom;
9497             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9498         }
9499     };
9500
9501     var ep = El.prototype;
9502
9503     /**
9504      * Appends an event handler (Shorthand for addListener)
9505      * @param {String}   eventName     The type of event to append
9506      * @param {Function} fn        The method the event invokes
9507      * @param {Object} scope       (optional) The scope (this object) of the fn
9508      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9509      * @method
9510      */
9511     ep.on = ep.addListener;
9512         // backwards compat
9513     ep.mon = ep.addListener;
9514
9515     /**
9516      * Removes an event handler from this element (shorthand for removeListener)
9517      * @param {String} eventName the type of event to remove
9518      * @param {Function} fn the method the event invokes
9519      * @return {Roo.Element} this
9520      * @method
9521      */
9522     ep.un = ep.removeListener;
9523
9524     /**
9525      * true to automatically adjust width and height settings for box-model issues (default to true)
9526      */
9527     ep.autoBoxAdjust = true;
9528
9529     // private
9530     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9531
9532     // private
9533     El.addUnits = function(v, defaultUnit){
9534         if(v === "" || v == "auto"){
9535             return v;
9536         }
9537         if(v === undefined){
9538             return '';
9539         }
9540         if(typeof v == "number" || !El.unitPattern.test(v)){
9541             return v + (defaultUnit || 'px');
9542         }
9543         return v;
9544     };
9545
9546     // special markup used throughout Roo when box wrapping elements
9547     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>';
9548     /**
9549      * Visibility mode constant - Use visibility to hide element
9550      * @static
9551      * @type Number
9552      */
9553     El.VISIBILITY = 1;
9554     /**
9555      * Visibility mode constant - Use display to hide element
9556      * @static
9557      * @type Number
9558      */
9559     El.DISPLAY = 2;
9560
9561     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9562     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9563     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9564
9565
9566
9567     /**
9568      * @private
9569      */
9570     El.cache = {};
9571
9572     var docEl;
9573
9574     /**
9575      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9576      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9577      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9578      * @return {Element} The Element object
9579      * @static
9580      */
9581     El.get = function(el){
9582         var ex, elm, id;
9583         if(!el){ return null; }
9584         if(typeof el == "string"){ // element id
9585             if(!(elm = document.getElementById(el))){
9586                 return null;
9587             }
9588             if(ex = El.cache[el]){
9589                 ex.dom = elm;
9590             }else{
9591                 ex = El.cache[el] = new El(elm);
9592             }
9593             return ex;
9594         }else if(el.tagName){ // dom element
9595             if(!(id = el.id)){
9596                 id = Roo.id(el);
9597             }
9598             if(ex = El.cache[id]){
9599                 ex.dom = el;
9600             }else{
9601                 ex = El.cache[id] = new El(el);
9602             }
9603             return ex;
9604         }else if(el instanceof El){
9605             if(el != docEl){
9606                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9607                                                               // catch case where it hasn't been appended
9608                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9609             }
9610             return el;
9611         }else if(el.isComposite){
9612             return el;
9613         }else if(el instanceof Array){
9614             return El.select(el);
9615         }else if(el == document){
9616             // create a bogus element object representing the document object
9617             if(!docEl){
9618                 var f = function(){};
9619                 f.prototype = El.prototype;
9620                 docEl = new f();
9621                 docEl.dom = document;
9622             }
9623             return docEl;
9624         }
9625         return null;
9626     };
9627
9628     // private
9629     El.uncache = function(el){
9630         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9631             if(a[i]){
9632                 delete El.cache[a[i].id || a[i]];
9633             }
9634         }
9635     };
9636
9637     // private
9638     // Garbage collection - uncache elements/purge listeners on orphaned elements
9639     // so we don't hold a reference and cause the browser to retain them
9640     El.garbageCollect = function(){
9641         if(!Roo.enableGarbageCollector){
9642             clearInterval(El.collectorThread);
9643             return;
9644         }
9645         for(var eid in El.cache){
9646             var el = El.cache[eid], d = el.dom;
9647             // -------------------------------------------------------
9648             // Determining what is garbage:
9649             // -------------------------------------------------------
9650             // !d
9651             // dom node is null, definitely garbage
9652             // -------------------------------------------------------
9653             // !d.parentNode
9654             // no parentNode == direct orphan, definitely garbage
9655             // -------------------------------------------------------
9656             // !d.offsetParent && !document.getElementById(eid)
9657             // display none elements have no offsetParent so we will
9658             // also try to look it up by it's id. However, check
9659             // offsetParent first so we don't do unneeded lookups.
9660             // This enables collection of elements that are not orphans
9661             // directly, but somewhere up the line they have an orphan
9662             // parent.
9663             // -------------------------------------------------------
9664             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9665                 delete El.cache[eid];
9666                 if(d && Roo.enableListenerCollection){
9667                     E.purgeElement(d);
9668                 }
9669             }
9670         }
9671     }
9672     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9673
9674
9675     // dom is optional
9676     El.Flyweight = function(dom){
9677         this.dom = dom;
9678     };
9679     El.Flyweight.prototype = El.prototype;
9680
9681     El._flyweights = {};
9682     /**
9683      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9684      * the dom node can be overwritten by other code.
9685      * @param {String/HTMLElement} el The dom node or id
9686      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9687      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9688      * @static
9689      * @return {Element} The shared Element object
9690      */
9691     El.fly = function(el, named){
9692         named = named || '_global';
9693         el = Roo.getDom(el);
9694         if(!el){
9695             return null;
9696         }
9697         if(!El._flyweights[named]){
9698             El._flyweights[named] = new El.Flyweight();
9699         }
9700         El._flyweights[named].dom = el;
9701         return El._flyweights[named];
9702     };
9703
9704     /**
9705      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9706      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9707      * Shorthand of {@link Roo.Element#get}
9708      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9709      * @return {Element} The Element object
9710      * @member Roo
9711      * @method get
9712      */
9713     Roo.get = El.get;
9714     /**
9715      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9716      * the dom node can be overwritten by other code.
9717      * Shorthand of {@link Roo.Element#fly}
9718      * @param {String/HTMLElement} el The dom node or id
9719      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9720      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9721      * @static
9722      * @return {Element} The shared Element object
9723      * @member Roo
9724      * @method fly
9725      */
9726     Roo.fly = El.fly;
9727
9728     // speedy lookup for elements never to box adjust
9729     var noBoxAdjust = Roo.isStrict ? {
9730         select:1
9731     } : {
9732         input:1, select:1, textarea:1
9733     };
9734     if(Roo.isIE || Roo.isGecko){
9735         noBoxAdjust['button'] = 1;
9736     }
9737
9738
9739     Roo.EventManager.on(window, 'unload', function(){
9740         delete El.cache;
9741         delete El._flyweights;
9742     });
9743 })();
9744
9745
9746
9747
9748 if(Roo.DomQuery){
9749     Roo.Element.selectorFunction = Roo.DomQuery.select;
9750 }
9751
9752 Roo.Element.select = function(selector, unique, root){
9753     var els;
9754     if(typeof selector == "string"){
9755         els = Roo.Element.selectorFunction(selector, root);
9756     }else if(selector.length !== undefined){
9757         els = selector;
9758     }else{
9759         throw "Invalid selector";
9760     }
9761     if(unique === true){
9762         return new Roo.CompositeElement(els);
9763     }else{
9764         return new Roo.CompositeElementLite(els);
9765     }
9766 };
9767 /**
9768  * Selects elements based on the passed CSS selector to enable working on them as 1.
9769  * @param {String/Array} selector The CSS selector or an array of elements
9770  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9771  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9772  * @return {CompositeElementLite/CompositeElement}
9773  * @member Roo
9774  * @method select
9775  */
9776 Roo.select = Roo.Element.select;
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791 /*
9792  * Based on:
9793  * Ext JS Library 1.1.1
9794  * Copyright(c) 2006-2007, Ext JS, LLC.
9795  *
9796  * Originally Released Under LGPL - original licence link has changed is not relivant.
9797  *
9798  * Fork - LGPL
9799  * <script type="text/javascript">
9800  */
9801
9802
9803
9804 //Notifies Element that fx methods are available
9805 Roo.enableFx = true;
9806
9807 /**
9808  * @class Roo.Fx
9809  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9810  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9811  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9812  * Element effects to work.</p><br/>
9813  *
9814  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9815  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9816  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9817  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9818  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9819  * expected results and should be done with care.</p><br/>
9820  *
9821  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9822  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9823 <pre>
9824 Value  Description
9825 -----  -----------------------------
9826 tl     The top left corner
9827 t      The center of the top edge
9828 tr     The top right corner
9829 l      The center of the left edge
9830 r      The center of the right edge
9831 bl     The bottom left corner
9832 b      The center of the bottom edge
9833 br     The bottom right corner
9834 </pre>
9835  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9836  * below are common options that can be passed to any Fx method.</b>
9837  * @cfg {Function} callback A function called when the effect is finished
9838  * @cfg {Object} scope The scope of the effect function
9839  * @cfg {String} easing A valid Easing value for the effect
9840  * @cfg {String} afterCls A css class to apply after the effect
9841  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9842  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9843  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9844  * effects that end with the element being visually hidden, ignored otherwise)
9845  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9846  * a function which returns such a specification that will be applied to the Element after the effect finishes
9847  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9848  * @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
9849  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9850  */
9851 Roo.Fx = {
9852         /**
9853          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9854          * origin for the slide effect.  This function automatically handles wrapping the element with
9855          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9856          * Usage:
9857          *<pre><code>
9858 // default: slide the element in from the top
9859 el.slideIn();
9860
9861 // custom: slide the element in from the right with a 2-second duration
9862 el.slideIn('r', { duration: 2 });
9863
9864 // common config options shown with default values
9865 el.slideIn('t', {
9866     easing: 'easeOut',
9867     duration: .5
9868 });
9869 </code></pre>
9870          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9871          * @param {Object} options (optional) Object literal with any of the Fx config options
9872          * @return {Roo.Element} The Element
9873          */
9874     slideIn : function(anchor, o){
9875         var el = this.getFxEl();
9876         o = o || {};
9877
9878         el.queueFx(o, function(){
9879
9880             anchor = anchor || "t";
9881
9882             // fix display to visibility
9883             this.fixDisplay();
9884
9885             // restore values after effect
9886             var r = this.getFxRestore();
9887             var b = this.getBox();
9888             // fixed size for slide
9889             this.setSize(b);
9890
9891             // wrap if needed
9892             var wrap = this.fxWrap(r.pos, o, "hidden");
9893
9894             var st = this.dom.style;
9895             st.visibility = "visible";
9896             st.position = "absolute";
9897
9898             // clear out temp styles after slide and unwrap
9899             var after = function(){
9900                 el.fxUnwrap(wrap, r.pos, o);
9901                 st.width = r.width;
9902                 st.height = r.height;
9903                 el.afterFx(o);
9904             };
9905             // time to calc the positions
9906             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9907
9908             switch(anchor.toLowerCase()){
9909                 case "t":
9910                     wrap.setSize(b.width, 0);
9911                     st.left = st.bottom = "0";
9912                     a = {height: bh};
9913                 break;
9914                 case "l":
9915                     wrap.setSize(0, b.height);
9916                     st.right = st.top = "0";
9917                     a = {width: bw};
9918                 break;
9919                 case "r":
9920                     wrap.setSize(0, b.height);
9921                     wrap.setX(b.right);
9922                     st.left = st.top = "0";
9923                     a = {width: bw, points: pt};
9924                 break;
9925                 case "b":
9926                     wrap.setSize(b.width, 0);
9927                     wrap.setY(b.bottom);
9928                     st.left = st.top = "0";
9929                     a = {height: bh, points: pt};
9930                 break;
9931                 case "tl":
9932                     wrap.setSize(0, 0);
9933                     st.right = st.bottom = "0";
9934                     a = {width: bw, height: bh};
9935                 break;
9936                 case "bl":
9937                     wrap.setSize(0, 0);
9938                     wrap.setY(b.y+b.height);
9939                     st.right = st.top = "0";
9940                     a = {width: bw, height: bh, points: pt};
9941                 break;
9942                 case "br":
9943                     wrap.setSize(0, 0);
9944                     wrap.setXY([b.right, b.bottom]);
9945                     st.left = st.top = "0";
9946                     a = {width: bw, height: bh, points: pt};
9947                 break;
9948                 case "tr":
9949                     wrap.setSize(0, 0);
9950                     wrap.setX(b.x+b.width);
9951                     st.left = st.bottom = "0";
9952                     a = {width: bw, height: bh, points: pt};
9953                 break;
9954             }
9955             this.dom.style.visibility = "visible";
9956             wrap.show();
9957
9958             arguments.callee.anim = wrap.fxanim(a,
9959                 o,
9960                 'motion',
9961                 .5,
9962                 'easeOut', after);
9963         });
9964         return this;
9965     },
9966     
9967         /**
9968          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9969          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9970          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9971          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9972          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9973          * Usage:
9974          *<pre><code>
9975 // default: slide the element out to the top
9976 el.slideOut();
9977
9978 // custom: slide the element out to the right with a 2-second duration
9979 el.slideOut('r', { duration: 2 });
9980
9981 // common config options shown with default values
9982 el.slideOut('t', {
9983     easing: 'easeOut',
9984     duration: .5,
9985     remove: false,
9986     useDisplay: false
9987 });
9988 </code></pre>
9989          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9990          * @param {Object} options (optional) Object literal with any of the Fx config options
9991          * @return {Roo.Element} The Element
9992          */
9993     slideOut : function(anchor, o){
9994         var el = this.getFxEl();
9995         o = o || {};
9996
9997         el.queueFx(o, function(){
9998
9999             anchor = anchor || "t";
10000
10001             // restore values after effect
10002             var r = this.getFxRestore();
10003             
10004             var b = this.getBox();
10005             // fixed size for slide
10006             this.setSize(b);
10007
10008             // wrap if needed
10009             var wrap = this.fxWrap(r.pos, o, "visible");
10010
10011             var st = this.dom.style;
10012             st.visibility = "visible";
10013             st.position = "absolute";
10014
10015             wrap.setSize(b);
10016
10017             var after = function(){
10018                 if(o.useDisplay){
10019                     el.setDisplayed(false);
10020                 }else{
10021                     el.hide();
10022                 }
10023
10024                 el.fxUnwrap(wrap, r.pos, o);
10025
10026                 st.width = r.width;
10027                 st.height = r.height;
10028
10029                 el.afterFx(o);
10030             };
10031
10032             var a, zero = {to: 0};
10033             switch(anchor.toLowerCase()){
10034                 case "t":
10035                     st.left = st.bottom = "0";
10036                     a = {height: zero};
10037                 break;
10038                 case "l":
10039                     st.right = st.top = "0";
10040                     a = {width: zero};
10041                 break;
10042                 case "r":
10043                     st.left = st.top = "0";
10044                     a = {width: zero, points: {to:[b.right, b.y]}};
10045                 break;
10046                 case "b":
10047                     st.left = st.top = "0";
10048                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10049                 break;
10050                 case "tl":
10051                     st.right = st.bottom = "0";
10052                     a = {width: zero, height: zero};
10053                 break;
10054                 case "bl":
10055                     st.right = st.top = "0";
10056                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10057                 break;
10058                 case "br":
10059                     st.left = st.top = "0";
10060                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10061                 break;
10062                 case "tr":
10063                     st.left = st.bottom = "0";
10064                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10065                 break;
10066             }
10067
10068             arguments.callee.anim = wrap.fxanim(a,
10069                 o,
10070                 'motion',
10071                 .5,
10072                 "easeOut", after);
10073         });
10074         return this;
10075     },
10076
10077         /**
10078          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10079          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10080          * The element must be removed from the DOM using the 'remove' config option if desired.
10081          * Usage:
10082          *<pre><code>
10083 // default
10084 el.puff();
10085
10086 // common config options shown with default values
10087 el.puff({
10088     easing: 'easeOut',
10089     duration: .5,
10090     remove: false,
10091     useDisplay: false
10092 });
10093 </code></pre>
10094          * @param {Object} options (optional) Object literal with any of the Fx config options
10095          * @return {Roo.Element} The Element
10096          */
10097     puff : function(o){
10098         var el = this.getFxEl();
10099         o = o || {};
10100
10101         el.queueFx(o, function(){
10102             this.clearOpacity();
10103             this.show();
10104
10105             // restore values after effect
10106             var r = this.getFxRestore();
10107             var st = this.dom.style;
10108
10109             var after = function(){
10110                 if(o.useDisplay){
10111                     el.setDisplayed(false);
10112                 }else{
10113                     el.hide();
10114                 }
10115
10116                 el.clearOpacity();
10117
10118                 el.setPositioning(r.pos);
10119                 st.width = r.width;
10120                 st.height = r.height;
10121                 st.fontSize = '';
10122                 el.afterFx(o);
10123             };
10124
10125             var width = this.getWidth();
10126             var height = this.getHeight();
10127
10128             arguments.callee.anim = this.fxanim({
10129                     width : {to: this.adjustWidth(width * 2)},
10130                     height : {to: this.adjustHeight(height * 2)},
10131                     points : {by: [-(width * .5), -(height * .5)]},
10132                     opacity : {to: 0},
10133                     fontSize: {to:200, unit: "%"}
10134                 },
10135                 o,
10136                 'motion',
10137                 .5,
10138                 "easeOut", after);
10139         });
10140         return this;
10141     },
10142
10143         /**
10144          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10145          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10146          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10147          * Usage:
10148          *<pre><code>
10149 // default
10150 el.switchOff();
10151
10152 // all config options shown with default values
10153 el.switchOff({
10154     easing: 'easeIn',
10155     duration: .3,
10156     remove: false,
10157     useDisplay: false
10158 });
10159 </code></pre>
10160          * @param {Object} options (optional) Object literal with any of the Fx config options
10161          * @return {Roo.Element} The Element
10162          */
10163     switchOff : function(o){
10164         var el = this.getFxEl();
10165         o = o || {};
10166
10167         el.queueFx(o, function(){
10168             this.clearOpacity();
10169             this.clip();
10170
10171             // restore values after effect
10172             var r = this.getFxRestore();
10173             var st = this.dom.style;
10174
10175             var after = function(){
10176                 if(o.useDisplay){
10177                     el.setDisplayed(false);
10178                 }else{
10179                     el.hide();
10180                 }
10181
10182                 el.clearOpacity();
10183                 el.setPositioning(r.pos);
10184                 st.width = r.width;
10185                 st.height = r.height;
10186
10187                 el.afterFx(o);
10188             };
10189
10190             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10191                 this.clearOpacity();
10192                 (function(){
10193                     this.fxanim({
10194                         height:{to:1},
10195                         points:{by:[0, this.getHeight() * .5]}
10196                     }, o, 'motion', 0.3, 'easeIn', after);
10197                 }).defer(100, this);
10198             });
10199         });
10200         return this;
10201     },
10202
10203     /**
10204      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10205      * changed using the "attr" config option) and then fading back to the original color. If no original
10206      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10207      * Usage:
10208 <pre><code>
10209 // default: highlight background to yellow
10210 el.highlight();
10211
10212 // custom: highlight foreground text to blue for 2 seconds
10213 el.highlight("0000ff", { attr: 'color', duration: 2 });
10214
10215 // common config options shown with default values
10216 el.highlight("ffff9c", {
10217     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10218     endColor: (current color) or "ffffff",
10219     easing: 'easeIn',
10220     duration: 1
10221 });
10222 </code></pre>
10223      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10224      * @param {Object} options (optional) Object literal with any of the Fx config options
10225      * @return {Roo.Element} The Element
10226      */ 
10227     highlight : function(color, o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230
10231         el.queueFx(o, function(){
10232             color = color || "ffff9c";
10233             attr = o.attr || "backgroundColor";
10234
10235             this.clearOpacity();
10236             this.show();
10237
10238             var origColor = this.getColor(attr);
10239             var restoreColor = this.dom.style[attr];
10240             endColor = (o.endColor || origColor) || "ffffff";
10241
10242             var after = function(){
10243                 el.dom.style[attr] = restoreColor;
10244                 el.afterFx(o);
10245             };
10246
10247             var a = {};
10248             a[attr] = {from: color, to: endColor};
10249             arguments.callee.anim = this.fxanim(a,
10250                 o,
10251                 'color',
10252                 1,
10253                 'easeIn', after);
10254         });
10255         return this;
10256     },
10257
10258    /**
10259     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10260     * Usage:
10261 <pre><code>
10262 // default: a single light blue ripple
10263 el.frame();
10264
10265 // custom: 3 red ripples lasting 3 seconds total
10266 el.frame("ff0000", 3, { duration: 3 });
10267
10268 // common config options shown with default values
10269 el.frame("C3DAF9", 1, {
10270     duration: 1 //duration of entire animation (not each individual ripple)
10271     // Note: Easing is not configurable and will be ignored if included
10272 });
10273 </code></pre>
10274     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10275     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10276     * @param {Object} options (optional) Object literal with any of the Fx config options
10277     * @return {Roo.Element} The Element
10278     */
10279     frame : function(color, count, o){
10280         var el = this.getFxEl();
10281         o = o || {};
10282
10283         el.queueFx(o, function(){
10284             color = color || "#C3DAF9";
10285             if(color.length == 6){
10286                 color = "#" + color;
10287             }
10288             count = count || 1;
10289             duration = o.duration || 1;
10290             this.show();
10291
10292             var b = this.getBox();
10293             var animFn = function(){
10294                 var proxy = this.createProxy({
10295
10296                      style:{
10297                         visbility:"hidden",
10298                         position:"absolute",
10299                         "z-index":"35000", // yee haw
10300                         border:"0px solid " + color
10301                      }
10302                   });
10303                 var scale = Roo.isBorderBox ? 2 : 1;
10304                 proxy.animate({
10305                     top:{from:b.y, to:b.y - 20},
10306                     left:{from:b.x, to:b.x - 20},
10307                     borderWidth:{from:0, to:10},
10308                     opacity:{from:1, to:0},
10309                     height:{from:b.height, to:(b.height + (20*scale))},
10310                     width:{from:b.width, to:(b.width + (20*scale))}
10311                 }, duration, function(){
10312                     proxy.remove();
10313                 });
10314                 if(--count > 0){
10315                      animFn.defer((duration/2)*1000, this);
10316                 }else{
10317                     el.afterFx(o);
10318                 }
10319             };
10320             animFn.call(this);
10321         });
10322         return this;
10323     },
10324
10325    /**
10326     * Creates a pause before any subsequent queued effects begin.  If there are
10327     * no effects queued after the pause it will have no effect.
10328     * Usage:
10329 <pre><code>
10330 el.pause(1);
10331 </code></pre>
10332     * @param {Number} seconds The length of time to pause (in seconds)
10333     * @return {Roo.Element} The Element
10334     */
10335     pause : function(seconds){
10336         var el = this.getFxEl();
10337         var o = {};
10338
10339         el.queueFx(o, function(){
10340             setTimeout(function(){
10341                 el.afterFx(o);
10342             }, seconds * 1000);
10343         });
10344         return this;
10345     },
10346
10347    /**
10348     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10349     * using the "endOpacity" config option.
10350     * Usage:
10351 <pre><code>
10352 // default: fade in from opacity 0 to 100%
10353 el.fadeIn();
10354
10355 // custom: fade in from opacity 0 to 75% over 2 seconds
10356 el.fadeIn({ endOpacity: .75, duration: 2});
10357
10358 // common config options shown with default values
10359 el.fadeIn({
10360     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10361     easing: 'easeOut',
10362     duration: .5
10363 });
10364 </code></pre>
10365     * @param {Object} options (optional) Object literal with any of the Fx config options
10366     * @return {Roo.Element} The Element
10367     */
10368     fadeIn : function(o){
10369         var el = this.getFxEl();
10370         o = o || {};
10371         el.queueFx(o, function(){
10372             this.setOpacity(0);
10373             this.fixDisplay();
10374             this.dom.style.visibility = 'visible';
10375             var to = o.endOpacity || 1;
10376             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10377                 o, null, .5, "easeOut", function(){
10378                 if(to == 1){
10379                     this.clearOpacity();
10380                 }
10381                 el.afterFx(o);
10382             });
10383         });
10384         return this;
10385     },
10386
10387    /**
10388     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10389     * using the "endOpacity" config option.
10390     * Usage:
10391 <pre><code>
10392 // default: fade out from the element's current opacity to 0
10393 el.fadeOut();
10394
10395 // custom: fade out from the element's current opacity to 25% over 2 seconds
10396 el.fadeOut({ endOpacity: .25, duration: 2});
10397
10398 // common config options shown with default values
10399 el.fadeOut({
10400     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10401     easing: 'easeOut',
10402     duration: .5
10403     remove: false,
10404     useDisplay: false
10405 });
10406 </code></pre>
10407     * @param {Object} options (optional) Object literal with any of the Fx config options
10408     * @return {Roo.Element} The Element
10409     */
10410     fadeOut : function(o){
10411         var el = this.getFxEl();
10412         o = o || {};
10413         el.queueFx(o, function(){
10414             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10415                 o, null, .5, "easeOut", function(){
10416                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10417                      this.dom.style.display = "none";
10418                 }else{
10419                      this.dom.style.visibility = "hidden";
10420                 }
10421                 this.clearOpacity();
10422                 el.afterFx(o);
10423             });
10424         });
10425         return this;
10426     },
10427
10428    /**
10429     * Animates the transition of an element's dimensions from a starting height/width
10430     * to an ending height/width.
10431     * Usage:
10432 <pre><code>
10433 // change height and width to 100x100 pixels
10434 el.scale(100, 100);
10435
10436 // common config options shown with default values.  The height and width will default to
10437 // the element's existing values if passed as null.
10438 el.scale(
10439     [element's width],
10440     [element's height], {
10441     easing: 'easeOut',
10442     duration: .35
10443 });
10444 </code></pre>
10445     * @param {Number} width  The new width (pass undefined to keep the original width)
10446     * @param {Number} height  The new height (pass undefined to keep the original height)
10447     * @param {Object} options (optional) Object literal with any of the Fx config options
10448     * @return {Roo.Element} The Element
10449     */
10450     scale : function(w, h, o){
10451         this.shift(Roo.apply({}, o, {
10452             width: w,
10453             height: h
10454         }));
10455         return this;
10456     },
10457
10458    /**
10459     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10460     * Any of these properties not specified in the config object will not be changed.  This effect 
10461     * requires that at least one new dimension, position or opacity setting must be passed in on
10462     * the config object in order for the function to have any effect.
10463     * Usage:
10464 <pre><code>
10465 // slide the element horizontally to x position 200 while changing the height and opacity
10466 el.shift({ x: 200, height: 50, opacity: .8 });
10467
10468 // common config options shown with default values.
10469 el.shift({
10470     width: [element's width],
10471     height: [element's height],
10472     x: [element's x position],
10473     y: [element's y position],
10474     opacity: [element's opacity],
10475     easing: 'easeOut',
10476     duration: .35
10477 });
10478 </code></pre>
10479     * @param {Object} options  Object literal with any of the Fx config options
10480     * @return {Roo.Element} The Element
10481     */
10482     shift : function(o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485         el.queueFx(o, function(){
10486             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10487             if(w !== undefined){
10488                 a.width = {to: this.adjustWidth(w)};
10489             }
10490             if(h !== undefined){
10491                 a.height = {to: this.adjustHeight(h)};
10492             }
10493             if(x !== undefined || y !== undefined){
10494                 a.points = {to: [
10495                     x !== undefined ? x : this.getX(),
10496                     y !== undefined ? y : this.getY()
10497                 ]};
10498             }
10499             if(op !== undefined){
10500                 a.opacity = {to: op};
10501             }
10502             if(o.xy !== undefined){
10503                 a.points = {to: o.xy};
10504             }
10505             arguments.callee.anim = this.fxanim(a,
10506                 o, 'motion', .35, "easeOut", function(){
10507                 el.afterFx(o);
10508             });
10509         });
10510         return this;
10511     },
10512
10513         /**
10514          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10515          * ending point of the effect.
10516          * Usage:
10517          *<pre><code>
10518 // default: slide the element downward while fading out
10519 el.ghost();
10520
10521 // custom: slide the element out to the right with a 2-second duration
10522 el.ghost('r', { duration: 2 });
10523
10524 // common config options shown with default values
10525 el.ghost('b', {
10526     easing: 'easeOut',
10527     duration: .5
10528     remove: false,
10529     useDisplay: false
10530 });
10531 </code></pre>
10532          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10533          * @param {Object} options (optional) Object literal with any of the Fx config options
10534          * @return {Roo.Element} The Element
10535          */
10536     ghost : function(anchor, o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539
10540         el.queueFx(o, function(){
10541             anchor = anchor || "b";
10542
10543             // restore values after effect
10544             var r = this.getFxRestore();
10545             var w = this.getWidth(),
10546                 h = this.getHeight();
10547
10548             var st = this.dom.style;
10549
10550             var after = function(){
10551                 if(o.useDisplay){
10552                     el.setDisplayed(false);
10553                 }else{
10554                     el.hide();
10555                 }
10556
10557                 el.clearOpacity();
10558                 el.setPositioning(r.pos);
10559                 st.width = r.width;
10560                 st.height = r.height;
10561
10562                 el.afterFx(o);
10563             };
10564
10565             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10566             switch(anchor.toLowerCase()){
10567                 case "t":
10568                     pt.by = [0, -h];
10569                 break;
10570                 case "l":
10571                     pt.by = [-w, 0];
10572                 break;
10573                 case "r":
10574                     pt.by = [w, 0];
10575                 break;
10576                 case "b":
10577                     pt.by = [0, h];
10578                 break;
10579                 case "tl":
10580                     pt.by = [-w, -h];
10581                 break;
10582                 case "bl":
10583                     pt.by = [-w, h];
10584                 break;
10585                 case "br":
10586                     pt.by = [w, h];
10587                 break;
10588                 case "tr":
10589                     pt.by = [w, -h];
10590                 break;
10591             }
10592
10593             arguments.callee.anim = this.fxanim(a,
10594                 o,
10595                 'motion',
10596                 .5,
10597                 "easeOut", after);
10598         });
10599         return this;
10600     },
10601
10602         /**
10603          * Ensures that all effects queued after syncFx is called on the element are
10604          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10605          * @return {Roo.Element} The Element
10606          */
10607     syncFx : function(){
10608         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10609             block : false,
10610             concurrent : true,
10611             stopFx : false
10612         });
10613         return this;
10614     },
10615
10616         /**
10617          * Ensures that all effects queued after sequenceFx is called on the element are
10618          * run in sequence.  This is the opposite of {@link #syncFx}.
10619          * @return {Roo.Element} The Element
10620          */
10621     sequenceFx : function(){
10622         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10623             block : false,
10624             concurrent : false,
10625             stopFx : false
10626         });
10627         return this;
10628     },
10629
10630         /* @private */
10631     nextFx : function(){
10632         var ef = this.fxQueue[0];
10633         if(ef){
10634             ef.call(this);
10635         }
10636     },
10637
10638         /**
10639          * Returns true if the element has any effects actively running or queued, else returns false.
10640          * @return {Boolean} True if element has active effects, else false
10641          */
10642     hasActiveFx : function(){
10643         return this.fxQueue && this.fxQueue[0];
10644     },
10645
10646         /**
10647          * Stops any running effects and clears the element's internal effects queue if it contains
10648          * any additional effects that haven't started yet.
10649          * @return {Roo.Element} The Element
10650          */
10651     stopFx : function(){
10652         if(this.hasActiveFx()){
10653             var cur = this.fxQueue[0];
10654             if(cur && cur.anim && cur.anim.isAnimated()){
10655                 this.fxQueue = [cur]; // clear out others
10656                 cur.anim.stop(true);
10657             }
10658         }
10659         return this;
10660     },
10661
10662         /* @private */
10663     beforeFx : function(o){
10664         if(this.hasActiveFx() && !o.concurrent){
10665            if(o.stopFx){
10666                this.stopFx();
10667                return true;
10668            }
10669            return false;
10670         }
10671         return true;
10672     },
10673
10674         /**
10675          * Returns true if the element is currently blocking so that no other effect can be queued
10676          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10677          * used to ensure that an effect initiated by a user action runs to completion prior to the
10678          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10679          * @return {Boolean} True if blocking, else false
10680          */
10681     hasFxBlock : function(){
10682         var q = this.fxQueue;
10683         return q && q[0] && q[0].block;
10684     },
10685
10686         /* @private */
10687     queueFx : function(o, fn){
10688         if(!this.fxQueue){
10689             this.fxQueue = [];
10690         }
10691         if(!this.hasFxBlock()){
10692             Roo.applyIf(o, this.fxDefaults);
10693             if(!o.concurrent){
10694                 var run = this.beforeFx(o);
10695                 fn.block = o.block;
10696                 this.fxQueue.push(fn);
10697                 if(run){
10698                     this.nextFx();
10699                 }
10700             }else{
10701                 fn.call(this);
10702             }
10703         }
10704         return this;
10705     },
10706
10707         /* @private */
10708     fxWrap : function(pos, o, vis){
10709         var wrap;
10710         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10711             var wrapXY;
10712             if(o.fixPosition){
10713                 wrapXY = this.getXY();
10714             }
10715             var div = document.createElement("div");
10716             div.style.visibility = vis;
10717             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10718             wrap.setPositioning(pos);
10719             if(wrap.getStyle("position") == "static"){
10720                 wrap.position("relative");
10721             }
10722             this.clearPositioning('auto');
10723             wrap.clip();
10724             wrap.dom.appendChild(this.dom);
10725             if(wrapXY){
10726                 wrap.setXY(wrapXY);
10727             }
10728         }
10729         return wrap;
10730     },
10731
10732         /* @private */
10733     fxUnwrap : function(wrap, pos, o){
10734         this.clearPositioning();
10735         this.setPositioning(pos);
10736         if(!o.wrap){
10737             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10738             wrap.remove();
10739         }
10740     },
10741
10742         /* @private */
10743     getFxRestore : function(){
10744         var st = this.dom.style;
10745         return {pos: this.getPositioning(), width: st.width, height : st.height};
10746     },
10747
10748         /* @private */
10749     afterFx : function(o){
10750         if(o.afterStyle){
10751             this.applyStyles(o.afterStyle);
10752         }
10753         if(o.afterCls){
10754             this.addClass(o.afterCls);
10755         }
10756         if(o.remove === true){
10757             this.remove();
10758         }
10759         Roo.callback(o.callback, o.scope, [this]);
10760         if(!o.concurrent){
10761             this.fxQueue.shift();
10762             this.nextFx();
10763         }
10764     },
10765
10766         /* @private */
10767     getFxEl : function(){ // support for composite element fx
10768         return Roo.get(this.dom);
10769     },
10770
10771         /* @private */
10772     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10773         animType = animType || 'run';
10774         opt = opt || {};
10775         var anim = Roo.lib.Anim[animType](
10776             this.dom, args,
10777             (opt.duration || defaultDur) || .35,
10778             (opt.easing || defaultEase) || 'easeOut',
10779             function(){
10780                 Roo.callback(cb, this);
10781             },
10782             this
10783         );
10784         opt.anim = anim;
10785         return anim;
10786     }
10787 };
10788
10789 // backwords compat
10790 Roo.Fx.resize = Roo.Fx.scale;
10791
10792 //When included, Roo.Fx is automatically applied to Element so that all basic
10793 //effects are available directly via the Element API
10794 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10795  * Based on:
10796  * Ext JS Library 1.1.1
10797  * Copyright(c) 2006-2007, Ext JS, LLC.
10798  *
10799  * Originally Released Under LGPL - original licence link has changed is not relivant.
10800  *
10801  * Fork - LGPL
10802  * <script type="text/javascript">
10803  */
10804
10805
10806 /**
10807  * @class Roo.CompositeElement
10808  * Standard composite class. Creates a Roo.Element for every element in the collection.
10809  * <br><br>
10810  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10811  * actions will be performed on all the elements in this collection.</b>
10812  * <br><br>
10813  * All methods return <i>this</i> and can be chained.
10814  <pre><code>
10815  var els = Roo.select("#some-el div.some-class", true);
10816  // or select directly from an existing element
10817  var el = Roo.get('some-el');
10818  el.select('div.some-class', true);
10819
10820  els.setWidth(100); // all elements become 100 width
10821  els.hide(true); // all elements fade out and hide
10822  // or
10823  els.setWidth(100).hide(true);
10824  </code></pre>
10825  */
10826 Roo.CompositeElement = function(els){
10827     this.elements = [];
10828     this.addElements(els);
10829 };
10830 Roo.CompositeElement.prototype = {
10831     isComposite: true,
10832     addElements : function(els){
10833         if(!els) return this;
10834         if(typeof els == "string"){
10835             els = Roo.Element.selectorFunction(els);
10836         }
10837         var yels = this.elements;
10838         var index = yels.length-1;
10839         for(var i = 0, len = els.length; i < len; i++) {
10840                 yels[++index] = Roo.get(els[i]);
10841         }
10842         return this;
10843     },
10844
10845     /**
10846     * Clears this composite and adds the elements returned by the passed selector.
10847     * @param {String/Array} els A string CSS selector, an array of elements or an element
10848     * @return {CompositeElement} this
10849     */
10850     fill : function(els){
10851         this.elements = [];
10852         this.add(els);
10853         return this;
10854     },
10855
10856     /**
10857     * Filters this composite to only elements that match the passed selector.
10858     * @param {String} selector A string CSS selector
10859     * @return {CompositeElement} this
10860     */
10861     filter : function(selector){
10862         var els = [];
10863         this.each(function(el){
10864             if(el.is(selector)){
10865                 els[els.length] = el.dom;
10866             }
10867         });
10868         this.fill(els);
10869         return this;
10870     },
10871
10872     invoke : function(fn, args){
10873         var els = this.elements;
10874         for(var i = 0, len = els.length; i < len; i++) {
10875                 Roo.Element.prototype[fn].apply(els[i], args);
10876         }
10877         return this;
10878     },
10879     /**
10880     * Adds elements to this composite.
10881     * @param {String/Array} els A string CSS selector, an array of elements or an element
10882     * @return {CompositeElement} this
10883     */
10884     add : function(els){
10885         if(typeof els == "string"){
10886             this.addElements(Roo.Element.selectorFunction(els));
10887         }else if(els.length !== undefined){
10888             this.addElements(els);
10889         }else{
10890             this.addElements([els]);
10891         }
10892         return this;
10893     },
10894     /**
10895     * Calls the passed function passing (el, this, index) for each element in this composite.
10896     * @param {Function} fn The function to call
10897     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10898     * @return {CompositeElement} this
10899     */
10900     each : function(fn, scope){
10901         var els = this.elements;
10902         for(var i = 0, len = els.length; i < len; i++){
10903             if(fn.call(scope || els[i], els[i], this, i) === false) {
10904                 break;
10905             }
10906         }
10907         return this;
10908     },
10909
10910     /**
10911      * Returns the Element object at the specified index
10912      * @param {Number} index
10913      * @return {Roo.Element}
10914      */
10915     item : function(index){
10916         return this.elements[index] || null;
10917     },
10918
10919     /**
10920      * Returns the first Element
10921      * @return {Roo.Element}
10922      */
10923     first : function(){
10924         return this.item(0);
10925     },
10926
10927     /**
10928      * Returns the last Element
10929      * @return {Roo.Element}
10930      */
10931     last : function(){
10932         return this.item(this.elements.length-1);
10933     },
10934
10935     /**
10936      * Returns the number of elements in this composite
10937      * @return Number
10938      */
10939     getCount : function(){
10940         return this.elements.length;
10941     },
10942
10943     /**
10944      * Returns true if this composite contains the passed element
10945      * @return Boolean
10946      */
10947     contains : function(el){
10948         return this.indexOf(el) !== -1;
10949     },
10950
10951     /**
10952      * Returns true if this composite contains the passed element
10953      * @return Boolean
10954      */
10955     indexOf : function(el){
10956         return this.elements.indexOf(Roo.get(el));
10957     },
10958
10959
10960     /**
10961     * Removes the specified element(s).
10962     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10963     * or an array of any of those.
10964     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10965     * @return {CompositeElement} this
10966     */
10967     removeElement : function(el, removeDom){
10968         if(el instanceof Array){
10969             for(var i = 0, len = el.length; i < len; i++){
10970                 this.removeElement(el[i]);
10971             }
10972             return this;
10973         }
10974         var index = typeof el == 'number' ? el : this.indexOf(el);
10975         if(index !== -1){
10976             if(removeDom){
10977                 var d = this.elements[index];
10978                 if(d.dom){
10979                     d.remove();
10980                 }else{
10981                     d.parentNode.removeChild(d);
10982                 }
10983             }
10984             this.elements.splice(index, 1);
10985         }
10986         return this;
10987     },
10988
10989     /**
10990     * Replaces the specified element with the passed element.
10991     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10992     * to replace.
10993     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10994     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10995     * @return {CompositeElement} this
10996     */
10997     replaceElement : function(el, replacement, domReplace){
10998         var index = typeof el == 'number' ? el : this.indexOf(el);
10999         if(index !== -1){
11000             if(domReplace){
11001                 this.elements[index].replaceWith(replacement);
11002             }else{
11003                 this.elements.splice(index, 1, Roo.get(replacement))
11004             }
11005         }
11006         return this;
11007     },
11008
11009     /**
11010      * Removes all elements.
11011      */
11012     clear : function(){
11013         this.elements = [];
11014     }
11015 };
11016 (function(){
11017     Roo.CompositeElement.createCall = function(proto, fnName){
11018         if(!proto[fnName]){
11019             proto[fnName] = function(){
11020                 return this.invoke(fnName, arguments);
11021             };
11022         }
11023     };
11024     for(var fnName in Roo.Element.prototype){
11025         if(typeof Roo.Element.prototype[fnName] == "function"){
11026             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11027         }
11028     };
11029 })();
11030 /*
11031  * Based on:
11032  * Ext JS Library 1.1.1
11033  * Copyright(c) 2006-2007, Ext JS, LLC.
11034  *
11035  * Originally Released Under LGPL - original licence link has changed is not relivant.
11036  *
11037  * Fork - LGPL
11038  * <script type="text/javascript">
11039  */
11040
11041 /**
11042  * @class Roo.CompositeElementLite
11043  * @extends Roo.CompositeElement
11044  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11045  <pre><code>
11046  var els = Roo.select("#some-el div.some-class");
11047  // or select directly from an existing element
11048  var el = Roo.get('some-el');
11049  el.select('div.some-class');
11050
11051  els.setWidth(100); // all elements become 100 width
11052  els.hide(true); // all elements fade out and hide
11053  // or
11054  els.setWidth(100).hide(true);
11055  </code></pre><br><br>
11056  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11057  * actions will be performed on all the elements in this collection.</b>
11058  */
11059 Roo.CompositeElementLite = function(els){
11060     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11061     this.el = new Roo.Element.Flyweight();
11062 };
11063 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11064     addElements : function(els){
11065         if(els){
11066             if(els instanceof Array){
11067                 this.elements = this.elements.concat(els);
11068             }else{
11069                 var yels = this.elements;
11070                 var index = yels.length-1;
11071                 for(var i = 0, len = els.length; i < len; i++) {
11072                     yels[++index] = els[i];
11073                 }
11074             }
11075         }
11076         return this;
11077     },
11078     invoke : function(fn, args){
11079         var els = this.elements;
11080         var el = this.el;
11081         for(var i = 0, len = els.length; i < len; i++) {
11082             el.dom = els[i];
11083                 Roo.Element.prototype[fn].apply(el, args);
11084         }
11085         return this;
11086     },
11087     /**
11088      * Returns a flyweight Element of the dom element object at the specified index
11089      * @param {Number} index
11090      * @return {Roo.Element}
11091      */
11092     item : function(index){
11093         if(!this.elements[index]){
11094             return null;
11095         }
11096         this.el.dom = this.elements[index];
11097         return this.el;
11098     },
11099
11100     // fixes scope with flyweight
11101     addListener : function(eventName, handler, scope, opt){
11102         var els = this.elements;
11103         for(var i = 0, len = els.length; i < len; i++) {
11104             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11105         }
11106         return this;
11107     },
11108
11109     /**
11110     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11111     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11112     * a reference to the dom node, use el.dom.</b>
11113     * @param {Function} fn The function to call
11114     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11115     * @return {CompositeElement} this
11116     */
11117     each : function(fn, scope){
11118         var els = this.elements;
11119         var el = this.el;
11120         for(var i = 0, len = els.length; i < len; i++){
11121             el.dom = els[i];
11122                 if(fn.call(scope || el, el, this, i) === false){
11123                 break;
11124             }
11125         }
11126         return this;
11127     },
11128
11129     indexOf : function(el){
11130         return this.elements.indexOf(Roo.getDom(el));
11131     },
11132
11133     replaceElement : function(el, replacement, domReplace){
11134         var index = typeof el == 'number' ? el : this.indexOf(el);
11135         if(index !== -1){
11136             replacement = Roo.getDom(replacement);
11137             if(domReplace){
11138                 var d = this.elements[index];
11139                 d.parentNode.insertBefore(replacement, d);
11140                 d.parentNode.removeChild(d);
11141             }
11142             this.elements.splice(index, 1, replacement);
11143         }
11144         return this;
11145     }
11146 });
11147 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11148
11149 /*
11150  * Based on:
11151  * Ext JS Library 1.1.1
11152  * Copyright(c) 2006-2007, Ext JS, LLC.
11153  *
11154  * Originally Released Under LGPL - original licence link has changed is not relivant.
11155  *
11156  * Fork - LGPL
11157  * <script type="text/javascript">
11158  */
11159
11160  
11161
11162 /**
11163  * @class Roo.data.Connection
11164  * @extends Roo.util.Observable
11165  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11166  * either to a configured URL, or to a URL specified at request time.<br><br>
11167  * <p>
11168  * Requests made by this class are asynchronous, and will return immediately. No data from
11169  * the server will be available to the statement immediately following the {@link #request} call.
11170  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11171  * <p>
11172  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11173  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11174  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11175  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11176  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11177  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11178  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11179  * standard DOM methods.
11180  * @constructor
11181  * @param {Object} config a configuration object.
11182  */
11183 Roo.data.Connection = function(config){
11184     Roo.apply(this, config);
11185     this.addEvents({
11186         /**
11187          * @event beforerequest
11188          * Fires before a network request is made to retrieve a data object.
11189          * @param {Connection} conn This Connection object.
11190          * @param {Object} options The options config object passed to the {@link #request} method.
11191          */
11192         "beforerequest" : true,
11193         /**
11194          * @event requestcomplete
11195          * Fires if the request was successfully completed.
11196          * @param {Connection} conn This Connection object.
11197          * @param {Object} response The XHR object containing the response data.
11198          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11199          * @param {Object} options The options config object passed to the {@link #request} method.
11200          */
11201         "requestcomplete" : true,
11202         /**
11203          * @event requestexception
11204          * Fires if an error HTTP status was returned from the server.
11205          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11206          * @param {Connection} conn This Connection object.
11207          * @param {Object} response The XHR object containing the response data.
11208          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11209          * @param {Object} options The options config object passed to the {@link #request} method.
11210          */
11211         "requestexception" : true
11212     });
11213     Roo.data.Connection.superclass.constructor.call(this);
11214 };
11215
11216 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11217     /**
11218      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11219      */
11220     /**
11221      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11222      * extra parameters to each request made by this object. (defaults to undefined)
11223      */
11224     /**
11225      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11226      *  to each request made by this object. (defaults to undefined)
11227      */
11228     /**
11229      * @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)
11230      */
11231     /**
11232      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11233      */
11234     timeout : 30000,
11235     /**
11236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11237      * @type Boolean
11238      */
11239     autoAbort:false,
11240
11241     /**
11242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11243      * @type Boolean
11244      */
11245     disableCaching: true,
11246
11247     /**
11248      * Sends an HTTP request to a remote server.
11249      * @param {Object} options An object which may contain the following properties:<ul>
11250      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11251      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11252      * request, a url encoded string or a function to call to get either.</li>
11253      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11254      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11255      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11256      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11257      * <li>options {Object} The parameter to the request call.</li>
11258      * <li>success {Boolean} True if the request succeeded.</li>
11259      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11260      * </ul></li>
11261      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11262      * The callback is passed the following parameters:<ul>
11263      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11264      * <li>options {Object} The parameter to the request call.</li>
11265      * </ul></li>
11266      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11267      * The callback is passed the following parameters:<ul>
11268      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11269      * <li>options {Object} The parameter to the request call.</li>
11270      * </ul></li>
11271      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11272      * for the callback function. Defaults to the browser window.</li>
11273      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11274      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11275      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11276      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11277      * params for the post data. Any params will be appended to the URL.</li>
11278      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11279      * </ul>
11280      * @return {Number} transactionId
11281      */
11282     request : function(o){
11283         if(this.fireEvent("beforerequest", this, o) !== false){
11284             var p = o.params;
11285
11286             if(typeof p == "function"){
11287                 p = p.call(o.scope||window, o);
11288             }
11289             if(typeof p == "object"){
11290                 p = Roo.urlEncode(o.params);
11291             }
11292             if(this.extraParams){
11293                 var extras = Roo.urlEncode(this.extraParams);
11294                 p = p ? (p + '&' + extras) : extras;
11295             }
11296
11297             var url = o.url || this.url;
11298             if(typeof url == 'function'){
11299                 url = url.call(o.scope||window, o);
11300             }
11301
11302             if(o.form){
11303                 var form = Roo.getDom(o.form);
11304                 url = url || form.action;
11305
11306                 var enctype = form.getAttribute("enctype");
11307                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11308                     return this.doFormUpload(o, p, url);
11309                 }
11310                 var f = Roo.lib.Ajax.serializeForm(form);
11311                 p = p ? (p + '&' + f) : f;
11312             }
11313
11314             var hs = o.headers;
11315             if(this.defaultHeaders){
11316                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11317                 if(!o.headers){
11318                     o.headers = hs;
11319                 }
11320             }
11321
11322             var cb = {
11323                 success: this.handleResponse,
11324                 failure: this.handleFailure,
11325                 scope: this,
11326                 argument: {options: o},
11327                 timeout : this.timeout
11328             };
11329
11330             var method = o.method||this.method||(p ? "POST" : "GET");
11331
11332             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11333                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11334             }
11335
11336             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11337                 if(o.autoAbort){
11338                     this.abort();
11339                 }
11340             }else if(this.autoAbort !== false){
11341                 this.abort();
11342             }
11343
11344             if((method == 'GET' && p) || o.xmlData){
11345                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11346                 p = '';
11347             }
11348             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11349             return this.transId;
11350         }else{
11351             Roo.callback(o.callback, o.scope, [o, null, null]);
11352             return null;
11353         }
11354     },
11355
11356     /**
11357      * Determine whether this object has a request outstanding.
11358      * @param {Number} transactionId (Optional) defaults to the last transaction
11359      * @return {Boolean} True if there is an outstanding request.
11360      */
11361     isLoading : function(transId){
11362         if(transId){
11363             return Roo.lib.Ajax.isCallInProgress(transId);
11364         }else{
11365             return this.transId ? true : false;
11366         }
11367     },
11368
11369     /**
11370      * Aborts any outstanding request.
11371      * @param {Number} transactionId (Optional) defaults to the last transaction
11372      */
11373     abort : function(transId){
11374         if(transId || this.isLoading()){
11375             Roo.lib.Ajax.abort(transId || this.transId);
11376         }
11377     },
11378
11379     // private
11380     handleResponse : function(response){
11381         this.transId = false;
11382         var options = response.argument.options;
11383         response.argument = options ? options.argument : null;
11384         this.fireEvent("requestcomplete", this, response, options);
11385         Roo.callback(options.success, options.scope, [response, options]);
11386         Roo.callback(options.callback, options.scope, [options, true, response]);
11387     },
11388
11389     // private
11390     handleFailure : function(response, e){
11391         this.transId = false;
11392         var options = response.argument.options;
11393         response.argument = options ? options.argument : null;
11394         this.fireEvent("requestexception", this, response, options, e);
11395         Roo.callback(options.failure, options.scope, [response, options]);
11396         Roo.callback(options.callback, options.scope, [options, false, response]);
11397     },
11398
11399     // private
11400     doFormUpload : function(o, ps, url){
11401         var id = Roo.id();
11402         var frame = document.createElement('iframe');
11403         frame.id = id;
11404         frame.name = id;
11405         frame.className = 'x-hidden';
11406         if(Roo.isIE){
11407             frame.src = Roo.SSL_SECURE_URL;
11408         }
11409         document.body.appendChild(frame);
11410
11411         if(Roo.isIE){
11412            document.frames[id].name = id;
11413         }
11414
11415         var form = Roo.getDom(o.form);
11416         form.target = id;
11417         form.method = 'POST';
11418         form.enctype = form.encoding = 'multipart/form-data';
11419         if(url){
11420             form.action = url;
11421         }
11422
11423         var hiddens, hd;
11424         if(ps){ // add dynamic params
11425             hiddens = [];
11426             ps = Roo.urlDecode(ps, false);
11427             for(var k in ps){
11428                 if(ps.hasOwnProperty(k)){
11429                     hd = document.createElement('input');
11430                     hd.type = 'hidden';
11431                     hd.name = k;
11432                     hd.value = ps[k];
11433                     form.appendChild(hd);
11434                     hiddens.push(hd);
11435                 }
11436             }
11437         }
11438
11439         function cb(){
11440             var r = {  // bogus response object
11441                 responseText : '',
11442                 responseXML : null
11443             };
11444
11445             r.argument = o ? o.argument : null;
11446
11447             try { //
11448                 var doc;
11449                 if(Roo.isIE){
11450                     doc = frame.contentWindow.document;
11451                 }else {
11452                     doc = (frame.contentDocument || window.frames[id].document);
11453                 }
11454                 if(doc && doc.body){
11455                     r.responseText = doc.body.innerHTML;
11456                 }
11457                 if(doc && doc.XMLDocument){
11458                     r.responseXML = doc.XMLDocument;
11459                 }else {
11460                     r.responseXML = doc;
11461                 }
11462             }
11463             catch(e) {
11464                 // ignore
11465             }
11466
11467             Roo.EventManager.removeListener(frame, 'load', cb, this);
11468
11469             this.fireEvent("requestcomplete", this, r, o);
11470             Roo.callback(o.success, o.scope, [r, o]);
11471             Roo.callback(o.callback, o.scope, [o, true, r]);
11472
11473             setTimeout(function(){document.body.removeChild(frame);}, 100);
11474         }
11475
11476         Roo.EventManager.on(frame, 'load', cb, this);
11477         form.submit();
11478
11479         if(hiddens){ // remove dynamic params
11480             for(var i = 0, len = hiddens.length; i < len; i++){
11481                 form.removeChild(hiddens[i]);
11482             }
11483         }
11484     }
11485 });
11486
11487 /**
11488  * @class Roo.Ajax
11489  * @extends Roo.data.Connection
11490  * Global Ajax request class.
11491  *
11492  * @singleton
11493  */
11494 Roo.Ajax = new Roo.data.Connection({
11495     // fix up the docs
11496    /**
11497      * @cfg {String} url @hide
11498      */
11499     /**
11500      * @cfg {Object} extraParams @hide
11501      */
11502     /**
11503      * @cfg {Object} defaultHeaders @hide
11504      */
11505     /**
11506      * @cfg {String} method (Optional) @hide
11507      */
11508     /**
11509      * @cfg {Number} timeout (Optional) @hide
11510      */
11511     /**
11512      * @cfg {Boolean} autoAbort (Optional) @hide
11513      */
11514
11515     /**
11516      * @cfg {Boolean} disableCaching (Optional) @hide
11517      */
11518
11519     /**
11520      * @property  disableCaching
11521      * True to add a unique cache-buster param to GET requests. (defaults to true)
11522      * @type Boolean
11523      */
11524     /**
11525      * @property  url
11526      * The default URL to be used for requests to the server. (defaults to undefined)
11527      * @type String
11528      */
11529     /**
11530      * @property  extraParams
11531      * An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      * @type Object
11534      */
11535     /**
11536      * @property  defaultHeaders
11537      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11538      * @type Object
11539      */
11540     /**
11541      * @property  method
11542      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11543      * @type String
11544      */
11545     /**
11546      * @property  timeout
11547      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11548      * @type Number
11549      */
11550
11551     /**
11552      * @property  autoAbort
11553      * Whether a new request should abort any pending requests. (defaults to false)
11554      * @type Boolean
11555      */
11556     autoAbort : false,
11557
11558     /**
11559      * Serialize the passed form into a url encoded string
11560      * @param {String/HTMLElement} form
11561      * @return {String}
11562      */
11563     serializeForm : function(form){
11564         return Roo.lib.Ajax.serializeForm(form);
11565     }
11566 });/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576  
11577 /**
11578  * Global Ajax request class.
11579  * 
11580  * @class Roo.Ajax
11581  * @extends Roo.data.Connection
11582  * @static
11583  * 
11584  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11585  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11586  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11587  * @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)
11588  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11589  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11590  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11591  */
11592 Roo.Ajax = new Roo.data.Connection({
11593     // fix up the docs
11594     /**
11595      * @scope Roo.Ajax
11596      * @type {Boolear} 
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @scope Roo.Ajax
11603      * @param {String/HTMLElement} form
11604      * @return {String}
11605      */
11606     serializeForm : function(form){
11607         return Roo.lib.Ajax.serializeForm(form);
11608     }
11609 });/*
11610  * Based on:
11611  * Ext JS Library 1.1.1
11612  * Copyright(c) 2006-2007, Ext JS, LLC.
11613  *
11614  * Originally Released Under LGPL - original licence link has changed is not relivant.
11615  *
11616  * Fork - LGPL
11617  * <script type="text/javascript">
11618  */
11619
11620  
11621 /**
11622  * @class Roo.UpdateManager
11623  * @extends Roo.util.Observable
11624  * Provides AJAX-style update for Element object.<br><br>
11625  * Usage:<br>
11626  * <pre><code>
11627  * // Get it from a Roo.Element object
11628  * var el = Roo.get("foo");
11629  * var mgr = el.getUpdateManager();
11630  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11631  * ...
11632  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11633  * <br>
11634  * // or directly (returns the same UpdateManager instance)
11635  * var mgr = new Roo.UpdateManager("myElementId");
11636  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11637  * mgr.on("update", myFcnNeedsToKnow);
11638  * <br>
11639    // short handed call directly from the element object
11640    Roo.get("foo").load({
11641         url: "bar.php",
11642         scripts:true,
11643         params: "for=bar",
11644         text: "Loading Foo..."
11645    });
11646  * </code></pre>
11647  * @constructor
11648  * Create new UpdateManager directly.
11649  * @param {String/HTMLElement/Roo.Element} el The element to update
11650  * @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).
11651  */
11652 Roo.UpdateManager = function(el, forceNew){
11653     el = Roo.get(el);
11654     if(!forceNew && el.updateManager){
11655         return el.updateManager;
11656     }
11657     /**
11658      * The Element object
11659      * @type Roo.Element
11660      */
11661     this.el = el;
11662     /**
11663      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11664      * @type String
11665      */
11666     this.defaultUrl = null;
11667
11668     this.addEvents({
11669         /**
11670          * @event beforeupdate
11671          * Fired before an update is made, return false from your handler and the update is cancelled.
11672          * @param {Roo.Element} el
11673          * @param {String/Object/Function} url
11674          * @param {String/Object} params
11675          */
11676         "beforeupdate": true,
11677         /**
11678          * @event update
11679          * Fired after successful update is made.
11680          * @param {Roo.Element} el
11681          * @param {Object} oResponseObject The response Object
11682          */
11683         "update": true,
11684         /**
11685          * @event failure
11686          * Fired on update failure.
11687          * @param {Roo.Element} el
11688          * @param {Object} oResponseObject The response Object
11689          */
11690         "failure": true
11691     });
11692     var d = Roo.UpdateManager.defaults;
11693     /**
11694      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11695      * @type String
11696      */
11697     this.sslBlankUrl = d.sslBlankUrl;
11698     /**
11699      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11700      * @type Boolean
11701      */
11702     this.disableCaching = d.disableCaching;
11703     /**
11704      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11705      * @type String
11706      */
11707     this.indicatorText = d.indicatorText;
11708     /**
11709      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11710      * @type String
11711      */
11712     this.showLoadIndicator = d.showLoadIndicator;
11713     /**
11714      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11715      * @type Number
11716      */
11717     this.timeout = d.timeout;
11718
11719     /**
11720      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11721      * @type Boolean
11722      */
11723     this.loadScripts = d.loadScripts;
11724
11725     /**
11726      * Transaction object of current executing transaction
11727      */
11728     this.transaction = null;
11729
11730     /**
11731      * @private
11732      */
11733     this.autoRefreshProcId = null;
11734     /**
11735      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11736      * @type Function
11737      */
11738     this.refreshDelegate = this.refresh.createDelegate(this);
11739     /**
11740      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11741      * @type Function
11742      */
11743     this.updateDelegate = this.update.createDelegate(this);
11744     /**
11745      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11749     /**
11750      * @private
11751      */
11752     this.successDelegate = this.processSuccess.createDelegate(this);
11753     /**
11754      * @private
11755      */
11756     this.failureDelegate = this.processFailure.createDelegate(this);
11757
11758     if(!this.renderer){
11759      /**
11760       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11761       */
11762     this.renderer = new Roo.UpdateManager.BasicRenderer();
11763     }
11764     
11765     Roo.UpdateManager.superclass.constructor.call(this);
11766 };
11767
11768 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11769     /**
11770      * Get the Element this UpdateManager is bound to
11771      * @return {Roo.Element} The element
11772      */
11773     getEl : function(){
11774         return this.el;
11775     },
11776     /**
11777      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11778      * @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:
11779 <pre><code>
11780 um.update({<br/>
11781     url: "your-url.php",<br/>
11782     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11783     callback: yourFunction,<br/>
11784     scope: yourObject, //(optional scope)  <br/>
11785     discardUrl: false, <br/>
11786     nocache: false,<br/>
11787     text: "Loading...",<br/>
11788     timeout: 30,<br/>
11789     scripts: false<br/>
11790 });
11791 </code></pre>
11792      * The only required property is url. The optional properties nocache, text and scripts
11793      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11794      * @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}
11795      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11796      * @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.
11797      */
11798     update : function(url, params, callback, discardUrl){
11799         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11800             var method = this.method, cfg;
11801             if(typeof url == "object"){ // must be config object
11802                 cfg = url;
11803                 url = cfg.url;
11804                 params = params || cfg.params;
11805                 callback = callback || cfg.callback;
11806                 discardUrl = discardUrl || cfg.discardUrl;
11807                 if(callback && cfg.scope){
11808                     callback = callback.createDelegate(cfg.scope);
11809                 }
11810                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11811                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11812                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11813                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11814                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11815             }
11816             this.showLoading();
11817             if(!discardUrl){
11818                 this.defaultUrl = url;
11819             }
11820             if(typeof url == "function"){
11821                 url = url.call(this);
11822             }
11823
11824             method = method || (params ? "POST" : "GET");
11825             if(method == "GET"){
11826                 url = this.prepareUrl(url);
11827             }
11828
11829             var o = Roo.apply(cfg ||{}, {
11830                 url : url,
11831                 params: params,
11832                 success: this.successDelegate,
11833                 failure: this.failureDelegate,
11834                 callback: undefined,
11835                 timeout: (this.timeout*1000),
11836                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11837             });
11838
11839             this.transaction = Roo.Ajax.request(o);
11840         }
11841     },
11842
11843     /**
11844      * 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.
11845      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11846      * @param {String/HTMLElement} form The form Id or form element
11847      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11848      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11849      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11850      */
11851     formUpdate : function(form, url, reset, callback){
11852         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11853             if(typeof url == "function"){
11854                 url = url.call(this);
11855             }
11856             form = Roo.getDom(form);
11857             this.transaction = Roo.Ajax.request({
11858                 form: form,
11859                 url:url,
11860                 success: this.successDelegate,
11861                 failure: this.failureDelegate,
11862                 timeout: (this.timeout*1000),
11863                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11864             });
11865             this.showLoading.defer(1, this);
11866         }
11867     },
11868
11869     /**
11870      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11872      */
11873     refresh : function(callback){
11874         if(this.defaultUrl == null){
11875             return;
11876         }
11877         this.update(this.defaultUrl, null, callback, true);
11878     },
11879
11880     /**
11881      * Set this element to auto refresh.
11882      * @param {Number} interval How often to update (in seconds).
11883      * @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)
11884      * @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}
11885      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11886      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11887      */
11888     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11889         if(refreshNow){
11890             this.update(url || this.defaultUrl, params, callback, true);
11891         }
11892         if(this.autoRefreshProcId){
11893             clearInterval(this.autoRefreshProcId);
11894         }
11895         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11896     },
11897
11898     /**
11899      * Stop auto refresh on this element.
11900      */
11901      stopAutoRefresh : function(){
11902         if(this.autoRefreshProcId){
11903             clearInterval(this.autoRefreshProcId);
11904             delete this.autoRefreshProcId;
11905         }
11906     },
11907
11908     isAutoRefreshing : function(){
11909        return this.autoRefreshProcId ? true : false;
11910     },
11911     /**
11912      * Called to update the element to "Loading" state. Override to perform custom action.
11913      */
11914     showLoading : function(){
11915         if(this.showLoadIndicator){
11916             this.el.update(this.indicatorText);
11917         }
11918     },
11919
11920     /**
11921      * Adds unique parameter to query string if disableCaching = true
11922      * @private
11923      */
11924     prepareUrl : function(url){
11925         if(this.disableCaching){
11926             var append = "_dc=" + (new Date().getTime());
11927             if(url.indexOf("?") !== -1){
11928                 url += "&" + append;
11929             }else{
11930                 url += "?" + append;
11931             }
11932         }
11933         return url;
11934     },
11935
11936     /**
11937      * @private
11938      */
11939     processSuccess : function(response){
11940         this.transaction = null;
11941         if(response.argument.form && response.argument.reset){
11942             try{ // put in try/catch since some older FF releases had problems with this
11943                 response.argument.form.reset();
11944             }catch(e){}
11945         }
11946         if(this.loadScripts){
11947             this.renderer.render(this.el, response, this,
11948                 this.updateComplete.createDelegate(this, [response]));
11949         }else{
11950             this.renderer.render(this.el, response, this);
11951             this.updateComplete(response);
11952         }
11953     },
11954
11955     updateComplete : function(response){
11956         this.fireEvent("update", this.el, response);
11957         if(typeof response.argument.callback == "function"){
11958             response.argument.callback(this.el, true, response);
11959         }
11960     },
11961
11962     /**
11963      * @private
11964      */
11965     processFailure : function(response){
11966         this.transaction = null;
11967         this.fireEvent("failure", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, false, response);
11970         }
11971     },
11972
11973     /**
11974      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11975      * @param {Object} renderer The object implementing the render() method
11976      */
11977     setRenderer : function(renderer){
11978         this.renderer = renderer;
11979     },
11980
11981     getRenderer : function(){
11982        return this.renderer;
11983     },
11984
11985     /**
11986      * Set the defaultUrl used for updates
11987      * @param {String/Function} defaultUrl The url or a function to call to get the url
11988      */
11989     setDefaultUrl : function(defaultUrl){
11990         this.defaultUrl = defaultUrl;
11991     },
11992
11993     /**
11994      * Aborts the executing transaction
11995      */
11996     abort : function(){
11997         if(this.transaction){
11998             Roo.Ajax.abort(this.transaction);
11999         }
12000     },
12001
12002     /**
12003      * Returns true if an update is in progress
12004      * @return {Boolean}
12005      */
12006     isUpdating : function(){
12007         if(this.transaction){
12008             return Roo.Ajax.isLoading(this.transaction);
12009         }
12010         return false;
12011     }
12012 });
12013
12014 /**
12015  * @class Roo.UpdateManager.defaults
12016  * @static (not really - but it helps the doc tool)
12017  * The defaults collection enables customizing the default properties of UpdateManager
12018  */
12019    Roo.UpdateManager.defaults = {
12020        /**
12021          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12022          * @type Number
12023          */
12024          timeout : 30,
12025
12026          /**
12027          * True to process scripts by default (Defaults to false).
12028          * @type Boolean
12029          */
12030         loadScripts : false,
12031
12032         /**
12033         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12034         * @type String
12035         */
12036         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12037         /**
12038          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12039          * @type Boolean
12040          */
12041         disableCaching : false,
12042         /**
12043          * Whether to show indicatorText when loading (Defaults to true).
12044          * @type Boolean
12045          */
12046         showLoadIndicator : true,
12047         /**
12048          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12049          * @type String
12050          */
12051         indicatorText : '<div class="loading-indicator">Loading...</div>'
12052    };
12053
12054 /**
12055  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12056  *Usage:
12057  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12058  * @param {String/HTMLElement/Roo.Element} el The element to update
12059  * @param {String} url The url
12060  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12061  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12062  * @static
12063  * @deprecated
12064  * @member Roo.UpdateManager
12065  */
12066 Roo.UpdateManager.updateElement = function(el, url, params, options){
12067     var um = Roo.get(el, true).getUpdateManager();
12068     Roo.apply(um, options);
12069     um.update(url, params, options ? options.callback : null);
12070 };
12071 // alias for backwards compat
12072 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12073 /**
12074  * @class Roo.UpdateManager.BasicRenderer
12075  * Default Content renderer. Updates the elements innerHTML with the responseText.
12076  */
12077 Roo.UpdateManager.BasicRenderer = function(){};
12078
12079 Roo.UpdateManager.BasicRenderer.prototype = {
12080     /**
12081      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12082      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12083      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12084      * @param {Roo.Element} el The element being rendered
12085      * @param {Object} response The YUI Connect response object
12086      * @param {UpdateManager} updateManager The calling update manager
12087      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12088      */
12089      render : function(el, response, updateManager, callback){
12090         el.update(response.responseText, updateManager.loadScripts, callback);
12091     }
12092 };
12093 /*
12094  * Based on:
12095  * Ext JS Library 1.1.1
12096  * Copyright(c) 2006-2007, Ext JS, LLC.
12097  *
12098  * Originally Released Under LGPL - original licence link has changed is not relivant.
12099  *
12100  * Fork - LGPL
12101  * <script type="text/javascript">
12102  */
12103
12104 /**
12105  * @class Roo.util.DelayedTask
12106  * Provides a convenient method of performing setTimeout where a new
12107  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12108  * You can use this class to buffer
12109  * the keypress events for a certain number of milliseconds, and perform only if they stop
12110  * for that amount of time.
12111  * @constructor The parameters to this constructor serve as defaults and are not required.
12112  * @param {Function} fn (optional) The default function to timeout
12113  * @param {Object} scope (optional) The default scope of that timeout
12114  * @param {Array} args (optional) The default Array of arguments
12115  */
12116 Roo.util.DelayedTask = function(fn, scope, args){
12117     var id = null, d, t;
12118
12119     var call = function(){
12120         var now = new Date().getTime();
12121         if(now - t >= d){
12122             clearInterval(id);
12123             id = null;
12124             fn.apply(scope, args || []);
12125         }
12126     };
12127     /**
12128      * Cancels any pending timeout and queues a new one
12129      * @param {Number} delay The milliseconds to delay
12130      * @param {Function} newFn (optional) Overrides function passed to constructor
12131      * @param {Object} newScope (optional) Overrides scope passed to constructor
12132      * @param {Array} newArgs (optional) Overrides args passed to constructor
12133      */
12134     this.delay = function(delay, newFn, newScope, newArgs){
12135         if(id && delay != d){
12136             this.cancel();
12137         }
12138         d = delay;
12139         t = new Date().getTime();
12140         fn = newFn || fn;
12141         scope = newScope || scope;
12142         args = newArgs || args;
12143         if(!id){
12144             id = setInterval(call, d);
12145         }
12146     };
12147
12148     /**
12149      * Cancel the last queued timeout
12150      */
12151     this.cancel = function(){
12152         if(id){
12153             clearInterval(id);
12154             id = null;
12155         }
12156     };
12157 };/*
12158  * Based on:
12159  * Ext JS Library 1.1.1
12160  * Copyright(c) 2006-2007, Ext JS, LLC.
12161  *
12162  * Originally Released Under LGPL - original licence link has changed is not relivant.
12163  *
12164  * Fork - LGPL
12165  * <script type="text/javascript">
12166  */
12167  
12168  
12169 Roo.util.TaskRunner = function(interval){
12170     interval = interval || 10;
12171     var tasks = [], removeQueue = [];
12172     var id = 0;
12173     var running = false;
12174
12175     var stopThread = function(){
12176         running = false;
12177         clearInterval(id);
12178         id = 0;
12179     };
12180
12181     var startThread = function(){
12182         if(!running){
12183             running = true;
12184             id = setInterval(runTasks, interval);
12185         }
12186     };
12187
12188     var removeTask = function(task){
12189         removeQueue.push(task);
12190         if(task.onStop){
12191             task.onStop();
12192         }
12193     };
12194
12195     var runTasks = function(){
12196         if(removeQueue.length > 0){
12197             for(var i = 0, len = removeQueue.length; i < len; i++){
12198                 tasks.remove(removeQueue[i]);
12199             }
12200             removeQueue = [];
12201             if(tasks.length < 1){
12202                 stopThread();
12203                 return;
12204             }
12205         }
12206         var now = new Date().getTime();
12207         for(var i = 0, len = tasks.length; i < len; ++i){
12208             var t = tasks[i];
12209             var itime = now - t.taskRunTime;
12210             if(t.interval <= itime){
12211                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12212                 t.taskRunTime = now;
12213                 if(rt === false || t.taskRunCount === t.repeat){
12214                     removeTask(t);
12215                     return;
12216                 }
12217             }
12218             if(t.duration && t.duration <= (now - t.taskStartTime)){
12219                 removeTask(t);
12220             }
12221         }
12222     };
12223
12224     /**
12225      * Queues a new task.
12226      * @param {Object} task
12227      */
12228     this.start = function(task){
12229         tasks.push(task);
12230         task.taskStartTime = new Date().getTime();
12231         task.taskRunTime = 0;
12232         task.taskRunCount = 0;
12233         startThread();
12234         return task;
12235     };
12236
12237     this.stop = function(task){
12238         removeTask(task);
12239         return task;
12240     };
12241
12242     this.stopAll = function(){
12243         stopThread();
12244         for(var i = 0, len = tasks.length; i < len; i++){
12245             if(tasks[i].onStop){
12246                 tasks[i].onStop();
12247             }
12248         }
12249         tasks = [];
12250         removeQueue = [];
12251     };
12252 };
12253
12254 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12255  * Based on:
12256  * Ext JS Library 1.1.1
12257  * Copyright(c) 2006-2007, Ext JS, LLC.
12258  *
12259  * Originally Released Under LGPL - original licence link has changed is not relivant.
12260  *
12261  * Fork - LGPL
12262  * <script type="text/javascript">
12263  */
12264
12265  
12266 /**
12267  * @class Roo.util.MixedCollection
12268  * @extends Roo.util.Observable
12269  * A Collection class that maintains both numeric indexes and keys and exposes events.
12270  * @constructor
12271  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12272  * collection (defaults to false)
12273  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12274  * and return the key value for that item.  This is used when available to look up the key on items that
12275  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12276  * equivalent to providing an implementation for the {@link #getKey} method.
12277  */
12278 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12279     this.items = [];
12280     this.map = {};
12281     this.keys = [];
12282     this.length = 0;
12283     this.addEvents({
12284         /**
12285          * @event clear
12286          * Fires when the collection is cleared.
12287          */
12288         "clear" : true,
12289         /**
12290          * @event add
12291          * Fires when an item is added to the collection.
12292          * @param {Number} index The index at which the item was added.
12293          * @param {Object} o The item added.
12294          * @param {String} key The key associated with the added item.
12295          */
12296         "add" : true,
12297         /**
12298          * @event replace
12299          * Fires when an item is replaced in the collection.
12300          * @param {String} key he key associated with the new added.
12301          * @param {Object} old The item being replaced.
12302          * @param {Object} new The new item.
12303          */
12304         "replace" : true,
12305         /**
12306          * @event remove
12307          * Fires when an item is removed from the collection.
12308          * @param {Object} o The item being removed.
12309          * @param {String} key (optional) The key associated with the removed item.
12310          */
12311         "remove" : true,
12312         "sort" : true
12313     });
12314     this.allowFunctions = allowFunctions === true;
12315     if(keyFn){
12316         this.getKey = keyFn;
12317     }
12318     Roo.util.MixedCollection.superclass.constructor.call(this);
12319 };
12320
12321 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12322     allowFunctions : false,
12323     
12324 /**
12325  * Adds an item to the collection.
12326  * @param {String} key The key to associate with the item
12327  * @param {Object} o The item to add.
12328  * @return {Object} The item added.
12329  */
12330     add : function(key, o){
12331         if(arguments.length == 1){
12332             o = arguments[0];
12333             key = this.getKey(o);
12334         }
12335         if(typeof key == "undefined" || key === null){
12336             this.length++;
12337             this.items.push(o);
12338             this.keys.push(null);
12339         }else{
12340             var old = this.map[key];
12341             if(old){
12342                 return this.replace(key, o);
12343             }
12344             this.length++;
12345             this.items.push(o);
12346             this.map[key] = o;
12347             this.keys.push(key);
12348         }
12349         this.fireEvent("add", this.length-1, o, key);
12350         return o;
12351     },
12352        
12353 /**
12354   * MixedCollection has a generic way to fetch keys if you implement getKey.
12355 <pre><code>
12356 // normal way
12357 var mc = new Roo.util.MixedCollection();
12358 mc.add(someEl.dom.id, someEl);
12359 mc.add(otherEl.dom.id, otherEl);
12360 //and so on
12361
12362 // using getKey
12363 var mc = new Roo.util.MixedCollection();
12364 mc.getKey = function(el){
12365    return el.dom.id;
12366 };
12367 mc.add(someEl);
12368 mc.add(otherEl);
12369
12370 // or via the constructor
12371 var mc = new Roo.util.MixedCollection(false, function(el){
12372    return el.dom.id;
12373 });
12374 mc.add(someEl);
12375 mc.add(otherEl);
12376 </code></pre>
12377  * @param o {Object} The item for which to find the key.
12378  * @return {Object} The key for the passed item.
12379  */
12380     getKey : function(o){
12381          return o.id; 
12382     },
12383    
12384 /**
12385  * Replaces an item in the collection.
12386  * @param {String} key The key associated with the item to replace, or the item to replace.
12387  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12388  * @return {Object}  The new item.
12389  */
12390     replace : function(key, o){
12391         if(arguments.length == 1){
12392             o = arguments[0];
12393             key = this.getKey(o);
12394         }
12395         var old = this.item(key);
12396         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12397              return this.add(key, o);
12398         }
12399         var index = this.indexOfKey(key);
12400         this.items[index] = o;
12401         this.map[key] = o;
12402         this.fireEvent("replace", key, old, o);
12403         return o;
12404     },
12405    
12406 /**
12407  * Adds all elements of an Array or an Object to the collection.
12408  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12409  * an Array of values, each of which are added to the collection.
12410  */
12411     addAll : function(objs){
12412         if(arguments.length > 1 || objs instanceof Array){
12413             var args = arguments.length > 1 ? arguments : objs;
12414             for(var i = 0, len = args.length; i < len; i++){
12415                 this.add(args[i]);
12416             }
12417         }else{
12418             for(var key in objs){
12419                 if(this.allowFunctions || typeof objs[key] != "function"){
12420                     this.add(key, objs[key]);
12421                 }
12422             }
12423         }
12424     },
12425    
12426 /**
12427  * Executes the specified function once for every item in the collection, passing each
12428  * item as the first and only parameter. returning false from the function will stop the iteration.
12429  * @param {Function} fn The function to execute for each item.
12430  * @param {Object} scope (optional) The scope in which to execute the function.
12431  */
12432     each : function(fn, scope){
12433         var items = [].concat(this.items); // each safe for removal
12434         for(var i = 0, len = items.length; i < len; i++){
12435             if(fn.call(scope || items[i], items[i], i, len) === false){
12436                 break;
12437             }
12438         }
12439     },
12440    
12441 /**
12442  * Executes the specified function once for every key in the collection, passing each
12443  * key, and its associated item as the first two parameters.
12444  * @param {Function} fn The function to execute for each item.
12445  * @param {Object} scope (optional) The scope in which to execute the function.
12446  */
12447     eachKey : function(fn, scope){
12448         for(var i = 0, len = this.keys.length; i < len; i++){
12449             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12450         }
12451     },
12452    
12453 /**
12454  * Returns the first item in the collection which elicits a true return value from the
12455  * passed selection function.
12456  * @param {Function} fn The selection function to execute for each item.
12457  * @param {Object} scope (optional) The scope in which to execute the function.
12458  * @return {Object} The first item in the collection which returned true from the selection function.
12459  */
12460     find : function(fn, scope){
12461         for(var i = 0, len = this.items.length; i < len; i++){
12462             if(fn.call(scope || window, this.items[i], this.keys[i])){
12463                 return this.items[i];
12464             }
12465         }
12466         return null;
12467     },
12468    
12469 /**
12470  * Inserts an item at the specified index in the collection.
12471  * @param {Number} index The index to insert the item at.
12472  * @param {String} key The key to associate with the new item, or the item itself.
12473  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12474  * @return {Object} The item inserted.
12475  */
12476     insert : function(index, key, o){
12477         if(arguments.length == 2){
12478             o = arguments[1];
12479             key = this.getKey(o);
12480         }
12481         if(index >= this.length){
12482             return this.add(key, o);
12483         }
12484         this.length++;
12485         this.items.splice(index, 0, o);
12486         if(typeof key != "undefined" && key != null){
12487             this.map[key] = o;
12488         }
12489         this.keys.splice(index, 0, key);
12490         this.fireEvent("add", index, o, key);
12491         return o;
12492     },
12493    
12494 /**
12495  * Removed an item from the collection.
12496  * @param {Object} o The item to remove.
12497  * @return {Object} The item removed.
12498  */
12499     remove : function(o){
12500         return this.removeAt(this.indexOf(o));
12501     },
12502    
12503 /**
12504  * Remove an item from a specified index in the collection.
12505  * @param {Number} index The index within the collection of the item to remove.
12506  */
12507     removeAt : function(index){
12508         if(index < this.length && index >= 0){
12509             this.length--;
12510             var o = this.items[index];
12511             this.items.splice(index, 1);
12512             var key = this.keys[index];
12513             if(typeof key != "undefined"){
12514                 delete this.map[key];
12515             }
12516             this.keys.splice(index, 1);
12517             this.fireEvent("remove", o, key);
12518         }
12519     },
12520    
12521 /**
12522  * Removed an item associated with the passed key fom the collection.
12523  * @param {String} key The key of the item to remove.
12524  */
12525     removeKey : function(key){
12526         return this.removeAt(this.indexOfKey(key));
12527     },
12528    
12529 /**
12530  * Returns the number of items in the collection.
12531  * @return {Number} the number of items in the collection.
12532  */
12533     getCount : function(){
12534         return this.length; 
12535     },
12536    
12537 /**
12538  * Returns index within the collection of the passed Object.
12539  * @param {Object} o The item to find the index of.
12540  * @return {Number} index of the item.
12541  */
12542     indexOf : function(o){
12543         if(!this.items.indexOf){
12544             for(var i = 0, len = this.items.length; i < len; i++){
12545                 if(this.items[i] == o) return i;
12546             }
12547             return -1;
12548         }else{
12549             return this.items.indexOf(o);
12550         }
12551     },
12552    
12553 /**
12554  * Returns index within the collection of the passed key.
12555  * @param {String} key The key to find the index of.
12556  * @return {Number} index of the key.
12557  */
12558     indexOfKey : function(key){
12559         if(!this.keys.indexOf){
12560             for(var i = 0, len = this.keys.length; i < len; i++){
12561                 if(this.keys[i] == key) return i;
12562             }
12563             return -1;
12564         }else{
12565             return this.keys.indexOf(key);
12566         }
12567     },
12568    
12569 /**
12570  * Returns the item associated with the passed key OR index. Key has priority over index.
12571  * @param {String/Number} key The key or index of the item.
12572  * @return {Object} The item associated with the passed key.
12573  */
12574     item : function(key){
12575         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12576         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12577     },
12578     
12579 /**
12580  * Returns the item at the specified index.
12581  * @param {Number} index The index of the item.
12582  * @return {Object}
12583  */
12584     itemAt : function(index){
12585         return this.items[index];
12586     },
12587     
12588 /**
12589  * Returns the item associated with the passed key.
12590  * @param {String/Number} key The key of the item.
12591  * @return {Object} The item associated with the passed key.
12592  */
12593     key : function(key){
12594         return this.map[key];
12595     },
12596    
12597 /**
12598  * Returns true if the collection contains the passed Object as an item.
12599  * @param {Object} o  The Object to look for in the collection.
12600  * @return {Boolean} True if the collection contains the Object as an item.
12601  */
12602     contains : function(o){
12603         return this.indexOf(o) != -1;
12604     },
12605    
12606 /**
12607  * Returns true if the collection contains the passed Object as a key.
12608  * @param {String} key The key to look for in the collection.
12609  * @return {Boolean} True if the collection contains the Object as a key.
12610  */
12611     containsKey : function(key){
12612         return typeof this.map[key] != "undefined";
12613     },
12614    
12615 /**
12616  * Removes all items from the collection.
12617  */
12618     clear : function(){
12619         this.length = 0;
12620         this.items = [];
12621         this.keys = [];
12622         this.map = {};
12623         this.fireEvent("clear");
12624     },
12625    
12626 /**
12627  * Returns the first item in the collection.
12628  * @return {Object} the first item in the collection..
12629  */
12630     first : function(){
12631         return this.items[0]; 
12632     },
12633    
12634 /**
12635  * Returns the last item in the collection.
12636  * @return {Object} the last item in the collection..
12637  */
12638     last : function(){
12639         return this.items[this.length-1];   
12640     },
12641     
12642     _sort : function(property, dir, fn){
12643         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12644         fn = fn || function(a, b){
12645             return a-b;
12646         };
12647         var c = [], k = this.keys, items = this.items;
12648         for(var i = 0, len = items.length; i < len; i++){
12649             c[c.length] = {key: k[i], value: items[i], index: i};
12650         }
12651         c.sort(function(a, b){
12652             var v = fn(a[property], b[property]) * dsc;
12653             if(v == 0){
12654                 v = (a.index < b.index ? -1 : 1);
12655             }
12656             return v;
12657         });
12658         for(var i = 0, len = c.length; i < len; i++){
12659             items[i] = c[i].value;
12660             k[i] = c[i].key;
12661         }
12662         this.fireEvent("sort", this);
12663     },
12664     
12665     /**
12666      * Sorts this collection with the passed comparison function
12667      * @param {String} direction (optional) "ASC" or "DESC"
12668      * @param {Function} fn (optional) comparison function
12669      */
12670     sort : function(dir, fn){
12671         this._sort("value", dir, fn);
12672     },
12673     
12674     /**
12675      * Sorts this collection by keys
12676      * @param {String} direction (optional) "ASC" or "DESC"
12677      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12678      */
12679     keySort : function(dir, fn){
12680         this._sort("key", dir, fn || function(a, b){
12681             return String(a).toUpperCase()-String(b).toUpperCase();
12682         });
12683     },
12684     
12685     /**
12686      * Returns a range of items in this collection
12687      * @param {Number} startIndex (optional) defaults to 0
12688      * @param {Number} endIndex (optional) default to the last item
12689      * @return {Array} An array of items
12690      */
12691     getRange : function(start, end){
12692         var items = this.items;
12693         if(items.length < 1){
12694             return [];
12695         }
12696         start = start || 0;
12697         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12698         var r = [];
12699         if(start <= end){
12700             for(var i = start; i <= end; i++) {
12701                     r[r.length] = items[i];
12702             }
12703         }else{
12704             for(var i = start; i >= end; i--) {
12705                     r[r.length] = items[i];
12706             }
12707         }
12708         return r;
12709     },
12710         
12711     /**
12712      * Filter the <i>objects</i> in this collection by a specific property. 
12713      * Returns a new collection that has been filtered.
12714      * @param {String} property A property on your objects
12715      * @param {String/RegExp} value Either string that the property values 
12716      * should start with or a RegExp to test against the property
12717      * @return {MixedCollection} The new filtered collection
12718      */
12719     filter : function(property, value){
12720         if(!value.exec){ // not a regex
12721             value = String(value);
12722             if(value.length == 0){
12723                 return this.clone();
12724             }
12725             value = new RegExp("^" + Roo.escapeRe(value), "i");
12726         }
12727         return this.filterBy(function(o){
12728             return o && value.test(o[property]);
12729         });
12730         },
12731     
12732     /**
12733      * Filter by a function. * Returns a new collection that has been filtered.
12734      * The passed function will be called with each 
12735      * object in the collection. If the function returns true, the value is included 
12736      * otherwise it is filtered.
12737      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12738      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12739      * @return {MixedCollection} The new filtered collection
12740      */
12741     filterBy : function(fn, scope){
12742         var r = new Roo.util.MixedCollection();
12743         r.getKey = this.getKey;
12744         var k = this.keys, it = this.items;
12745         for(var i = 0, len = it.length; i < len; i++){
12746             if(fn.call(scope||this, it[i], k[i])){
12747                                 r.add(k[i], it[i]);
12748                         }
12749         }
12750         return r;
12751     },
12752     
12753     /**
12754      * Creates a duplicate of this collection
12755      * @return {MixedCollection}
12756      */
12757     clone : function(){
12758         var r = new Roo.util.MixedCollection();
12759         var k = this.keys, it = this.items;
12760         for(var i = 0, len = it.length; i < len; i++){
12761             r.add(k[i], it[i]);
12762         }
12763         r.getKey = this.getKey;
12764         return r;
12765     }
12766 });
12767 /**
12768  * Returns the item associated with the passed key or index.
12769  * @method
12770  * @param {String/Number} key The key or index of the item.
12771  * @return {Object} The item associated with the passed key.
12772  */
12773 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12774  * Based on:
12775  * Ext JS Library 1.1.1
12776  * Copyright(c) 2006-2007, Ext JS, LLC.
12777  *
12778  * Originally Released Under LGPL - original licence link has changed is not relivant.
12779  *
12780  * Fork - LGPL
12781  * <script type="text/javascript">
12782  */
12783 /**
12784  * @class Roo.util.JSON
12785  * Modified version of Douglas Crockford"s json.js that doesn"t
12786  * mess with the Object prototype 
12787  * http://www.json.org/js.html
12788  * @singleton
12789  */
12790 Roo.util.JSON = new (function(){
12791     var useHasOwn = {}.hasOwnProperty ? true : false;
12792     
12793     // crashes Safari in some instances
12794     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12795     
12796     var pad = function(n) {
12797         return n < 10 ? "0" + n : n;
12798     };
12799     
12800     var m = {
12801         "\b": '\\b',
12802         "\t": '\\t',
12803         "\n": '\\n',
12804         "\f": '\\f',
12805         "\r": '\\r',
12806         '"' : '\\"',
12807         "\\": '\\\\'
12808     };
12809
12810     var encodeString = function(s){
12811         if (/["\\\x00-\x1f]/.test(s)) {
12812             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12813                 var c = m[b];
12814                 if(c){
12815                     return c;
12816                 }
12817                 c = b.charCodeAt();
12818                 return "\\u00" +
12819                     Math.floor(c / 16).toString(16) +
12820                     (c % 16).toString(16);
12821             }) + '"';
12822         }
12823         return '"' + s + '"';
12824     };
12825     
12826     var encodeArray = function(o){
12827         var a = ["["], b, i, l = o.length, v;
12828             for (i = 0; i < l; i += 1) {
12829                 v = o[i];
12830                 switch (typeof v) {
12831                     case "undefined":
12832                     case "function":
12833                     case "unknown":
12834                         break;
12835                     default:
12836                         if (b) {
12837                             a.push(',');
12838                         }
12839                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12840                         b = true;
12841                 }
12842             }
12843             a.push("]");
12844             return a.join("");
12845     };
12846     
12847     var encodeDate = function(o){
12848         return '"' + o.getFullYear() + "-" +
12849                 pad(o.getMonth() + 1) + "-" +
12850                 pad(o.getDate()) + "T" +
12851                 pad(o.getHours()) + ":" +
12852                 pad(o.getMinutes()) + ":" +
12853                 pad(o.getSeconds()) + '"';
12854     };
12855     
12856     /**
12857      * Encodes an Object, Array or other value
12858      * @param {Mixed} o The variable to encode
12859      * @return {String} The JSON string
12860      */
12861     this.encode = function(o)
12862     {
12863         // should this be extended to fully wrap stringify..
12864         
12865         if(typeof o == "undefined" || o === null){
12866             return "null";
12867         }else if(o instanceof Array){
12868             return encodeArray(o);
12869         }else if(o instanceof Date){
12870             return encodeDate(o);
12871         }else if(typeof o == "string"){
12872             return encodeString(o);
12873         }else if(typeof o == "number"){
12874             return isFinite(o) ? String(o) : "null";
12875         }else if(typeof o == "boolean"){
12876             return String(o);
12877         }else {
12878             var a = ["{"], b, i, v;
12879             for (i in o) {
12880                 if(!useHasOwn || o.hasOwnProperty(i)) {
12881                     v = o[i];
12882                     switch (typeof v) {
12883                     case "undefined":
12884                     case "function":
12885                     case "unknown":
12886                         break;
12887                     default:
12888                         if(b){
12889                             a.push(',');
12890                         }
12891                         a.push(this.encode(i), ":",
12892                                 v === null ? "null" : this.encode(v));
12893                         b = true;
12894                     }
12895                 }
12896             }
12897             a.push("}");
12898             return a.join("");
12899         }
12900     };
12901     
12902     /**
12903      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12904      * @param {String} json The JSON string
12905      * @return {Object} The resulting object
12906      */
12907     this.decode = function(json){
12908         
12909         return  /** eval:var:json */ eval("(" + json + ')');
12910     };
12911 })();
12912 /** 
12913  * Shorthand for {@link Roo.util.JSON#encode}
12914  * @member Roo encode 
12915  * @method */
12916 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12917 /** 
12918  * Shorthand for {@link Roo.util.JSON#decode}
12919  * @member Roo decode 
12920  * @method */
12921 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12922 /*
12923  * Based on:
12924  * Ext JS Library 1.1.1
12925  * Copyright(c) 2006-2007, Ext JS, LLC.
12926  *
12927  * Originally Released Under LGPL - original licence link has changed is not relivant.
12928  *
12929  * Fork - LGPL
12930  * <script type="text/javascript">
12931  */
12932  
12933 /**
12934  * @class Roo.util.Format
12935  * Reusable data formatting functions
12936  * @singleton
12937  */
12938 Roo.util.Format = function(){
12939     var trimRe = /^\s+|\s+$/g;
12940     return {
12941         /**
12942          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12943          * @param {String} value The string to truncate
12944          * @param {Number} length The maximum length to allow before truncating
12945          * @return {String} The converted text
12946          */
12947         ellipsis : function(value, len){
12948             if(value && value.length > len){
12949                 return value.substr(0, len-3)+"...";
12950             }
12951             return value;
12952         },
12953
12954         /**
12955          * Checks a reference and converts it to empty string if it is undefined
12956          * @param {Mixed} value Reference to check
12957          * @return {Mixed} Empty string if converted, otherwise the original value
12958          */
12959         undef : function(value){
12960             return typeof value != "undefined" ? value : "";
12961         },
12962
12963         /**
12964          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12965          * @param {String} value The string to encode
12966          * @return {String} The encoded text
12967          */
12968         htmlEncode : function(value){
12969             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12970         },
12971
12972         /**
12973          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12974          * @param {String} value The string to decode
12975          * @return {String} The decoded text
12976          */
12977         htmlDecode : function(value){
12978             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12979         },
12980
12981         /**
12982          * Trims any whitespace from either side of a string
12983          * @param {String} value The text to trim
12984          * @return {String} The trimmed text
12985          */
12986         trim : function(value){
12987             return String(value).replace(trimRe, "");
12988         },
12989
12990         /**
12991          * Returns a substring from within an original string
12992          * @param {String} value The original text
12993          * @param {Number} start The start index of the substring
12994          * @param {Number} length The length of the substring
12995          * @return {String} The substring
12996          */
12997         substr : function(value, start, length){
12998             return String(value).substr(start, length);
12999         },
13000
13001         /**
13002          * Converts a string to all lower case letters
13003          * @param {String} value The text to convert
13004          * @return {String} The converted text
13005          */
13006         lowercase : function(value){
13007             return String(value).toLowerCase();
13008         },
13009
13010         /**
13011          * Converts a string to all upper case letters
13012          * @param {String} value The text to convert
13013          * @return {String} The converted text
13014          */
13015         uppercase : function(value){
13016             return String(value).toUpperCase();
13017         },
13018
13019         /**
13020          * Converts the first character only of a string to upper case
13021          * @param {String} value The text to convert
13022          * @return {String} The converted text
13023          */
13024         capitalize : function(value){
13025             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13026         },
13027
13028         // private
13029         call : function(value, fn){
13030             if(arguments.length > 2){
13031                 var args = Array.prototype.slice.call(arguments, 2);
13032                 args.unshift(value);
13033                  
13034                 return /** eval:var:value */  eval(fn).apply(window, args);
13035             }else{
13036                 /** eval:var:value */
13037                 return /** eval:var:value */ eval(fn).call(window, value);
13038             }
13039         },
13040
13041        
13042         /**
13043          * safer version of Math.toFixed..??/
13044          * @param {Number/String} value The numeric value to format
13045          * @param {Number/String} value Decimal places 
13046          * @return {String} The formatted currency string
13047          */
13048         toFixed : function(v, n)
13049         {
13050             // why not use to fixed - precision is buggered???
13051             if (!n) {
13052                 return Math.round(v-0);
13053             }
13054             var fact = Math.pow(10,n+1);
13055             v = (Math.round((v-0)*fact))/fact;
13056             var z = (''+fact).substring(2);
13057             if (v == Math.floor(v)) {
13058                 return Math.floor(v) + '.' + z;
13059             }
13060             
13061             // now just padd decimals..
13062             var ps = String(v).split('.');
13063             var fd = (ps[1] + z);
13064             var r = fd.substring(0,n); 
13065             var rm = fd.substring(n); 
13066             if (rm < 5) {
13067                 return ps[0] + '.' + r;
13068             }
13069             r*=1; // turn it into a number;
13070             r++;
13071             if (String(r).length != n) {
13072                 ps[0]*=1;
13073                 ps[0]++;
13074                 r = String(r).substring(1); // chop the end off.
13075             }
13076             
13077             return ps[0] + '.' + r;
13078              
13079         },
13080         
13081         /**
13082          * Format a number as US currency
13083          * @param {Number/String} value The numeric value to format
13084          * @return {String} The formatted currency string
13085          */
13086         usMoney : function(v){
13087             v = (Math.round((v-0)*100))/100;
13088             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13089             v = String(v);
13090             var ps = v.split('.');
13091             var whole = ps[0];
13092             var sub = ps[1] ? '.'+ ps[1] : '.00';
13093             var r = /(\d+)(\d{3})/;
13094             while (r.test(whole)) {
13095                 whole = whole.replace(r, '$1' + ',' + '$2');
13096             }
13097             return "$" + whole + sub ;
13098         },
13099         
13100         /**
13101          * Parse a value into a formatted date using the specified format pattern.
13102          * @param {Mixed} value The value to format
13103          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13104          * @return {String} The formatted date string
13105          */
13106         date : function(v, format){
13107             if(!v){
13108                 return "";
13109             }
13110             if(!(v instanceof Date)){
13111                 v = new Date(Date.parse(v));
13112             }
13113             return v.dateFormat(format || "m/d/Y");
13114         },
13115
13116         /**
13117          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13118          * @param {String} format Any valid date format string
13119          * @return {Function} The date formatting function
13120          */
13121         dateRenderer : function(format){
13122             return function(v){
13123                 return Roo.util.Format.date(v, format);  
13124             };
13125         },
13126
13127         // private
13128         stripTagsRE : /<\/?[^>]+>/gi,
13129         
13130         /**
13131          * Strips all HTML tags
13132          * @param {Mixed} value The text from which to strip tags
13133          * @return {String} The stripped text
13134          */
13135         stripTags : function(v){
13136             return !v ? v : String(v).replace(this.stripTagsRE, "");
13137         }
13138     };
13139 }();/*
13140  * Based on:
13141  * Ext JS Library 1.1.1
13142  * Copyright(c) 2006-2007, Ext JS, LLC.
13143  *
13144  * Originally Released Under LGPL - original licence link has changed is not relivant.
13145  *
13146  * Fork - LGPL
13147  * <script type="text/javascript">
13148  */
13149
13150
13151  
13152
13153 /**
13154  * @class Roo.MasterTemplate
13155  * @extends Roo.Template
13156  * Provides a template that can have child templates. The syntax is:
13157 <pre><code>
13158 var t = new Roo.MasterTemplate(
13159         '&lt;select name="{name}"&gt;',
13160                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13161         '&lt;/select&gt;'
13162 );
13163 t.add('options', {value: 'foo', text: 'bar'});
13164 // or you can add multiple child elements in one shot
13165 t.addAll('options', [
13166     {value: 'foo', text: 'bar'},
13167     {value: 'foo2', text: 'bar2'},
13168     {value: 'foo3', text: 'bar3'}
13169 ]);
13170 // then append, applying the master template values
13171 t.append('my-form', {name: 'my-select'});
13172 </code></pre>
13173 * A name attribute for the child template is not required if you have only one child
13174 * template or you want to refer to them by index.
13175  */
13176 Roo.MasterTemplate = function(){
13177     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13178     this.originalHtml = this.html;
13179     var st = {};
13180     var m, re = this.subTemplateRe;
13181     re.lastIndex = 0;
13182     var subIndex = 0;
13183     while(m = re.exec(this.html)){
13184         var name = m[1], content = m[2];
13185         st[subIndex] = {
13186             name: name,
13187             index: subIndex,
13188             buffer: [],
13189             tpl : new Roo.Template(content)
13190         };
13191         if(name){
13192             st[name] = st[subIndex];
13193         }
13194         st[subIndex].tpl.compile();
13195         st[subIndex].tpl.call = this.call.createDelegate(this);
13196         subIndex++;
13197     }
13198     this.subCount = subIndex;
13199     this.subs = st;
13200 };
13201 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13202     /**
13203     * The regular expression used to match sub templates
13204     * @type RegExp
13205     * @property
13206     */
13207     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13208
13209     /**
13210      * Applies the passed values to a child template.
13211      * @param {String/Number} name (optional) The name or index of the child template
13212      * @param {Array/Object} values The values to be applied to the template
13213      * @return {MasterTemplate} this
13214      */
13215      add : function(name, values){
13216         if(arguments.length == 1){
13217             values = arguments[0];
13218             name = 0;
13219         }
13220         var s = this.subs[name];
13221         s.buffer[s.buffer.length] = s.tpl.apply(values);
13222         return this;
13223     },
13224
13225     /**
13226      * Applies all the passed values to a child template.
13227      * @param {String/Number} name (optional) The name or index of the child template
13228      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13229      * @param {Boolean} reset (optional) True to reset the template first
13230      * @return {MasterTemplate} this
13231      */
13232     fill : function(name, values, reset){
13233         var a = arguments;
13234         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13235             values = a[0];
13236             name = 0;
13237             reset = a[1];
13238         }
13239         if(reset){
13240             this.reset();
13241         }
13242         for(var i = 0, len = values.length; i < len; i++){
13243             this.add(name, values[i]);
13244         }
13245         return this;
13246     },
13247
13248     /**
13249      * Resets the template for reuse
13250      * @return {MasterTemplate} this
13251      */
13252      reset : function(){
13253         var s = this.subs;
13254         for(var i = 0; i < this.subCount; i++){
13255             s[i].buffer = [];
13256         }
13257         return this;
13258     },
13259
13260     applyTemplate : function(values){
13261         var s = this.subs;
13262         var replaceIndex = -1;
13263         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13264             return s[++replaceIndex].buffer.join("");
13265         });
13266         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13267     },
13268
13269     apply : function(){
13270         return this.applyTemplate.apply(this, arguments);
13271     },
13272
13273     compile : function(){return this;}
13274 });
13275
13276 /**
13277  * Alias for fill().
13278  * @method
13279  */
13280 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13281  /**
13282  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13283  * var tpl = Roo.MasterTemplate.from('element-id');
13284  * @param {String/HTMLElement} el
13285  * @param {Object} config
13286  * @static
13287  */
13288 Roo.MasterTemplate.from = function(el, config){
13289     el = Roo.getDom(el);
13290     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13291 };/*
13292  * Based on:
13293  * Ext JS Library 1.1.1
13294  * Copyright(c) 2006-2007, Ext JS, LLC.
13295  *
13296  * Originally Released Under LGPL - original licence link has changed is not relivant.
13297  *
13298  * Fork - LGPL
13299  * <script type="text/javascript">
13300  */
13301
13302  
13303 /**
13304  * @class Roo.util.CSS
13305  * Utility class for manipulating CSS rules
13306  * @singleton
13307  */
13308 Roo.util.CSS = function(){
13309         var rules = null;
13310         var doc = document;
13311
13312     var camelRe = /(-[a-z])/gi;
13313     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13314
13315    return {
13316    /**
13317     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13318     * tag and appended to the HEAD of the document.
13319     * @param {String|Object} cssText The text containing the css rules
13320     * @param {String} id An id to add to the stylesheet for later removal
13321     * @return {StyleSheet}
13322     */
13323     createStyleSheet : function(cssText, id){
13324         var ss;
13325         var head = doc.getElementsByTagName("head")[0];
13326         var nrules = doc.createElement("style");
13327         nrules.setAttribute("type", "text/css");
13328         if(id){
13329             nrules.setAttribute("id", id);
13330         }
13331         if (typeof(cssText) != 'string') {
13332             // support object maps..
13333             // not sure if this a good idea.. 
13334             // perhaps it should be merged with the general css handling
13335             // and handle js style props.
13336             var cssTextNew = [];
13337             for(var n in cssText) {
13338                 var citems = [];
13339                 for(var k in cssText[n]) {
13340                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13341                 }
13342                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13343                 
13344             }
13345             cssText = cssTextNew.join("\n");
13346             
13347         }
13348        
13349        
13350        if(Roo.isIE){
13351            head.appendChild(nrules);
13352            ss = nrules.styleSheet;
13353            ss.cssText = cssText;
13354        }else{
13355            try{
13356                 nrules.appendChild(doc.createTextNode(cssText));
13357            }catch(e){
13358                nrules.cssText = cssText; 
13359            }
13360            head.appendChild(nrules);
13361            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13362        }
13363        this.cacheStyleSheet(ss);
13364        return ss;
13365    },
13366
13367    /**
13368     * Removes a style or link tag by id
13369     * @param {String} id The id of the tag
13370     */
13371    removeStyleSheet : function(id){
13372        var existing = doc.getElementById(id);
13373        if(existing){
13374            existing.parentNode.removeChild(existing);
13375        }
13376    },
13377
13378    /**
13379     * Dynamically swaps an existing stylesheet reference for a new one
13380     * @param {String} id The id of an existing link tag to remove
13381     * @param {String} url The href of the new stylesheet to include
13382     */
13383    swapStyleSheet : function(id, url){
13384        this.removeStyleSheet(id);
13385        var ss = doc.createElement("link");
13386        ss.setAttribute("rel", "stylesheet");
13387        ss.setAttribute("type", "text/css");
13388        ss.setAttribute("id", id);
13389        ss.setAttribute("href", url);
13390        doc.getElementsByTagName("head")[0].appendChild(ss);
13391    },
13392    
13393    /**
13394     * Refresh the rule cache if you have dynamically added stylesheets
13395     * @return {Object} An object (hash) of rules indexed by selector
13396     */
13397    refreshCache : function(){
13398        return this.getRules(true);
13399    },
13400
13401    // private
13402    cacheStyleSheet : function(stylesheet){
13403        if(!rules){
13404            rules = {};
13405        }
13406        try{// try catch for cross domain access issue
13407            var ssRules = stylesheet.cssRules || stylesheet.rules;
13408            for(var j = ssRules.length-1; j >= 0; --j){
13409                rules[ssRules[j].selectorText] = ssRules[j];
13410            }
13411        }catch(e){}
13412    },
13413    
13414    /**
13415     * Gets all css rules for the document
13416     * @param {Boolean} refreshCache true to refresh the internal cache
13417     * @return {Object} An object (hash) of rules indexed by selector
13418     */
13419    getRules : function(refreshCache){
13420                 if(rules == null || refreshCache){
13421                         rules = {};
13422                         var ds = doc.styleSheets;
13423                         for(var i =0, len = ds.length; i < len; i++){
13424                             try{
13425                         this.cacheStyleSheet(ds[i]);
13426                     }catch(e){} 
13427                 }
13428                 }
13429                 return rules;
13430         },
13431         
13432         /**
13433     * Gets an an individual CSS rule by selector(s)
13434     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13435     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13436     * @return {CSSRule} The CSS rule or null if one is not found
13437     */
13438    getRule : function(selector, refreshCache){
13439                 var rs = this.getRules(refreshCache);
13440                 if(!(selector instanceof Array)){
13441                     return rs[selector];
13442                 }
13443                 for(var i = 0; i < selector.length; i++){
13444                         if(rs[selector[i]]){
13445                                 return rs[selector[i]];
13446                         }
13447                 }
13448                 return null;
13449         },
13450         
13451         
13452         /**
13453     * Updates a rule property
13454     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13455     * @param {String} property The css property
13456     * @param {String} value The new value for the property
13457     * @return {Boolean} true If a rule was found and updated
13458     */
13459    updateRule : function(selector, property, value){
13460                 if(!(selector instanceof Array)){
13461                         var rule = this.getRule(selector);
13462                         if(rule){
13463                                 rule.style[property.replace(camelRe, camelFn)] = value;
13464                                 return true;
13465                         }
13466                 }else{
13467                         for(var i = 0; i < selector.length; i++){
13468                                 if(this.updateRule(selector[i], property, value)){
13469                                         return true;
13470                                 }
13471                         }
13472                 }
13473                 return false;
13474         }
13475    };   
13476 }();/*
13477  * Based on:
13478  * Ext JS Library 1.1.1
13479  * Copyright(c) 2006-2007, Ext JS, LLC.
13480  *
13481  * Originally Released Under LGPL - original licence link has changed is not relivant.
13482  *
13483  * Fork - LGPL
13484  * <script type="text/javascript">
13485  */
13486
13487  
13488
13489 /**
13490  * @class Roo.util.ClickRepeater
13491  * @extends Roo.util.Observable
13492  * 
13493  * A wrapper class which can be applied to any element. Fires a "click" event while the
13494  * mouse is pressed. The interval between firings may be specified in the config but
13495  * defaults to 10 milliseconds.
13496  * 
13497  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13498  * 
13499  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13500  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13501  * Similar to an autorepeat key delay.
13502  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13503  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13504  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13505  *           "interval" and "delay" are ignored. "immediate" is honored.
13506  * @cfg {Boolean} preventDefault True to prevent the default click event
13507  * @cfg {Boolean} stopDefault True to stop the default click event
13508  * 
13509  * @history
13510  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13511  *     2007-02-02 jvs Renamed to ClickRepeater
13512  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13513  *
13514  *  @constructor
13515  * @param {String/HTMLElement/Element} el The element to listen on
13516  * @param {Object} config
13517  **/
13518 Roo.util.ClickRepeater = function(el, config)
13519 {
13520     this.el = Roo.get(el);
13521     this.el.unselectable();
13522
13523     Roo.apply(this, config);
13524
13525     this.addEvents({
13526     /**
13527      * @event mousedown
13528      * Fires when the mouse button is depressed.
13529      * @param {Roo.util.ClickRepeater} this
13530      */
13531         "mousedown" : true,
13532     /**
13533      * @event click
13534      * Fires on a specified interval during the time the element is pressed.
13535      * @param {Roo.util.ClickRepeater} this
13536      */
13537         "click" : true,
13538     /**
13539      * @event mouseup
13540      * Fires when the mouse key is released.
13541      * @param {Roo.util.ClickRepeater} this
13542      */
13543         "mouseup" : true
13544     });
13545
13546     this.el.on("mousedown", this.handleMouseDown, this);
13547     if(this.preventDefault || this.stopDefault){
13548         this.el.on("click", function(e){
13549             if(this.preventDefault){
13550                 e.preventDefault();
13551             }
13552             if(this.stopDefault){
13553                 e.stopEvent();
13554             }
13555         }, this);
13556     }
13557
13558     // allow inline handler
13559     if(this.handler){
13560         this.on("click", this.handler,  this.scope || this);
13561     }
13562
13563     Roo.util.ClickRepeater.superclass.constructor.call(this);
13564 };
13565
13566 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13567     interval : 20,
13568     delay: 250,
13569     preventDefault : true,
13570     stopDefault : false,
13571     timer : 0,
13572
13573     // private
13574     handleMouseDown : function(){
13575         clearTimeout(this.timer);
13576         this.el.blur();
13577         if(this.pressClass){
13578             this.el.addClass(this.pressClass);
13579         }
13580         this.mousedownTime = new Date();
13581
13582         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13583         this.el.on("mouseout", this.handleMouseOut, this);
13584
13585         this.fireEvent("mousedown", this);
13586         this.fireEvent("click", this);
13587         
13588         this.timer = this.click.defer(this.delay || this.interval, this);
13589     },
13590
13591     // private
13592     click : function(){
13593         this.fireEvent("click", this);
13594         this.timer = this.click.defer(this.getInterval(), this);
13595     },
13596
13597     // private
13598     getInterval: function(){
13599         if(!this.accelerate){
13600             return this.interval;
13601         }
13602         var pressTime = this.mousedownTime.getElapsed();
13603         if(pressTime < 500){
13604             return 400;
13605         }else if(pressTime < 1700){
13606             return 320;
13607         }else if(pressTime < 2600){
13608             return 250;
13609         }else if(pressTime < 3500){
13610             return 180;
13611         }else if(pressTime < 4400){
13612             return 140;
13613         }else if(pressTime < 5300){
13614             return 80;
13615         }else if(pressTime < 6200){
13616             return 50;
13617         }else{
13618             return 10;
13619         }
13620     },
13621
13622     // private
13623     handleMouseOut : function(){
13624         clearTimeout(this.timer);
13625         if(this.pressClass){
13626             this.el.removeClass(this.pressClass);
13627         }
13628         this.el.on("mouseover", this.handleMouseReturn, this);
13629     },
13630
13631     // private
13632     handleMouseReturn : function(){
13633         this.el.un("mouseover", this.handleMouseReturn);
13634         if(this.pressClass){
13635             this.el.addClass(this.pressClass);
13636         }
13637         this.click();
13638     },
13639
13640     // private
13641     handleMouseUp : function(){
13642         clearTimeout(this.timer);
13643         this.el.un("mouseover", this.handleMouseReturn);
13644         this.el.un("mouseout", this.handleMouseOut);
13645         Roo.get(document).un("mouseup", this.handleMouseUp);
13646         this.el.removeClass(this.pressClass);
13647         this.fireEvent("mouseup", this);
13648     }
13649 });/*
13650  * Based on:
13651  * Ext JS Library 1.1.1
13652  * Copyright(c) 2006-2007, Ext JS, LLC.
13653  *
13654  * Originally Released Under LGPL - original licence link has changed is not relivant.
13655  *
13656  * Fork - LGPL
13657  * <script type="text/javascript">
13658  */
13659
13660  
13661 /**
13662  * @class Roo.KeyNav
13663  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13664  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13665  * way to implement custom navigation schemes for any UI component.</p>
13666  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13667  * pageUp, pageDown, del, home, end.  Usage:</p>
13668  <pre><code>
13669 var nav = new Roo.KeyNav("my-element", {
13670     "left" : function(e){
13671         this.moveLeft(e.ctrlKey);
13672     },
13673     "right" : function(e){
13674         this.moveRight(e.ctrlKey);
13675     },
13676     "enter" : function(e){
13677         this.save();
13678     },
13679     scope : this
13680 });
13681 </code></pre>
13682  * @constructor
13683  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13684  * @param {Object} config The config
13685  */
13686 Roo.KeyNav = function(el, config){
13687     this.el = Roo.get(el);
13688     Roo.apply(this, config);
13689     if(!this.disabled){
13690         this.disabled = true;
13691         this.enable();
13692     }
13693 };
13694
13695 Roo.KeyNav.prototype = {
13696     /**
13697      * @cfg {Boolean} disabled
13698      * True to disable this KeyNav instance (defaults to false)
13699      */
13700     disabled : false,
13701     /**
13702      * @cfg {String} defaultEventAction
13703      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13704      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13705      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13706      */
13707     defaultEventAction: "stopEvent",
13708     /**
13709      * @cfg {Boolean} forceKeyDown
13710      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13711      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13712      * handle keydown instead of keypress.
13713      */
13714     forceKeyDown : false,
13715
13716     // private
13717     prepareEvent : function(e){
13718         var k = e.getKey();
13719         var h = this.keyToHandler[k];
13720         //if(h && this[h]){
13721         //    e.stopPropagation();
13722         //}
13723         if(Roo.isSafari && h && k >= 37 && k <= 40){
13724             e.stopEvent();
13725         }
13726     },
13727
13728     // private
13729     relay : function(e){
13730         var k = e.getKey();
13731         var h = this.keyToHandler[k];
13732         if(h && this[h]){
13733             if(this.doRelay(e, this[h], h) !== true){
13734                 e[this.defaultEventAction]();
13735             }
13736         }
13737     },
13738
13739     // private
13740     doRelay : function(e, h, hname){
13741         return h.call(this.scope || this, e);
13742     },
13743
13744     // possible handlers
13745     enter : false,
13746     left : false,
13747     right : false,
13748     up : false,
13749     down : false,
13750     tab : false,
13751     esc : false,
13752     pageUp : false,
13753     pageDown : false,
13754     del : false,
13755     home : false,
13756     end : false,
13757
13758     // quick lookup hash
13759     keyToHandler : {
13760         37 : "left",
13761         39 : "right",
13762         38 : "up",
13763         40 : "down",
13764         33 : "pageUp",
13765         34 : "pageDown",
13766         46 : "del",
13767         36 : "home",
13768         35 : "end",
13769         13 : "enter",
13770         27 : "esc",
13771         9  : "tab"
13772     },
13773
13774         /**
13775          * Enable this KeyNav
13776          */
13777         enable: function(){
13778                 if(this.disabled){
13779             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13780             // the EventObject will normalize Safari automatically
13781             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13782                 this.el.on("keydown", this.relay,  this);
13783             }else{
13784                 this.el.on("keydown", this.prepareEvent,  this);
13785                 this.el.on("keypress", this.relay,  this);
13786             }
13787                     this.disabled = false;
13788                 }
13789         },
13790
13791         /**
13792          * Disable this KeyNav
13793          */
13794         disable: function(){
13795                 if(!this.disabled){
13796                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13797                 this.el.un("keydown", this.relay);
13798             }else{
13799                 this.el.un("keydown", this.prepareEvent);
13800                 this.el.un("keypress", this.relay);
13801             }
13802                     this.disabled = true;
13803                 }
13804         }
13805 };/*
13806  * Based on:
13807  * Ext JS Library 1.1.1
13808  * Copyright(c) 2006-2007, Ext JS, LLC.
13809  *
13810  * Originally Released Under LGPL - original licence link has changed is not relivant.
13811  *
13812  * Fork - LGPL
13813  * <script type="text/javascript">
13814  */
13815
13816  
13817 /**
13818  * @class Roo.KeyMap
13819  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13820  * The constructor accepts the same config object as defined by {@link #addBinding}.
13821  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13822  * combination it will call the function with this signature (if the match is a multi-key
13823  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13824  * A KeyMap can also handle a string representation of keys.<br />
13825  * Usage:
13826  <pre><code>
13827 // map one key by key code
13828 var map = new Roo.KeyMap("my-element", {
13829     key: 13, // or Roo.EventObject.ENTER
13830     fn: myHandler,
13831     scope: myObject
13832 });
13833
13834 // map multiple keys to one action by string
13835 var map = new Roo.KeyMap("my-element", {
13836     key: "a\r\n\t",
13837     fn: myHandler,
13838     scope: myObject
13839 });
13840
13841 // map multiple keys to multiple actions by strings and array of codes
13842 var map = new Roo.KeyMap("my-element", [
13843     {
13844         key: [10,13],
13845         fn: function(){ alert("Return was pressed"); }
13846     }, {
13847         key: "abc",
13848         fn: function(){ alert('a, b or c was pressed'); }
13849     }, {
13850         key: "\t",
13851         ctrl:true,
13852         shift:true,
13853         fn: function(){ alert('Control + shift + tab was pressed.'); }
13854     }
13855 ]);
13856 </code></pre>
13857  * <b>Note: A KeyMap starts enabled</b>
13858  * @constructor
13859  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13860  * @param {Object} config The config (see {@link #addBinding})
13861  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13862  */
13863 Roo.KeyMap = function(el, config, eventName){
13864     this.el  = Roo.get(el);
13865     this.eventName = eventName || "keydown";
13866     this.bindings = [];
13867     if(config){
13868         this.addBinding(config);
13869     }
13870     this.enable();
13871 };
13872
13873 Roo.KeyMap.prototype = {
13874     /**
13875      * True to stop the event from bubbling and prevent the default browser action if the
13876      * key was handled by the KeyMap (defaults to false)
13877      * @type Boolean
13878      */
13879     stopEvent : false,
13880
13881     /**
13882      * Add a new binding to this KeyMap. The following config object properties are supported:
13883      * <pre>
13884 Property    Type             Description
13885 ----------  ---------------  ----------------------------------------------------------------------
13886 key         String/Array     A single keycode or an array of keycodes to handle
13887 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13888 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13889 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13890 fn          Function         The function to call when KeyMap finds the expected key combination
13891 scope       Object           The scope of the callback function
13892 </pre>
13893      *
13894      * Usage:
13895      * <pre><code>
13896 // Create a KeyMap
13897 var map = new Roo.KeyMap(document, {
13898     key: Roo.EventObject.ENTER,
13899     fn: handleKey,
13900     scope: this
13901 });
13902
13903 //Add a new binding to the existing KeyMap later
13904 map.addBinding({
13905     key: 'abc',
13906     shift: true,
13907     fn: handleKey,
13908     scope: this
13909 });
13910 </code></pre>
13911      * @param {Object/Array} config A single KeyMap config or an array of configs
13912      */
13913         addBinding : function(config){
13914         if(config instanceof Array){
13915             for(var i = 0, len = config.length; i < len; i++){
13916                 this.addBinding(config[i]);
13917             }
13918             return;
13919         }
13920         var keyCode = config.key,
13921             shift = config.shift, 
13922             ctrl = config.ctrl, 
13923             alt = config.alt,
13924             fn = config.fn,
13925             scope = config.scope;
13926         if(typeof keyCode == "string"){
13927             var ks = [];
13928             var keyString = keyCode.toUpperCase();
13929             for(var j = 0, len = keyString.length; j < len; j++){
13930                 ks.push(keyString.charCodeAt(j));
13931             }
13932             keyCode = ks;
13933         }
13934         var keyArray = keyCode instanceof Array;
13935         var handler = function(e){
13936             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13937                 var k = e.getKey();
13938                 if(keyArray){
13939                     for(var i = 0, len = keyCode.length; i < len; i++){
13940                         if(keyCode[i] == k){
13941                           if(this.stopEvent){
13942                               e.stopEvent();
13943                           }
13944                           fn.call(scope || window, k, e);
13945                           return;
13946                         }
13947                     }
13948                 }else{
13949                     if(k == keyCode){
13950                         if(this.stopEvent){
13951                            e.stopEvent();
13952                         }
13953                         fn.call(scope || window, k, e);
13954                     }
13955                 }
13956             }
13957         };
13958         this.bindings.push(handler);  
13959         },
13960
13961     /**
13962      * Shorthand for adding a single key listener
13963      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13964      * following options:
13965      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13966      * @param {Function} fn The function to call
13967      * @param {Object} scope (optional) The scope of the function
13968      */
13969     on : function(key, fn, scope){
13970         var keyCode, shift, ctrl, alt;
13971         if(typeof key == "object" && !(key instanceof Array)){
13972             keyCode = key.key;
13973             shift = key.shift;
13974             ctrl = key.ctrl;
13975             alt = key.alt;
13976         }else{
13977             keyCode = key;
13978         }
13979         this.addBinding({
13980             key: keyCode,
13981             shift: shift,
13982             ctrl: ctrl,
13983             alt: alt,
13984             fn: fn,
13985             scope: scope
13986         })
13987     },
13988
13989     // private
13990     handleKeyDown : function(e){
13991             if(this.enabled){ //just in case
13992             var b = this.bindings;
13993             for(var i = 0, len = b.length; i < len; i++){
13994                 b[i].call(this, e);
13995             }
13996             }
13997         },
13998         
13999         /**
14000          * Returns true if this KeyMap is enabled
14001          * @return {Boolean} 
14002          */
14003         isEnabled : function(){
14004             return this.enabled;  
14005         },
14006         
14007         /**
14008          * Enables this KeyMap
14009          */
14010         enable: function(){
14011                 if(!this.enabled){
14012                     this.el.on(this.eventName, this.handleKeyDown, this);
14013                     this.enabled = true;
14014                 }
14015         },
14016
14017         /**
14018          * Disable this KeyMap
14019          */
14020         disable: function(){
14021                 if(this.enabled){
14022                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14023                     this.enabled = false;
14024                 }
14025         }
14026 };/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037  
14038 /**
14039  * @class Roo.util.TextMetrics
14040  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14041  * wide, in pixels, a given block of text will be.
14042  * @singleton
14043  */
14044 Roo.util.TextMetrics = function(){
14045     var shared;
14046     return {
14047         /**
14048          * Measures the size of the specified text
14049          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14050          * that can affect the size of the rendered text
14051          * @param {String} text The text to measure
14052          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14053          * in order to accurately measure the text height
14054          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14055          */
14056         measure : function(el, text, fixedWidth){
14057             if(!shared){
14058                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14059             }
14060             shared.bind(el);
14061             shared.setFixedWidth(fixedWidth || 'auto');
14062             return shared.getSize(text);
14063         },
14064
14065         /**
14066          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14067          * the overhead of multiple calls to initialize the style properties on each measurement.
14068          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14069          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14070          * in order to accurately measure the text height
14071          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14072          */
14073         createInstance : function(el, fixedWidth){
14074             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14075         }
14076     };
14077 }();
14078
14079  
14080
14081 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14082     var ml = new Roo.Element(document.createElement('div'));
14083     document.body.appendChild(ml.dom);
14084     ml.position('absolute');
14085     ml.setLeftTop(-1000, -1000);
14086     ml.hide();
14087
14088     if(fixedWidth){
14089         ml.setWidth(fixedWidth);
14090     }
14091      
14092     var instance = {
14093         /**
14094          * Returns the size of the specified text based on the internal element's style and width properties
14095          * @memberOf Roo.util.TextMetrics.Instance#
14096          * @param {String} text The text to measure
14097          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14098          */
14099         getSize : function(text){
14100             ml.update(text);
14101             var s = ml.getSize();
14102             ml.update('');
14103             return s;
14104         },
14105
14106         /**
14107          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14108          * that can affect the size of the rendered text
14109          * @memberOf Roo.util.TextMetrics.Instance#
14110          * @param {String/HTMLElement} el The element, dom node or id
14111          */
14112         bind : function(el){
14113             ml.setStyle(
14114                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14115             );
14116         },
14117
14118         /**
14119          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14120          * to set a fixed width in order to accurately measure the text height.
14121          * @memberOf Roo.util.TextMetrics.Instance#
14122          * @param {Number} width The width to set on the element
14123          */
14124         setFixedWidth : function(width){
14125             ml.setWidth(width);
14126         },
14127
14128         /**
14129          * Returns the measured width of the specified text
14130          * @memberOf Roo.util.TextMetrics.Instance#
14131          * @param {String} text The text to measure
14132          * @return {Number} width The width in pixels
14133          */
14134         getWidth : function(text){
14135             ml.dom.style.width = 'auto';
14136             return this.getSize(text).width;
14137         },
14138
14139         /**
14140          * Returns the measured height of the specified text.  For multiline text, be sure to call
14141          * {@link #setFixedWidth} if necessary.
14142          * @memberOf Roo.util.TextMetrics.Instance#
14143          * @param {String} text The text to measure
14144          * @return {Number} height The height in pixels
14145          */
14146         getHeight : function(text){
14147             return this.getSize(text).height;
14148         }
14149     };
14150
14151     instance.bind(bindTo);
14152
14153     return instance;
14154 };
14155
14156 // backwards compat
14157 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167
14168 /**
14169  * @class Roo.state.Provider
14170  * Abstract base class for state provider implementations. This class provides methods
14171  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14172  * Provider interface.
14173  */
14174 Roo.state.Provider = function(){
14175     /**
14176      * @event statechange
14177      * Fires when a state change occurs.
14178      * @param {Provider} this This state provider
14179      * @param {String} key The state key which was changed
14180      * @param {String} value The encoded value for the state
14181      */
14182     this.addEvents({
14183         "statechange": true
14184     });
14185     this.state = {};
14186     Roo.state.Provider.superclass.constructor.call(this);
14187 };
14188 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14189     /**
14190      * Returns the current value for a key
14191      * @param {String} name The key name
14192      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14193      * @return {Mixed} The state data
14194      */
14195     get : function(name, defaultValue){
14196         return typeof this.state[name] == "undefined" ?
14197             defaultValue : this.state[name];
14198     },
14199     
14200     /**
14201      * Clears a value from the state
14202      * @param {String} name The key name
14203      */
14204     clear : function(name){
14205         delete this.state[name];
14206         this.fireEvent("statechange", this, name, null);
14207     },
14208     
14209     /**
14210      * Sets the value for a key
14211      * @param {String} name The key name
14212      * @param {Mixed} value The value to set
14213      */
14214     set : function(name, value){
14215         this.state[name] = value;
14216         this.fireEvent("statechange", this, name, value);
14217     },
14218     
14219     /**
14220      * Decodes a string previously encoded with {@link #encodeValue}.
14221      * @param {String} value The value to decode
14222      * @return {Mixed} The decoded value
14223      */
14224     decodeValue : function(cookie){
14225         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14226         var matches = re.exec(unescape(cookie));
14227         if(!matches || !matches[1]) return; // non state cookie
14228         var type = matches[1];
14229         var v = matches[2];
14230         switch(type){
14231             case "n":
14232                 return parseFloat(v);
14233             case "d":
14234                 return new Date(Date.parse(v));
14235             case "b":
14236                 return (v == "1");
14237             case "a":
14238                 var all = [];
14239                 var values = v.split("^");
14240                 for(var i = 0, len = values.length; i < len; i++){
14241                     all.push(this.decodeValue(values[i]));
14242                 }
14243                 return all;
14244            case "o":
14245                 var all = {};
14246                 var values = v.split("^");
14247                 for(var i = 0, len = values.length; i < len; i++){
14248                     var kv = values[i].split("=");
14249                     all[kv[0]] = this.decodeValue(kv[1]);
14250                 }
14251                 return all;
14252            default:
14253                 return v;
14254         }
14255     },
14256     
14257     /**
14258      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14259      * @param {Mixed} value The value to encode
14260      * @return {String} The encoded value
14261      */
14262     encodeValue : function(v){
14263         var enc;
14264         if(typeof v == "number"){
14265             enc = "n:" + v;
14266         }else if(typeof v == "boolean"){
14267             enc = "b:" + (v ? "1" : "0");
14268         }else if(v instanceof Date){
14269             enc = "d:" + v.toGMTString();
14270         }else if(v instanceof Array){
14271             var flat = "";
14272             for(var i = 0, len = v.length; i < len; i++){
14273                 flat += this.encodeValue(v[i]);
14274                 if(i != len-1) flat += "^";
14275             }
14276             enc = "a:" + flat;
14277         }else if(typeof v == "object"){
14278             var flat = "";
14279             for(var key in v){
14280                 if(typeof v[key] != "function"){
14281                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14282                 }
14283             }
14284             enc = "o:" + flat.substring(0, flat.length-1);
14285         }else{
14286             enc = "s:" + v;
14287         }
14288         return escape(enc);        
14289     }
14290 });
14291
14292 /*
14293  * Based on:
14294  * Ext JS Library 1.1.1
14295  * Copyright(c) 2006-2007, Ext JS, LLC.
14296  *
14297  * Originally Released Under LGPL - original licence link has changed is not relivant.
14298  *
14299  * Fork - LGPL
14300  * <script type="text/javascript">
14301  */
14302 /**
14303  * @class Roo.state.Manager
14304  * This is the global state manager. By default all components that are "state aware" check this class
14305  * for state information if you don't pass them a custom state provider. In order for this class
14306  * to be useful, it must be initialized with a provider when your application initializes.
14307  <pre><code>
14308 // in your initialization function
14309 init : function(){
14310    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14311    ...
14312    // supposed you have a {@link Roo.BorderLayout}
14313    var layout = new Roo.BorderLayout(...);
14314    layout.restoreState();
14315    // or a {Roo.BasicDialog}
14316    var dialog = new Roo.BasicDialog(...);
14317    dialog.restoreState();
14318  </code></pre>
14319  * @singleton
14320  */
14321 Roo.state.Manager = function(){
14322     var provider = new Roo.state.Provider();
14323     
14324     return {
14325         /**
14326          * Configures the default state provider for your application
14327          * @param {Provider} stateProvider The state provider to set
14328          */
14329         setProvider : function(stateProvider){
14330             provider = stateProvider;
14331         },
14332         
14333         /**
14334          * Returns the current value for a key
14335          * @param {String} name The key name
14336          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14337          * @return {Mixed} The state data
14338          */
14339         get : function(key, defaultValue){
14340             return provider.get(key, defaultValue);
14341         },
14342         
14343         /**
14344          * Sets the value for a key
14345          * @param {String} name The key name
14346          * @param {Mixed} value The state data
14347          */
14348          set : function(key, value){
14349             provider.set(key, value);
14350         },
14351         
14352         /**
14353          * Clears a value from the state
14354          * @param {String} name The key name
14355          */
14356         clear : function(key){
14357             provider.clear(key);
14358         },
14359         
14360         /**
14361          * Gets the currently configured state provider
14362          * @return {Provider} The state provider
14363          */
14364         getProvider : function(){
14365             return provider;
14366         }
14367     };
14368 }();
14369 /*
14370  * Based on:
14371  * Ext JS Library 1.1.1
14372  * Copyright(c) 2006-2007, Ext JS, LLC.
14373  *
14374  * Originally Released Under LGPL - original licence link has changed is not relivant.
14375  *
14376  * Fork - LGPL
14377  * <script type="text/javascript">
14378  */
14379 /**
14380  * @class Roo.state.CookieProvider
14381  * @extends Roo.state.Provider
14382  * The default Provider implementation which saves state via cookies.
14383  * <br />Usage:
14384  <pre><code>
14385    var cp = new Roo.state.CookieProvider({
14386        path: "/cgi-bin/",
14387        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14388        domain: "roojs.com"
14389    })
14390    Roo.state.Manager.setProvider(cp);
14391  </code></pre>
14392  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14393  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14394  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14395  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14396  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14397  * domain the page is running on including the 'www' like 'www.roojs.com')
14398  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14399  * @constructor
14400  * Create a new CookieProvider
14401  * @param {Object} config The configuration object
14402  */
14403 Roo.state.CookieProvider = function(config){
14404     Roo.state.CookieProvider.superclass.constructor.call(this);
14405     this.path = "/";
14406     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14407     this.domain = null;
14408     this.secure = false;
14409     Roo.apply(this, config);
14410     this.state = this.readCookies();
14411 };
14412
14413 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14414     // private
14415     set : function(name, value){
14416         if(typeof value == "undefined" || value === null){
14417             this.clear(name);
14418             return;
14419         }
14420         this.setCookie(name, value);
14421         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14422     },
14423
14424     // private
14425     clear : function(name){
14426         this.clearCookie(name);
14427         Roo.state.CookieProvider.superclass.clear.call(this, name);
14428     },
14429
14430     // private
14431     readCookies : function(){
14432         var cookies = {};
14433         var c = document.cookie + ";";
14434         var re = /\s?(.*?)=(.*?);/g;
14435         var matches;
14436         while((matches = re.exec(c)) != null){
14437             var name = matches[1];
14438             var value = matches[2];
14439             if(name && name.substring(0,3) == "ys-"){
14440                 cookies[name.substr(3)] = this.decodeValue(value);
14441             }
14442         }
14443         return cookies;
14444     },
14445
14446     // private
14447     setCookie : function(name, value){
14448         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14449            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14450            ((this.path == null) ? "" : ("; path=" + this.path)) +
14451            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14452            ((this.secure == true) ? "; secure" : "");
14453     },
14454
14455     // private
14456     clearCookie : function(name){
14457         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14458            ((this.path == null) ? "" : ("; path=" + this.path)) +
14459            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14460            ((this.secure == true) ? "; secure" : "");
14461     }
14462 });/*
14463  * Based on:
14464  * Ext JS Library 1.1.1
14465  * Copyright(c) 2006-2007, Ext JS, LLC.
14466  *
14467  * Originally Released Under LGPL - original licence link has changed is not relivant.
14468  *
14469  * Fork - LGPL
14470  * <script type="text/javascript">
14471  */
14472
14473
14474
14475 /*
14476  * These classes are derivatives of the similarly named classes in the YUI Library.
14477  * The original license:
14478  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14479  * Code licensed under the BSD License:
14480  * http://developer.yahoo.net/yui/license.txt
14481  */
14482
14483 (function() {
14484
14485 var Event=Roo.EventManager;
14486 var Dom=Roo.lib.Dom;
14487
14488 /**
14489  * @class Roo.dd.DragDrop
14490  * @extends Roo.util.Observable
14491  * Defines the interface and base operation of items that that can be
14492  * dragged or can be drop targets.  It was designed to be extended, overriding
14493  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14494  * Up to three html elements can be associated with a DragDrop instance:
14495  * <ul>
14496  * <li>linked element: the element that is passed into the constructor.
14497  * This is the element which defines the boundaries for interaction with
14498  * other DragDrop objects.</li>
14499  * <li>handle element(s): The drag operation only occurs if the element that
14500  * was clicked matches a handle element.  By default this is the linked
14501  * element, but there are times that you will want only a portion of the
14502  * linked element to initiate the drag operation, and the setHandleElId()
14503  * method provides a way to define this.</li>
14504  * <li>drag element: this represents the element that would be moved along
14505  * with the cursor during a drag operation.  By default, this is the linked
14506  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14507  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14508  * </li>
14509  * </ul>
14510  * This class should not be instantiated until the onload event to ensure that
14511  * the associated elements are available.
14512  * The following would define a DragDrop obj that would interact with any
14513  * other DragDrop obj in the "group1" group:
14514  * <pre>
14515  *  dd = new Roo.dd.DragDrop("div1", "group1");
14516  * </pre>
14517  * Since none of the event handlers have been implemented, nothing would
14518  * actually happen if you were to run the code above.  Normally you would
14519  * override this class or one of the default implementations, but you can
14520  * also override the methods you want on an instance of the class...
14521  * <pre>
14522  *  dd.onDragDrop = function(e, id) {
14523  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14524  *  }
14525  * </pre>
14526  * @constructor
14527  * @param {String} id of the element that is linked to this instance
14528  * @param {String} sGroup the group of related DragDrop objects
14529  * @param {object} config an object containing configurable attributes
14530  *                Valid properties for DragDrop:
14531  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14532  */
14533 Roo.dd.DragDrop = function(id, sGroup, config) {
14534     if (id) {
14535         this.init(id, sGroup, config);
14536     }
14537     
14538 };
14539
14540 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14541
14542     /**
14543      * The id of the element associated with this object.  This is what we
14544      * refer to as the "linked element" because the size and position of
14545      * this element is used to determine when the drag and drop objects have
14546      * interacted.
14547      * @property id
14548      * @type String
14549      */
14550     id: null,
14551
14552     /**
14553      * Configuration attributes passed into the constructor
14554      * @property config
14555      * @type object
14556      */
14557     config: null,
14558
14559     /**
14560      * The id of the element that will be dragged.  By default this is same
14561      * as the linked element , but could be changed to another element. Ex:
14562      * Roo.dd.DDProxy
14563      * @property dragElId
14564      * @type String
14565      * @private
14566      */
14567     dragElId: null,
14568
14569     /**
14570      * the id of the element that initiates the drag operation.  By default
14571      * this is the linked element, but could be changed to be a child of this
14572      * element.  This lets us do things like only starting the drag when the
14573      * header element within the linked html element is clicked.
14574      * @property handleElId
14575      * @type String
14576      * @private
14577      */
14578     handleElId: null,
14579
14580     /**
14581      * An associative array of HTML tags that will be ignored if clicked.
14582      * @property invalidHandleTypes
14583      * @type {string: string}
14584      */
14585     invalidHandleTypes: null,
14586
14587     /**
14588      * An associative array of ids for elements that will be ignored if clicked
14589      * @property invalidHandleIds
14590      * @type {string: string}
14591      */
14592     invalidHandleIds: null,
14593
14594     /**
14595      * An indexted array of css class names for elements that will be ignored
14596      * if clicked.
14597      * @property invalidHandleClasses
14598      * @type string[]
14599      */
14600     invalidHandleClasses: null,
14601
14602     /**
14603      * The linked element's absolute X position at the time the drag was
14604      * started
14605      * @property startPageX
14606      * @type int
14607      * @private
14608      */
14609     startPageX: 0,
14610
14611     /**
14612      * The linked element's absolute X position at the time the drag was
14613      * started
14614      * @property startPageY
14615      * @type int
14616      * @private
14617      */
14618     startPageY: 0,
14619
14620     /**
14621      * The group defines a logical collection of DragDrop objects that are
14622      * related.  Instances only get events when interacting with other
14623      * DragDrop object in the same group.  This lets us define multiple
14624      * groups using a single DragDrop subclass if we want.
14625      * @property groups
14626      * @type {string: string}
14627      */
14628     groups: null,
14629
14630     /**
14631      * Individual drag/drop instances can be locked.  This will prevent
14632      * onmousedown start drag.
14633      * @property locked
14634      * @type boolean
14635      * @private
14636      */
14637     locked: false,
14638
14639     /**
14640      * Lock this instance
14641      * @method lock
14642      */
14643     lock: function() { this.locked = true; },
14644
14645     /**
14646      * Unlock this instace
14647      * @method unlock
14648      */
14649     unlock: function() { this.locked = false; },
14650
14651     /**
14652      * By default, all insances can be a drop target.  This can be disabled by
14653      * setting isTarget to false.
14654      * @method isTarget
14655      * @type boolean
14656      */
14657     isTarget: true,
14658
14659     /**
14660      * The padding configured for this drag and drop object for calculating
14661      * the drop zone intersection with this object.
14662      * @method padding
14663      * @type int[]
14664      */
14665     padding: null,
14666
14667     /**
14668      * Cached reference to the linked element
14669      * @property _domRef
14670      * @private
14671      */
14672     _domRef: null,
14673
14674     /**
14675      * Internal typeof flag
14676      * @property __ygDragDrop
14677      * @private
14678      */
14679     __ygDragDrop: true,
14680
14681     /**
14682      * Set to true when horizontal contraints are applied
14683      * @property constrainX
14684      * @type boolean
14685      * @private
14686      */
14687     constrainX: false,
14688
14689     /**
14690      * Set to true when vertical contraints are applied
14691      * @property constrainY
14692      * @type boolean
14693      * @private
14694      */
14695     constrainY: false,
14696
14697     /**
14698      * The left constraint
14699      * @property minX
14700      * @type int
14701      * @private
14702      */
14703     minX: 0,
14704
14705     /**
14706      * The right constraint
14707      * @property maxX
14708      * @type int
14709      * @private
14710      */
14711     maxX: 0,
14712
14713     /**
14714      * The up constraint
14715      * @property minY
14716      * @type int
14717      * @type int
14718      * @private
14719      */
14720     minY: 0,
14721
14722     /**
14723      * The down constraint
14724      * @property maxY
14725      * @type int
14726      * @private
14727      */
14728     maxY: 0,
14729
14730     /**
14731      * Maintain offsets when we resetconstraints.  Set to true when you want
14732      * the position of the element relative to its parent to stay the same
14733      * when the page changes
14734      *
14735      * @property maintainOffset
14736      * @type boolean
14737      */
14738     maintainOffset: false,
14739
14740     /**
14741      * Array of pixel locations the element will snap to if we specified a
14742      * horizontal graduation/interval.  This array is generated automatically
14743      * when you define a tick interval.
14744      * @property xTicks
14745      * @type int[]
14746      */
14747     xTicks: null,
14748
14749     /**
14750      * Array of pixel locations the element will snap to if we specified a
14751      * vertical graduation/interval.  This array is generated automatically
14752      * when you define a tick interval.
14753      * @property yTicks
14754      * @type int[]
14755      */
14756     yTicks: null,
14757
14758     /**
14759      * By default the drag and drop instance will only respond to the primary
14760      * button click (left button for a right-handed mouse).  Set to true to
14761      * allow drag and drop to start with any mouse click that is propogated
14762      * by the browser
14763      * @property primaryButtonOnly
14764      * @type boolean
14765      */
14766     primaryButtonOnly: true,
14767
14768     /**
14769      * The availabe property is false until the linked dom element is accessible.
14770      * @property available
14771      * @type boolean
14772      */
14773     available: false,
14774
14775     /**
14776      * By default, drags can only be initiated if the mousedown occurs in the
14777      * region the linked element is.  This is done in part to work around a
14778      * bug in some browsers that mis-report the mousedown if the previous
14779      * mouseup happened outside of the window.  This property is set to true
14780      * if outer handles are defined.
14781      *
14782      * @property hasOuterHandles
14783      * @type boolean
14784      * @default false
14785      */
14786     hasOuterHandles: false,
14787
14788     /**
14789      * Code that executes immediately before the startDrag event
14790      * @method b4StartDrag
14791      * @private
14792      */
14793     b4StartDrag: function(x, y) { },
14794
14795     /**
14796      * Abstract method called after a drag/drop object is clicked
14797      * and the drag or mousedown time thresholds have beeen met.
14798      * @method startDrag
14799      * @param {int} X click location
14800      * @param {int} Y click location
14801      */
14802     startDrag: function(x, y) { /* override this */ },
14803
14804     /**
14805      * Code that executes immediately before the onDrag event
14806      * @method b4Drag
14807      * @private
14808      */
14809     b4Drag: function(e) { },
14810
14811     /**
14812      * Abstract method called during the onMouseMove event while dragging an
14813      * object.
14814      * @method onDrag
14815      * @param {Event} e the mousemove event
14816      */
14817     onDrag: function(e) { /* override this */ },
14818
14819     /**
14820      * Abstract method called when this element fist begins hovering over
14821      * another DragDrop obj
14822      * @method onDragEnter
14823      * @param {Event} e the mousemove event
14824      * @param {String|DragDrop[]} id In POINT mode, the element
14825      * id this is hovering over.  In INTERSECT mode, an array of one or more
14826      * dragdrop items being hovered over.
14827      */
14828     onDragEnter: function(e, id) { /* override this */ },
14829
14830     /**
14831      * Code that executes immediately before the onDragOver event
14832      * @method b4DragOver
14833      * @private
14834      */
14835     b4DragOver: function(e) { },
14836
14837     /**
14838      * Abstract method called when this element is hovering over another
14839      * DragDrop obj
14840      * @method onDragOver
14841      * @param {Event} e the mousemove event
14842      * @param {String|DragDrop[]} id In POINT mode, the element
14843      * id this is hovering over.  In INTERSECT mode, an array of dd items
14844      * being hovered over.
14845      */
14846     onDragOver: function(e, id) { /* override this */ },
14847
14848     /**
14849      * Code that executes immediately before the onDragOut event
14850      * @method b4DragOut
14851      * @private
14852      */
14853     b4DragOut: function(e) { },
14854
14855     /**
14856      * Abstract method called when we are no longer hovering over an element
14857      * @method onDragOut
14858      * @param {Event} e the mousemove event
14859      * @param {String|DragDrop[]} id In POINT mode, the element
14860      * id this was hovering over.  In INTERSECT mode, an array of dd items
14861      * that the mouse is no longer over.
14862      */
14863     onDragOut: function(e, id) { /* override this */ },
14864
14865     /**
14866      * Code that executes immediately before the onDragDrop event
14867      * @method b4DragDrop
14868      * @private
14869      */
14870     b4DragDrop: function(e) { },
14871
14872     /**
14873      * Abstract method called when this item is dropped on another DragDrop
14874      * obj
14875      * @method onDragDrop
14876      * @param {Event} e the mouseup event
14877      * @param {String|DragDrop[]} id In POINT mode, the element
14878      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14879      * was dropped on.
14880      */
14881     onDragDrop: function(e, id) { /* override this */ },
14882
14883     /**
14884      * Abstract method called when this item is dropped on an area with no
14885      * drop target
14886      * @method onInvalidDrop
14887      * @param {Event} e the mouseup event
14888      */
14889     onInvalidDrop: function(e) { /* override this */ },
14890
14891     /**
14892      * Code that executes immediately before the endDrag event
14893      * @method b4EndDrag
14894      * @private
14895      */
14896     b4EndDrag: function(e) { },
14897
14898     /**
14899      * Fired when we are done dragging the object
14900      * @method endDrag
14901      * @param {Event} e the mouseup event
14902      */
14903     endDrag: function(e) { /* override this */ },
14904
14905     /**
14906      * Code executed immediately before the onMouseDown event
14907      * @method b4MouseDown
14908      * @param {Event} e the mousedown event
14909      * @private
14910      */
14911     b4MouseDown: function(e) {  },
14912
14913     /**
14914      * Event handler that fires when a drag/drop obj gets a mousedown
14915      * @method onMouseDown
14916      * @param {Event} e the mousedown event
14917      */
14918     onMouseDown: function(e) { /* override this */ },
14919
14920     /**
14921      * Event handler that fires when a drag/drop obj gets a mouseup
14922      * @method onMouseUp
14923      * @param {Event} e the mouseup event
14924      */
14925     onMouseUp: function(e) { /* override this */ },
14926
14927     /**
14928      * Override the onAvailable method to do what is needed after the initial
14929      * position was determined.
14930      * @method onAvailable
14931      */
14932     onAvailable: function () {
14933     },
14934
14935     /*
14936      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14937      * @type Object
14938      */
14939     defaultPadding : {left:0, right:0, top:0, bottom:0},
14940
14941     /*
14942      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14943  *
14944  * Usage:
14945  <pre><code>
14946  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14947                 { dragElId: "existingProxyDiv" });
14948  dd.startDrag = function(){
14949      this.constrainTo("parent-id");
14950  };
14951  </code></pre>
14952  * Or you can initalize it using the {@link Roo.Element} object:
14953  <pre><code>
14954  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14955      startDrag : function(){
14956          this.constrainTo("parent-id");
14957      }
14958  });
14959  </code></pre>
14960      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14961      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14962      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14963      * an object containing the sides to pad. For example: {right:10, bottom:10}
14964      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14965      */
14966     constrainTo : function(constrainTo, pad, inContent){
14967         if(typeof pad == "number"){
14968             pad = {left: pad, right:pad, top:pad, bottom:pad};
14969         }
14970         pad = pad || this.defaultPadding;
14971         var b = Roo.get(this.getEl()).getBox();
14972         var ce = Roo.get(constrainTo);
14973         var s = ce.getScroll();
14974         var c, cd = ce.dom;
14975         if(cd == document.body){
14976             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14977         }else{
14978             xy = ce.getXY();
14979             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14980         }
14981
14982
14983         var topSpace = b.y - c.y;
14984         var leftSpace = b.x - c.x;
14985
14986         this.resetConstraints();
14987         this.setXConstraint(leftSpace - (pad.left||0), // left
14988                 c.width - leftSpace - b.width - (pad.right||0) //right
14989         );
14990         this.setYConstraint(topSpace - (pad.top||0), //top
14991                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14992         );
14993     },
14994
14995     /**
14996      * Returns a reference to the linked element
14997      * @method getEl
14998      * @return {HTMLElement} the html element
14999      */
15000     getEl: function() {
15001         if (!this._domRef) {
15002             this._domRef = Roo.getDom(this.id);
15003         }
15004
15005         return this._domRef;
15006     },
15007
15008     /**
15009      * Returns a reference to the actual element to drag.  By default this is
15010      * the same as the html element, but it can be assigned to another
15011      * element. An example of this can be found in Roo.dd.DDProxy
15012      * @method getDragEl
15013      * @return {HTMLElement} the html element
15014      */
15015     getDragEl: function() {
15016         return Roo.getDom(this.dragElId);
15017     },
15018
15019     /**
15020      * Sets up the DragDrop object.  Must be called in the constructor of any
15021      * Roo.dd.DragDrop subclass
15022      * @method init
15023      * @param id the id of the linked element
15024      * @param {String} sGroup the group of related items
15025      * @param {object} config configuration attributes
15026      */
15027     init: function(id, sGroup, config) {
15028         this.initTarget(id, sGroup, config);
15029         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15030         // Event.on(this.id, "selectstart", Event.preventDefault);
15031     },
15032
15033     /**
15034      * Initializes Targeting functionality only... the object does not
15035      * get a mousedown handler.
15036      * @method initTarget
15037      * @param id the id of the linked element
15038      * @param {String} sGroup the group of related items
15039      * @param {object} config configuration attributes
15040      */
15041     initTarget: function(id, sGroup, config) {
15042
15043         // configuration attributes
15044         this.config = config || {};
15045
15046         // create a local reference to the drag and drop manager
15047         this.DDM = Roo.dd.DDM;
15048         // initialize the groups array
15049         this.groups = {};
15050
15051         // assume that we have an element reference instead of an id if the
15052         // parameter is not a string
15053         if (typeof id !== "string") {
15054             id = Roo.id(id);
15055         }
15056
15057         // set the id
15058         this.id = id;
15059
15060         // add to an interaction group
15061         this.addToGroup((sGroup) ? sGroup : "default");
15062
15063         // We don't want to register this as the handle with the manager
15064         // so we just set the id rather than calling the setter.
15065         this.handleElId = id;
15066
15067         // the linked element is the element that gets dragged by default
15068         this.setDragElId(id);
15069
15070         // by default, clicked anchors will not start drag operations.
15071         this.invalidHandleTypes = { A: "A" };
15072         this.invalidHandleIds = {};
15073         this.invalidHandleClasses = [];
15074
15075         this.applyConfig();
15076
15077         this.handleOnAvailable();
15078     },
15079
15080     /**
15081      * Applies the configuration parameters that were passed into the constructor.
15082      * This is supposed to happen at each level through the inheritance chain.  So
15083      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15084      * DragDrop in order to get all of the parameters that are available in
15085      * each object.
15086      * @method applyConfig
15087      */
15088     applyConfig: function() {
15089
15090         // configurable properties:
15091         //    padding, isTarget, maintainOffset, primaryButtonOnly
15092         this.padding           = this.config.padding || [0, 0, 0, 0];
15093         this.isTarget          = (this.config.isTarget !== false);
15094         this.maintainOffset    = (this.config.maintainOffset);
15095         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15096
15097     },
15098
15099     /**
15100      * Executed when the linked element is available
15101      * @method handleOnAvailable
15102      * @private
15103      */
15104     handleOnAvailable: function() {
15105         this.available = true;
15106         this.resetConstraints();
15107         this.onAvailable();
15108     },
15109
15110      /**
15111      * Configures the padding for the target zone in px.  Effectively expands
15112      * (or reduces) the virtual object size for targeting calculations.
15113      * Supports css-style shorthand; if only one parameter is passed, all sides
15114      * will have that padding, and if only two are passed, the top and bottom
15115      * will have the first param, the left and right the second.
15116      * @method setPadding
15117      * @param {int} iTop    Top pad
15118      * @param {int} iRight  Right pad
15119      * @param {int} iBot    Bot pad
15120      * @param {int} iLeft   Left pad
15121      */
15122     setPadding: function(iTop, iRight, iBot, iLeft) {
15123         // this.padding = [iLeft, iRight, iTop, iBot];
15124         if (!iRight && 0 !== iRight) {
15125             this.padding = [iTop, iTop, iTop, iTop];
15126         } else if (!iBot && 0 !== iBot) {
15127             this.padding = [iTop, iRight, iTop, iRight];
15128         } else {
15129             this.padding = [iTop, iRight, iBot, iLeft];
15130         }
15131     },
15132
15133     /**
15134      * Stores the initial placement of the linked element.
15135      * @method setInitialPosition
15136      * @param {int} diffX   the X offset, default 0
15137      * @param {int} diffY   the Y offset, default 0
15138      */
15139     setInitPosition: function(diffX, diffY) {
15140         var el = this.getEl();
15141
15142         if (!this.DDM.verifyEl(el)) {
15143             return;
15144         }
15145
15146         var dx = diffX || 0;
15147         var dy = diffY || 0;
15148
15149         var p = Dom.getXY( el );
15150
15151         this.initPageX = p[0] - dx;
15152         this.initPageY = p[1] - dy;
15153
15154         this.lastPageX = p[0];
15155         this.lastPageY = p[1];
15156
15157
15158         this.setStartPosition(p);
15159     },
15160
15161     /**
15162      * Sets the start position of the element.  This is set when the obj
15163      * is initialized, the reset when a drag is started.
15164      * @method setStartPosition
15165      * @param pos current position (from previous lookup)
15166      * @private
15167      */
15168     setStartPosition: function(pos) {
15169         var p = pos || Dom.getXY( this.getEl() );
15170         this.deltaSetXY = null;
15171
15172         this.startPageX = p[0];
15173         this.startPageY = p[1];
15174     },
15175
15176     /**
15177      * Add this instance to a group of related drag/drop objects.  All
15178      * instances belong to at least one group, and can belong to as many
15179      * groups as needed.
15180      * @method addToGroup
15181      * @param sGroup {string} the name of the group
15182      */
15183     addToGroup: function(sGroup) {
15184         this.groups[sGroup] = true;
15185         this.DDM.regDragDrop(this, sGroup);
15186     },
15187
15188     /**
15189      * Remove's this instance from the supplied interaction group
15190      * @method removeFromGroup
15191      * @param {string}  sGroup  The group to drop
15192      */
15193     removeFromGroup: function(sGroup) {
15194         if (this.groups[sGroup]) {
15195             delete this.groups[sGroup];
15196         }
15197
15198         this.DDM.removeDDFromGroup(this, sGroup);
15199     },
15200
15201     /**
15202      * Allows you to specify that an element other than the linked element
15203      * will be moved with the cursor during a drag
15204      * @method setDragElId
15205      * @param id {string} the id of the element that will be used to initiate the drag
15206      */
15207     setDragElId: function(id) {
15208         this.dragElId = id;
15209     },
15210
15211     /**
15212      * Allows you to specify a child of the linked element that should be
15213      * used to initiate the drag operation.  An example of this would be if
15214      * you have a content div with text and links.  Clicking anywhere in the
15215      * content area would normally start the drag operation.  Use this method
15216      * to specify that an element inside of the content div is the element
15217      * that starts the drag operation.
15218      * @method setHandleElId
15219      * @param id {string} the id of the element that will be used to
15220      * initiate the drag.
15221      */
15222     setHandleElId: function(id) {
15223         if (typeof id !== "string") {
15224             id = Roo.id(id);
15225         }
15226         this.handleElId = id;
15227         this.DDM.regHandle(this.id, id);
15228     },
15229
15230     /**
15231      * Allows you to set an element outside of the linked element as a drag
15232      * handle
15233      * @method setOuterHandleElId
15234      * @param id the id of the element that will be used to initiate the drag
15235      */
15236     setOuterHandleElId: function(id) {
15237         if (typeof id !== "string") {
15238             id = Roo.id(id);
15239         }
15240         Event.on(id, "mousedown",
15241                 this.handleMouseDown, this);
15242         this.setHandleElId(id);
15243
15244         this.hasOuterHandles = true;
15245     },
15246
15247     /**
15248      * Remove all drag and drop hooks for this element
15249      * @method unreg
15250      */
15251     unreg: function() {
15252         Event.un(this.id, "mousedown",
15253                 this.handleMouseDown);
15254         this._domRef = null;
15255         this.DDM._remove(this);
15256     },
15257
15258     destroy : function(){
15259         this.unreg();
15260     },
15261
15262     /**
15263      * Returns true if this instance is locked, or the drag drop mgr is locked
15264      * (meaning that all drag/drop is disabled on the page.)
15265      * @method isLocked
15266      * @return {boolean} true if this obj or all drag/drop is locked, else
15267      * false
15268      */
15269     isLocked: function() {
15270         return (this.DDM.isLocked() || this.locked);
15271     },
15272
15273     /**
15274      * Fired when this object is clicked
15275      * @method handleMouseDown
15276      * @param {Event} e
15277      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15278      * @private
15279      */
15280     handleMouseDown: function(e, oDD){
15281         if (this.primaryButtonOnly && e.button != 0) {
15282             return;
15283         }
15284
15285         if (this.isLocked()) {
15286             return;
15287         }
15288
15289         this.DDM.refreshCache(this.groups);
15290
15291         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15292         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15293         } else {
15294             if (this.clickValidator(e)) {
15295
15296                 // set the initial element position
15297                 this.setStartPosition();
15298
15299
15300                 this.b4MouseDown(e);
15301                 this.onMouseDown(e);
15302
15303                 this.DDM.handleMouseDown(e, this);
15304
15305                 this.DDM.stopEvent(e);
15306             } else {
15307
15308
15309             }
15310         }
15311     },
15312
15313     clickValidator: function(e) {
15314         var target = e.getTarget();
15315         return ( this.isValidHandleChild(target) &&
15316                     (this.id == this.handleElId ||
15317                         this.DDM.handleWasClicked(target, this.id)) );
15318     },
15319
15320     /**
15321      * Allows you to specify a tag name that should not start a drag operation
15322      * when clicked.  This is designed to facilitate embedding links within a
15323      * drag handle that do something other than start the drag.
15324      * @method addInvalidHandleType
15325      * @param {string} tagName the type of element to exclude
15326      */
15327     addInvalidHandleType: function(tagName) {
15328         var type = tagName.toUpperCase();
15329         this.invalidHandleTypes[type] = type;
15330     },
15331
15332     /**
15333      * Lets you to specify an element id for a child of a drag handle
15334      * that should not initiate a drag
15335      * @method addInvalidHandleId
15336      * @param {string} id the element id of the element you wish to ignore
15337      */
15338     addInvalidHandleId: function(id) {
15339         if (typeof id !== "string") {
15340             id = Roo.id(id);
15341         }
15342         this.invalidHandleIds[id] = id;
15343     },
15344
15345     /**
15346      * Lets you specify a css class of elements that will not initiate a drag
15347      * @method addInvalidHandleClass
15348      * @param {string} cssClass the class of the elements you wish to ignore
15349      */
15350     addInvalidHandleClass: function(cssClass) {
15351         this.invalidHandleClasses.push(cssClass);
15352     },
15353
15354     /**
15355      * Unsets an excluded tag name set by addInvalidHandleType
15356      * @method removeInvalidHandleType
15357      * @param {string} tagName the type of element to unexclude
15358      */
15359     removeInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         // this.invalidHandleTypes[type] = null;
15362         delete this.invalidHandleTypes[type];
15363     },
15364
15365     /**
15366      * Unsets an invalid handle id
15367      * @method removeInvalidHandleId
15368      * @param {string} id the id of the element to re-enable
15369      */
15370     removeInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         delete this.invalidHandleIds[id];
15375     },
15376
15377     /**
15378      * Unsets an invalid css class
15379      * @method removeInvalidHandleClass
15380      * @param {string} cssClass the class of the element(s) you wish to
15381      * re-enable
15382      */
15383     removeInvalidHandleClass: function(cssClass) {
15384         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15385             if (this.invalidHandleClasses[i] == cssClass) {
15386                 delete this.invalidHandleClasses[i];
15387             }
15388         }
15389     },
15390
15391     /**
15392      * Checks the tag exclusion list to see if this click should be ignored
15393      * @method isValidHandleChild
15394      * @param {HTMLElement} node the HTMLElement to evaluate
15395      * @return {boolean} true if this is a valid tag type, false if not
15396      */
15397     isValidHandleChild: function(node) {
15398
15399         var valid = true;
15400         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15401         var nodeName;
15402         try {
15403             nodeName = node.nodeName.toUpperCase();
15404         } catch(e) {
15405             nodeName = node.nodeName;
15406         }
15407         valid = valid && !this.invalidHandleTypes[nodeName];
15408         valid = valid && !this.invalidHandleIds[node.id];
15409
15410         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15411             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15412         }
15413
15414
15415         return valid;
15416
15417     },
15418
15419     /**
15420      * Create the array of horizontal tick marks if an interval was specified
15421      * in setXConstraint().
15422      * @method setXTicks
15423      * @private
15424      */
15425     setXTicks: function(iStartX, iTickSize) {
15426         this.xTicks = [];
15427         this.xTickSize = iTickSize;
15428
15429         var tickMap = {};
15430
15431         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15432             if (!tickMap[i]) {
15433                 this.xTicks[this.xTicks.length] = i;
15434                 tickMap[i] = true;
15435             }
15436         }
15437
15438         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15439             if (!tickMap[i]) {
15440                 this.xTicks[this.xTicks.length] = i;
15441                 tickMap[i] = true;
15442             }
15443         }
15444
15445         this.xTicks.sort(this.DDM.numericSort) ;
15446     },
15447
15448     /**
15449      * Create the array of vertical tick marks if an interval was specified in
15450      * setYConstraint().
15451      * @method setYTicks
15452      * @private
15453      */
15454     setYTicks: function(iStartY, iTickSize) {
15455         this.yTicks = [];
15456         this.yTickSize = iTickSize;
15457
15458         var tickMap = {};
15459
15460         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15461             if (!tickMap[i]) {
15462                 this.yTicks[this.yTicks.length] = i;
15463                 tickMap[i] = true;
15464             }
15465         }
15466
15467         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15468             if (!tickMap[i]) {
15469                 this.yTicks[this.yTicks.length] = i;
15470                 tickMap[i] = true;
15471             }
15472         }
15473
15474         this.yTicks.sort(this.DDM.numericSort) ;
15475     },
15476
15477     /**
15478      * By default, the element can be dragged any place on the screen.  Use
15479      * this method to limit the horizontal travel of the element.  Pass in
15480      * 0,0 for the parameters if you want to lock the drag to the y axis.
15481      * @method setXConstraint
15482      * @param {int} iLeft the number of pixels the element can move to the left
15483      * @param {int} iRight the number of pixels the element can move to the
15484      * right
15485      * @param {int} iTickSize optional parameter for specifying that the
15486      * element
15487      * should move iTickSize pixels at a time.
15488      */
15489     setXConstraint: function(iLeft, iRight, iTickSize) {
15490         this.leftConstraint = iLeft;
15491         this.rightConstraint = iRight;
15492
15493         this.minX = this.initPageX - iLeft;
15494         this.maxX = this.initPageX + iRight;
15495         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15496
15497         this.constrainX = true;
15498     },
15499
15500     /**
15501      * Clears any constraints applied to this instance.  Also clears ticks
15502      * since they can't exist independent of a constraint at this time.
15503      * @method clearConstraints
15504      */
15505     clearConstraints: function() {
15506         this.constrainX = false;
15507         this.constrainY = false;
15508         this.clearTicks();
15509     },
15510
15511     /**
15512      * Clears any tick interval defined for this instance
15513      * @method clearTicks
15514      */
15515     clearTicks: function() {
15516         this.xTicks = null;
15517         this.yTicks = null;
15518         this.xTickSize = 0;
15519         this.yTickSize = 0;
15520     },
15521
15522     /**
15523      * By default, the element can be dragged any place on the screen.  Set
15524      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15525      * parameters if you want to lock the drag to the x axis.
15526      * @method setYConstraint
15527      * @param {int} iUp the number of pixels the element can move up
15528      * @param {int} iDown the number of pixels the element can move down
15529      * @param {int} iTickSize optional parameter for specifying that the
15530      * element should move iTickSize pixels at a time.
15531      */
15532     setYConstraint: function(iUp, iDown, iTickSize) {
15533         this.topConstraint = iUp;
15534         this.bottomConstraint = iDown;
15535
15536         this.minY = this.initPageY - iUp;
15537         this.maxY = this.initPageY + iDown;
15538         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15539
15540         this.constrainY = true;
15541
15542     },
15543
15544     /**
15545      * resetConstraints must be called if you manually reposition a dd element.
15546      * @method resetConstraints
15547      * @param {boolean} maintainOffset
15548      */
15549     resetConstraints: function() {
15550
15551
15552         // Maintain offsets if necessary
15553         if (this.initPageX || this.initPageX === 0) {
15554             // figure out how much this thing has moved
15555             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15556             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15557
15558             this.setInitPosition(dx, dy);
15559
15560         // This is the first time we have detected the element's position
15561         } else {
15562             this.setInitPosition();
15563         }
15564
15565         if (this.constrainX) {
15566             this.setXConstraint( this.leftConstraint,
15567                                  this.rightConstraint,
15568                                  this.xTickSize        );
15569         }
15570
15571         if (this.constrainY) {
15572             this.setYConstraint( this.topConstraint,
15573                                  this.bottomConstraint,
15574                                  this.yTickSize         );
15575         }
15576     },
15577
15578     /**
15579      * Normally the drag element is moved pixel by pixel, but we can specify
15580      * that it move a number of pixels at a time.  This method resolves the
15581      * location when we have it set up like this.
15582      * @method getTick
15583      * @param {int} val where we want to place the object
15584      * @param {int[]} tickArray sorted array of valid points
15585      * @return {int} the closest tick
15586      * @private
15587      */
15588     getTick: function(val, tickArray) {
15589
15590         if (!tickArray) {
15591             // If tick interval is not defined, it is effectively 1 pixel,
15592             // so we return the value passed to us.
15593             return val;
15594         } else if (tickArray[0] >= val) {
15595             // The value is lower than the first tick, so we return the first
15596             // tick.
15597             return tickArray[0];
15598         } else {
15599             for (var i=0, len=tickArray.length; i<len; ++i) {
15600                 var next = i + 1;
15601                 if (tickArray[next] && tickArray[next] >= val) {
15602                     var diff1 = val - tickArray[i];
15603                     var diff2 = tickArray[next] - val;
15604                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15605                 }
15606             }
15607
15608             // The value is larger than the last tick, so we return the last
15609             // tick.
15610             return tickArray[tickArray.length - 1];
15611         }
15612     },
15613
15614     /**
15615      * toString method
15616      * @method toString
15617      * @return {string} string representation of the dd obj
15618      */
15619     toString: function() {
15620         return ("DragDrop " + this.id);
15621     }
15622
15623 });
15624
15625 })();
15626 /*
15627  * Based on:
15628  * Ext JS Library 1.1.1
15629  * Copyright(c) 2006-2007, Ext JS, LLC.
15630  *
15631  * Originally Released Under LGPL - original licence link has changed is not relivant.
15632  *
15633  * Fork - LGPL
15634  * <script type="text/javascript">
15635  */
15636
15637
15638 /**
15639  * The drag and drop utility provides a framework for building drag and drop
15640  * applications.  In addition to enabling drag and drop for specific elements,
15641  * the drag and drop elements are tracked by the manager class, and the
15642  * interactions between the various elements are tracked during the drag and
15643  * the implementing code is notified about these important moments.
15644  */
15645
15646 // Only load the library once.  Rewriting the manager class would orphan
15647 // existing drag and drop instances.
15648 if (!Roo.dd.DragDropMgr) {
15649
15650 /**
15651  * @class Roo.dd.DragDropMgr
15652  * DragDropMgr is a singleton that tracks the element interaction for
15653  * all DragDrop items in the window.  Generally, you will not call
15654  * this class directly, but it does have helper methods that could
15655  * be useful in your DragDrop implementations.
15656  * @singleton
15657  */
15658 Roo.dd.DragDropMgr = function() {
15659
15660     var Event = Roo.EventManager;
15661
15662     return {
15663
15664         /**
15665          * Two dimensional Array of registered DragDrop objects.  The first
15666          * dimension is the DragDrop item group, the second the DragDrop
15667          * object.
15668          * @property ids
15669          * @type {string: string}
15670          * @private
15671          * @static
15672          */
15673         ids: {},
15674
15675         /**
15676          * Array of element ids defined as drag handles.  Used to determine
15677          * if the element that generated the mousedown event is actually the
15678          * handle and not the html element itself.
15679          * @property handleIds
15680          * @type {string: string}
15681          * @private
15682          * @static
15683          */
15684         handleIds: {},
15685
15686         /**
15687          * the DragDrop object that is currently being dragged
15688          * @property dragCurrent
15689          * @type DragDrop
15690          * @private
15691          * @static
15692          **/
15693         dragCurrent: null,
15694
15695         /**
15696          * the DragDrop object(s) that are being hovered over
15697          * @property dragOvers
15698          * @type Array
15699          * @private
15700          * @static
15701          */
15702         dragOvers: {},
15703
15704         /**
15705          * the X distance between the cursor and the object being dragged
15706          * @property deltaX
15707          * @type int
15708          * @private
15709          * @static
15710          */
15711         deltaX: 0,
15712
15713         /**
15714          * the Y distance between the cursor and the object being dragged
15715          * @property deltaY
15716          * @type int
15717          * @private
15718          * @static
15719          */
15720         deltaY: 0,
15721
15722         /**
15723          * Flag to determine if we should prevent the default behavior of the
15724          * events we define. By default this is true, but this can be set to
15725          * false if you need the default behavior (not recommended)
15726          * @property preventDefault
15727          * @type boolean
15728          * @static
15729          */
15730         preventDefault: true,
15731
15732         /**
15733          * Flag to determine if we should stop the propagation of the events
15734          * we generate. This is true by default but you may want to set it to
15735          * false if the html element contains other features that require the
15736          * mouse click.
15737          * @property stopPropagation
15738          * @type boolean
15739          * @static
15740          */
15741         stopPropagation: true,
15742
15743         /**
15744          * Internal flag that is set to true when drag and drop has been
15745          * intialized
15746          * @property initialized
15747          * @private
15748          * @static
15749          */
15750         initalized: false,
15751
15752         /**
15753          * All drag and drop can be disabled.
15754          * @property locked
15755          * @private
15756          * @static
15757          */
15758         locked: false,
15759
15760         /**
15761          * Called the first time an element is registered.
15762          * @method init
15763          * @private
15764          * @static
15765          */
15766         init: function() {
15767             this.initialized = true;
15768         },
15769
15770         /**
15771          * In point mode, drag and drop interaction is defined by the
15772          * location of the cursor during the drag/drop
15773          * @property POINT
15774          * @type int
15775          * @static
15776          */
15777         POINT: 0,
15778
15779         /**
15780          * In intersect mode, drag and drop interactio nis defined by the
15781          * overlap of two or more drag and drop objects.
15782          * @property INTERSECT
15783          * @type int
15784          * @static
15785          */
15786         INTERSECT: 1,
15787
15788         /**
15789          * The current drag and drop mode.  Default: POINT
15790          * @property mode
15791          * @type int
15792          * @static
15793          */
15794         mode: 0,
15795
15796         /**
15797          * Runs method on all drag and drop objects
15798          * @method _execOnAll
15799          * @private
15800          * @static
15801          */
15802         _execOnAll: function(sMethod, args) {
15803             for (var i in this.ids) {
15804                 for (var j in this.ids[i]) {
15805                     var oDD = this.ids[i][j];
15806                     if (! this.isTypeOfDD(oDD)) {
15807                         continue;
15808                     }
15809                     oDD[sMethod].apply(oDD, args);
15810                 }
15811             }
15812         },
15813
15814         /**
15815          * Drag and drop initialization.  Sets up the global event handlers
15816          * @method _onLoad
15817          * @private
15818          * @static
15819          */
15820         _onLoad: function() {
15821
15822             this.init();
15823
15824
15825             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15826             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15827             Event.on(window,   "unload",    this._onUnload, this, true);
15828             Event.on(window,   "resize",    this._onResize, this, true);
15829             // Event.on(window,   "mouseout",    this._test);
15830
15831         },
15832
15833         /**
15834          * Reset constraints on all drag and drop objs
15835          * @method _onResize
15836          * @private
15837          * @static
15838          */
15839         _onResize: function(e) {
15840             this._execOnAll("resetConstraints", []);
15841         },
15842
15843         /**
15844          * Lock all drag and drop functionality
15845          * @method lock
15846          * @static
15847          */
15848         lock: function() { this.locked = true; },
15849
15850         /**
15851          * Unlock all drag and drop functionality
15852          * @method unlock
15853          * @static
15854          */
15855         unlock: function() { this.locked = false; },
15856
15857         /**
15858          * Is drag and drop locked?
15859          * @method isLocked
15860          * @return {boolean} True if drag and drop is locked, false otherwise.
15861          * @static
15862          */
15863         isLocked: function() { return this.locked; },
15864
15865         /**
15866          * Location cache that is set for all drag drop objects when a drag is
15867          * initiated, cleared when the drag is finished.
15868          * @property locationCache
15869          * @private
15870          * @static
15871          */
15872         locationCache: {},
15873
15874         /**
15875          * Set useCache to false if you want to force object the lookup of each
15876          * drag and drop linked element constantly during a drag.
15877          * @property useCache
15878          * @type boolean
15879          * @static
15880          */
15881         useCache: true,
15882
15883         /**
15884          * The number of pixels that the mouse needs to move after the
15885          * mousedown before the drag is initiated.  Default=3;
15886          * @property clickPixelThresh
15887          * @type int
15888          * @static
15889          */
15890         clickPixelThresh: 3,
15891
15892         /**
15893          * The number of milliseconds after the mousedown event to initiate the
15894          * drag if we don't get a mouseup event. Default=1000
15895          * @property clickTimeThresh
15896          * @type int
15897          * @static
15898          */
15899         clickTimeThresh: 350,
15900
15901         /**
15902          * Flag that indicates that either the drag pixel threshold or the
15903          * mousdown time threshold has been met
15904          * @property dragThreshMet
15905          * @type boolean
15906          * @private
15907          * @static
15908          */
15909         dragThreshMet: false,
15910
15911         /**
15912          * Timeout used for the click time threshold
15913          * @property clickTimeout
15914          * @type Object
15915          * @private
15916          * @static
15917          */
15918         clickTimeout: null,
15919
15920         /**
15921          * The X position of the mousedown event stored for later use when a
15922          * drag threshold is met.
15923          * @property startX
15924          * @type int
15925          * @private
15926          * @static
15927          */
15928         startX: 0,
15929
15930         /**
15931          * The Y position of the mousedown event stored for later use when a
15932          * drag threshold is met.
15933          * @property startY
15934          * @type int
15935          * @private
15936          * @static
15937          */
15938         startY: 0,
15939
15940         /**
15941          * Each DragDrop instance must be registered with the DragDropMgr.
15942          * This is executed in DragDrop.init()
15943          * @method regDragDrop
15944          * @param {DragDrop} oDD the DragDrop object to register
15945          * @param {String} sGroup the name of the group this element belongs to
15946          * @static
15947          */
15948         regDragDrop: function(oDD, sGroup) {
15949             if (!this.initialized) { this.init(); }
15950
15951             if (!this.ids[sGroup]) {
15952                 this.ids[sGroup] = {};
15953             }
15954             this.ids[sGroup][oDD.id] = oDD;
15955         },
15956
15957         /**
15958          * Removes the supplied dd instance from the supplied group. Executed
15959          * by DragDrop.removeFromGroup, so don't call this function directly.
15960          * @method removeDDFromGroup
15961          * @private
15962          * @static
15963          */
15964         removeDDFromGroup: function(oDD, sGroup) {
15965             if (!this.ids[sGroup]) {
15966                 this.ids[sGroup] = {};
15967             }
15968
15969             var obj = this.ids[sGroup];
15970             if (obj && obj[oDD.id]) {
15971                 delete obj[oDD.id];
15972             }
15973         },
15974
15975         /**
15976          * Unregisters a drag and drop item.  This is executed in
15977          * DragDrop.unreg, use that method instead of calling this directly.
15978          * @method _remove
15979          * @private
15980          * @static
15981          */
15982         _remove: function(oDD) {
15983             for (var g in oDD.groups) {
15984                 if (g && this.ids[g][oDD.id]) {
15985                     delete this.ids[g][oDD.id];
15986                 }
15987             }
15988             delete this.handleIds[oDD.id];
15989         },
15990
15991         /**
15992          * Each DragDrop handle element must be registered.  This is done
15993          * automatically when executing DragDrop.setHandleElId()
15994          * @method regHandle
15995          * @param {String} sDDId the DragDrop id this element is a handle for
15996          * @param {String} sHandleId the id of the element that is the drag
15997          * handle
15998          * @static
15999          */
16000         regHandle: function(sDDId, sHandleId) {
16001             if (!this.handleIds[sDDId]) {
16002                 this.handleIds[sDDId] = {};
16003             }
16004             this.handleIds[sDDId][sHandleId] = sHandleId;
16005         },
16006
16007         /**
16008          * Utility function to determine if a given element has been
16009          * registered as a drag drop item.
16010          * @method isDragDrop
16011          * @param {String} id the element id to check
16012          * @return {boolean} true if this element is a DragDrop item,
16013          * false otherwise
16014          * @static
16015          */
16016         isDragDrop: function(id) {
16017             return ( this.getDDById(id) ) ? true : false;
16018         },
16019
16020         /**
16021          * Returns the drag and drop instances that are in all groups the
16022          * passed in instance belongs to.
16023          * @method getRelated
16024          * @param {DragDrop} p_oDD the obj to get related data for
16025          * @param {boolean} bTargetsOnly if true, only return targetable objs
16026          * @return {DragDrop[]} the related instances
16027          * @static
16028          */
16029         getRelated: function(p_oDD, bTargetsOnly) {
16030             var oDDs = [];
16031             for (var i in p_oDD.groups) {
16032                 for (j in this.ids[i]) {
16033                     var dd = this.ids[i][j];
16034                     if (! this.isTypeOfDD(dd)) {
16035                         continue;
16036                     }
16037                     if (!bTargetsOnly || dd.isTarget) {
16038                         oDDs[oDDs.length] = dd;
16039                     }
16040                 }
16041             }
16042
16043             return oDDs;
16044         },
16045
16046         /**
16047          * Returns true if the specified dd target is a legal target for
16048          * the specifice drag obj
16049          * @method isLegalTarget
16050          * @param {DragDrop} the drag obj
16051          * @param {DragDrop} the target
16052          * @return {boolean} true if the target is a legal target for the
16053          * dd obj
16054          * @static
16055          */
16056         isLegalTarget: function (oDD, oTargetDD) {
16057             var targets = this.getRelated(oDD, true);
16058             for (var i=0, len=targets.length;i<len;++i) {
16059                 if (targets[i].id == oTargetDD.id) {
16060                     return true;
16061                 }
16062             }
16063
16064             return false;
16065         },
16066
16067         /**
16068          * My goal is to be able to transparently determine if an object is
16069          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16070          * returns "object", oDD.constructor.toString() always returns
16071          * "DragDrop" and not the name of the subclass.  So for now it just
16072          * evaluates a well-known variable in DragDrop.
16073          * @method isTypeOfDD
16074          * @param {Object} the object to evaluate
16075          * @return {boolean} true if typeof oDD = DragDrop
16076          * @static
16077          */
16078         isTypeOfDD: function (oDD) {
16079             return (oDD && oDD.__ygDragDrop);
16080         },
16081
16082         /**
16083          * Utility function to determine if a given element has been
16084          * registered as a drag drop handle for the given Drag Drop object.
16085          * @method isHandle
16086          * @param {String} id the element id to check
16087          * @return {boolean} true if this element is a DragDrop handle, false
16088          * otherwise
16089          * @static
16090          */
16091         isHandle: function(sDDId, sHandleId) {
16092             return ( this.handleIds[sDDId] &&
16093                             this.handleIds[sDDId][sHandleId] );
16094         },
16095
16096         /**
16097          * Returns the DragDrop instance for a given id
16098          * @method getDDById
16099          * @param {String} id the id of the DragDrop object
16100          * @return {DragDrop} the drag drop object, null if it is not found
16101          * @static
16102          */
16103         getDDById: function(id) {
16104             for (var i in this.ids) {
16105                 if (this.ids[i][id]) {
16106                     return this.ids[i][id];
16107                 }
16108             }
16109             return null;
16110         },
16111
16112         /**
16113          * Fired after a registered DragDrop object gets the mousedown event.
16114          * Sets up the events required to track the object being dragged
16115          * @method handleMouseDown
16116          * @param {Event} e the event
16117          * @param oDD the DragDrop object being dragged
16118          * @private
16119          * @static
16120          */
16121         handleMouseDown: function(e, oDD) {
16122             if(Roo.QuickTips){
16123                 Roo.QuickTips.disable();
16124             }
16125             this.currentTarget = e.getTarget();
16126
16127             this.dragCurrent = oDD;
16128
16129             var el = oDD.getEl();
16130
16131             // track start position
16132             this.startX = e.getPageX();
16133             this.startY = e.getPageY();
16134
16135             this.deltaX = this.startX - el.offsetLeft;
16136             this.deltaY = this.startY - el.offsetTop;
16137
16138             this.dragThreshMet = false;
16139
16140             this.clickTimeout = setTimeout(
16141                     function() {
16142                         var DDM = Roo.dd.DDM;
16143                         DDM.startDrag(DDM.startX, DDM.startY);
16144                     },
16145                     this.clickTimeThresh );
16146         },
16147
16148         /**
16149          * Fired when either the drag pixel threshol or the mousedown hold
16150          * time threshold has been met.
16151          * @method startDrag
16152          * @param x {int} the X position of the original mousedown
16153          * @param y {int} the Y position of the original mousedown
16154          * @static
16155          */
16156         startDrag: function(x, y) {
16157             clearTimeout(this.clickTimeout);
16158             if (this.dragCurrent) {
16159                 this.dragCurrent.b4StartDrag(x, y);
16160                 this.dragCurrent.startDrag(x, y);
16161             }
16162             this.dragThreshMet = true;
16163         },
16164
16165         /**
16166          * Internal function to handle the mouseup event.  Will be invoked
16167          * from the context of the document.
16168          * @method handleMouseUp
16169          * @param {Event} e the event
16170          * @private
16171          * @static
16172          */
16173         handleMouseUp: function(e) {
16174
16175             if(Roo.QuickTips){
16176                 Roo.QuickTips.enable();
16177             }
16178             if (! this.dragCurrent) {
16179                 return;
16180             }
16181
16182             clearTimeout(this.clickTimeout);
16183
16184             if (this.dragThreshMet) {
16185                 this.fireEvents(e, true);
16186             } else {
16187             }
16188
16189             this.stopDrag(e);
16190
16191             this.stopEvent(e);
16192         },
16193
16194         /**
16195          * Utility to stop event propagation and event default, if these
16196          * features are turned on.
16197          * @method stopEvent
16198          * @param {Event} e the event as returned by this.getEvent()
16199          * @static
16200          */
16201         stopEvent: function(e){
16202             if(this.stopPropagation) {
16203                 e.stopPropagation();
16204             }
16205
16206             if (this.preventDefault) {
16207                 e.preventDefault();
16208             }
16209         },
16210
16211         /**
16212          * Internal function to clean up event handlers after the drag
16213          * operation is complete
16214          * @method stopDrag
16215          * @param {Event} e the event
16216          * @private
16217          * @static
16218          */
16219         stopDrag: function(e) {
16220             // Fire the drag end event for the item that was dragged
16221             if (this.dragCurrent) {
16222                 if (this.dragThreshMet) {
16223                     this.dragCurrent.b4EndDrag(e);
16224                     this.dragCurrent.endDrag(e);
16225                 }
16226
16227                 this.dragCurrent.onMouseUp(e);
16228             }
16229
16230             this.dragCurrent = null;
16231             this.dragOvers = {};
16232         },
16233
16234         /**
16235          * Internal function to handle the mousemove event.  Will be invoked
16236          * from the context of the html element.
16237          *
16238          * @TODO figure out what we can do about mouse events lost when the
16239          * user drags objects beyond the window boundary.  Currently we can
16240          * detect this in internet explorer by verifying that the mouse is
16241          * down during the mousemove event.  Firefox doesn't give us the
16242          * button state on the mousemove event.
16243          * @method handleMouseMove
16244          * @param {Event} e the event
16245          * @private
16246          * @static
16247          */
16248         handleMouseMove: function(e) {
16249             if (! this.dragCurrent) {
16250                 return true;
16251             }
16252
16253             // var button = e.which || e.button;
16254
16255             // check for IE mouseup outside of page boundary
16256             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16257                 this.stopEvent(e);
16258                 return this.handleMouseUp(e);
16259             }
16260
16261             if (!this.dragThreshMet) {
16262                 var diffX = Math.abs(this.startX - e.getPageX());
16263                 var diffY = Math.abs(this.startY - e.getPageY());
16264                 if (diffX > this.clickPixelThresh ||
16265                             diffY > this.clickPixelThresh) {
16266                     this.startDrag(this.startX, this.startY);
16267                 }
16268             }
16269
16270             if (this.dragThreshMet) {
16271                 this.dragCurrent.b4Drag(e);
16272                 this.dragCurrent.onDrag(e);
16273                 if(!this.dragCurrent.moveOnly){
16274                     this.fireEvents(e, false);
16275                 }
16276             }
16277
16278             this.stopEvent(e);
16279
16280             return true;
16281         },
16282
16283         /**
16284          * Iterates over all of the DragDrop elements to find ones we are
16285          * hovering over or dropping on
16286          * @method fireEvents
16287          * @param {Event} e the event
16288          * @param {boolean} isDrop is this a drop op or a mouseover op?
16289          * @private
16290          * @static
16291          */
16292         fireEvents: function(e, isDrop) {
16293             var dc = this.dragCurrent;
16294
16295             // If the user did the mouse up outside of the window, we could
16296             // get here even though we have ended the drag.
16297             if (!dc || dc.isLocked()) {
16298                 return;
16299             }
16300
16301             var pt = e.getPoint();
16302
16303             // cache the previous dragOver array
16304             var oldOvers = [];
16305
16306             var outEvts   = [];
16307             var overEvts  = [];
16308             var dropEvts  = [];
16309             var enterEvts = [];
16310
16311             // Check to see if the object(s) we were hovering over is no longer
16312             // being hovered over so we can fire the onDragOut event
16313             for (var i in this.dragOvers) {
16314
16315                 var ddo = this.dragOvers[i];
16316
16317                 if (! this.isTypeOfDD(ddo)) {
16318                     continue;
16319                 }
16320
16321                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16322                     outEvts.push( ddo );
16323                 }
16324
16325                 oldOvers[i] = true;
16326                 delete this.dragOvers[i];
16327             }
16328
16329             for (var sGroup in dc.groups) {
16330
16331                 if ("string" != typeof sGroup) {
16332                     continue;
16333                 }
16334
16335                 for (i in this.ids[sGroup]) {
16336                     var oDD = this.ids[sGroup][i];
16337                     if (! this.isTypeOfDD(oDD)) {
16338                         continue;
16339                     }
16340
16341                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16342                         if (this.isOverTarget(pt, oDD, this.mode)) {
16343                             // look for drop interactions
16344                             if (isDrop) {
16345                                 dropEvts.push( oDD );
16346                             // look for drag enter and drag over interactions
16347                             } else {
16348
16349                                 // initial drag over: dragEnter fires
16350                                 if (!oldOvers[oDD.id]) {
16351                                     enterEvts.push( oDD );
16352                                 // subsequent drag overs: dragOver fires
16353                                 } else {
16354                                     overEvts.push( oDD );
16355                                 }
16356
16357                                 this.dragOvers[oDD.id] = oDD;
16358                             }
16359                         }
16360                     }
16361                 }
16362             }
16363
16364             if (this.mode) {
16365                 if (outEvts.length) {
16366                     dc.b4DragOut(e, outEvts);
16367                     dc.onDragOut(e, outEvts);
16368                 }
16369
16370                 if (enterEvts.length) {
16371                     dc.onDragEnter(e, enterEvts);
16372                 }
16373
16374                 if (overEvts.length) {
16375                     dc.b4DragOver(e, overEvts);
16376                     dc.onDragOver(e, overEvts);
16377                 }
16378
16379                 if (dropEvts.length) {
16380                     dc.b4DragDrop(e, dropEvts);
16381                     dc.onDragDrop(e, dropEvts);
16382                 }
16383
16384             } else {
16385                 // fire dragout events
16386                 var len = 0;
16387                 for (i=0, len=outEvts.length; i<len; ++i) {
16388                     dc.b4DragOut(e, outEvts[i].id);
16389                     dc.onDragOut(e, outEvts[i].id);
16390                 }
16391
16392                 // fire enter events
16393                 for (i=0,len=enterEvts.length; i<len; ++i) {
16394                     // dc.b4DragEnter(e, oDD.id);
16395                     dc.onDragEnter(e, enterEvts[i].id);
16396                 }
16397
16398                 // fire over events
16399                 for (i=0,len=overEvts.length; i<len; ++i) {
16400                     dc.b4DragOver(e, overEvts[i].id);
16401                     dc.onDragOver(e, overEvts[i].id);
16402                 }
16403
16404                 // fire drop events
16405                 for (i=0, len=dropEvts.length; i<len; ++i) {
16406                     dc.b4DragDrop(e, dropEvts[i].id);
16407                     dc.onDragDrop(e, dropEvts[i].id);
16408                 }
16409
16410             }
16411
16412             // notify about a drop that did not find a target
16413             if (isDrop && !dropEvts.length) {
16414                 dc.onInvalidDrop(e);
16415             }
16416
16417         },
16418
16419         /**
16420          * Helper function for getting the best match from the list of drag
16421          * and drop objects returned by the drag and drop events when we are
16422          * in INTERSECT mode.  It returns either the first object that the
16423          * cursor is over, or the object that has the greatest overlap with
16424          * the dragged element.
16425          * @method getBestMatch
16426          * @param  {DragDrop[]} dds The array of drag and drop objects
16427          * targeted
16428          * @return {DragDrop}       The best single match
16429          * @static
16430          */
16431         getBestMatch: function(dds) {
16432             var winner = null;
16433             // Return null if the input is not what we expect
16434             //if (!dds || !dds.length || dds.length == 0) {
16435                // winner = null;
16436             // If there is only one item, it wins
16437             //} else if (dds.length == 1) {
16438
16439             var len = dds.length;
16440
16441             if (len == 1) {
16442                 winner = dds[0];
16443             } else {
16444                 // Loop through the targeted items
16445                 for (var i=0; i<len; ++i) {
16446                     var dd = dds[i];
16447                     // If the cursor is over the object, it wins.  If the
16448                     // cursor is over multiple matches, the first one we come
16449                     // to wins.
16450                     if (dd.cursorIsOver) {
16451                         winner = dd;
16452                         break;
16453                     // Otherwise the object with the most overlap wins
16454                     } else {
16455                         if (!winner ||
16456                             winner.overlap.getArea() < dd.overlap.getArea()) {
16457                             winner = dd;
16458                         }
16459                     }
16460                 }
16461             }
16462
16463             return winner;
16464         },
16465
16466         /**
16467          * Refreshes the cache of the top-left and bottom-right points of the
16468          * drag and drop objects in the specified group(s).  This is in the
16469          * format that is stored in the drag and drop instance, so typical
16470          * usage is:
16471          * <code>
16472          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16473          * </code>
16474          * Alternatively:
16475          * <code>
16476          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16477          * </code>
16478          * @TODO this really should be an indexed array.  Alternatively this
16479          * method could accept both.
16480          * @method refreshCache
16481          * @param {Object} groups an associative array of groups to refresh
16482          * @static
16483          */
16484         refreshCache: function(groups) {
16485             for (var sGroup in groups) {
16486                 if ("string" != typeof sGroup) {
16487                     continue;
16488                 }
16489                 for (var i in this.ids[sGroup]) {
16490                     var oDD = this.ids[sGroup][i];
16491
16492                     if (this.isTypeOfDD(oDD)) {
16493                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16494                         var loc = this.getLocation(oDD);
16495                         if (loc) {
16496                             this.locationCache[oDD.id] = loc;
16497                         } else {
16498                             delete this.locationCache[oDD.id];
16499                             // this will unregister the drag and drop object if
16500                             // the element is not in a usable state
16501                             // oDD.unreg();
16502                         }
16503                     }
16504                 }
16505             }
16506         },
16507
16508         /**
16509          * This checks to make sure an element exists and is in the DOM.  The
16510          * main purpose is to handle cases where innerHTML is used to remove
16511          * drag and drop objects from the DOM.  IE provides an 'unspecified
16512          * error' when trying to access the offsetParent of such an element
16513          * @method verifyEl
16514          * @param {HTMLElement} el the element to check
16515          * @return {boolean} true if the element looks usable
16516          * @static
16517          */
16518         verifyEl: function(el) {
16519             if (el) {
16520                 var parent;
16521                 if(Roo.isIE){
16522                     try{
16523                         parent = el.offsetParent;
16524                     }catch(e){}
16525                 }else{
16526                     parent = el.offsetParent;
16527                 }
16528                 if (parent) {
16529                     return true;
16530                 }
16531             }
16532
16533             return false;
16534         },
16535
16536         /**
16537          * Returns a Region object containing the drag and drop element's position
16538          * and size, including the padding configured for it
16539          * @method getLocation
16540          * @param {DragDrop} oDD the drag and drop object to get the
16541          *                       location for
16542          * @return {Roo.lib.Region} a Region object representing the total area
16543          *                             the element occupies, including any padding
16544          *                             the instance is configured for.
16545          * @static
16546          */
16547         getLocation: function(oDD) {
16548             if (! this.isTypeOfDD(oDD)) {
16549                 return null;
16550             }
16551
16552             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16553
16554             try {
16555                 pos= Roo.lib.Dom.getXY(el);
16556             } catch (e) { }
16557
16558             if (!pos) {
16559                 return null;
16560             }
16561
16562             x1 = pos[0];
16563             x2 = x1 + el.offsetWidth;
16564             y1 = pos[1];
16565             y2 = y1 + el.offsetHeight;
16566
16567             t = y1 - oDD.padding[0];
16568             r = x2 + oDD.padding[1];
16569             b = y2 + oDD.padding[2];
16570             l = x1 - oDD.padding[3];
16571
16572             return new Roo.lib.Region( t, r, b, l );
16573         },
16574
16575         /**
16576          * Checks the cursor location to see if it over the target
16577          * @method isOverTarget
16578          * @param {Roo.lib.Point} pt The point to evaluate
16579          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16580          * @return {boolean} true if the mouse is over the target
16581          * @private
16582          * @static
16583          */
16584         isOverTarget: function(pt, oTarget, intersect) {
16585             // use cache if available
16586             var loc = this.locationCache[oTarget.id];
16587             if (!loc || !this.useCache) {
16588                 loc = this.getLocation(oTarget);
16589                 this.locationCache[oTarget.id] = loc;
16590
16591             }
16592
16593             if (!loc) {
16594                 return false;
16595             }
16596
16597             oTarget.cursorIsOver = loc.contains( pt );
16598
16599             // DragDrop is using this as a sanity check for the initial mousedown
16600             // in this case we are done.  In POINT mode, if the drag obj has no
16601             // contraints, we are also done. Otherwise we need to evaluate the
16602             // location of the target as related to the actual location of the
16603             // dragged element.
16604             var dc = this.dragCurrent;
16605             if (!dc || !dc.getTargetCoord ||
16606                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16607                 return oTarget.cursorIsOver;
16608             }
16609
16610             oTarget.overlap = null;
16611
16612             // Get the current location of the drag element, this is the
16613             // location of the mouse event less the delta that represents
16614             // where the original mousedown happened on the element.  We
16615             // need to consider constraints and ticks as well.
16616             var pos = dc.getTargetCoord(pt.x, pt.y);
16617
16618             var el = dc.getDragEl();
16619             var curRegion = new Roo.lib.Region( pos.y,
16620                                                    pos.x + el.offsetWidth,
16621                                                    pos.y + el.offsetHeight,
16622                                                    pos.x );
16623
16624             var overlap = curRegion.intersect(loc);
16625
16626             if (overlap) {
16627                 oTarget.overlap = overlap;
16628                 return (intersect) ? true : oTarget.cursorIsOver;
16629             } else {
16630                 return false;
16631             }
16632         },
16633
16634         /**
16635          * unload event handler
16636          * @method _onUnload
16637          * @private
16638          * @static
16639          */
16640         _onUnload: function(e, me) {
16641             Roo.dd.DragDropMgr.unregAll();
16642         },
16643
16644         /**
16645          * Cleans up the drag and drop events and objects.
16646          * @method unregAll
16647          * @private
16648          * @static
16649          */
16650         unregAll: function() {
16651
16652             if (this.dragCurrent) {
16653                 this.stopDrag();
16654                 this.dragCurrent = null;
16655             }
16656
16657             this._execOnAll("unreg", []);
16658
16659             for (i in this.elementCache) {
16660                 delete this.elementCache[i];
16661             }
16662
16663             this.elementCache = {};
16664             this.ids = {};
16665         },
16666
16667         /**
16668          * A cache of DOM elements
16669          * @property elementCache
16670          * @private
16671          * @static
16672          */
16673         elementCache: {},
16674
16675         /**
16676          * Get the wrapper for the DOM element specified
16677          * @method getElWrapper
16678          * @param {String} id the id of the element to get
16679          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16680          * @private
16681          * @deprecated This wrapper isn't that useful
16682          * @static
16683          */
16684         getElWrapper: function(id) {
16685             var oWrapper = this.elementCache[id];
16686             if (!oWrapper || !oWrapper.el) {
16687                 oWrapper = this.elementCache[id] =
16688                     new this.ElementWrapper(Roo.getDom(id));
16689             }
16690             return oWrapper;
16691         },
16692
16693         /**
16694          * Returns the actual DOM element
16695          * @method getElement
16696          * @param {String} id the id of the elment to get
16697          * @return {Object} The element
16698          * @deprecated use Roo.getDom instead
16699          * @static
16700          */
16701         getElement: function(id) {
16702             return Roo.getDom(id);
16703         },
16704
16705         /**
16706          * Returns the style property for the DOM element (i.e.,
16707          * document.getElById(id).style)
16708          * @method getCss
16709          * @param {String} id the id of the elment to get
16710          * @return {Object} The style property of the element
16711          * @deprecated use Roo.getDom instead
16712          * @static
16713          */
16714         getCss: function(id) {
16715             var el = Roo.getDom(id);
16716             return (el) ? el.style : null;
16717         },
16718
16719         /**
16720          * Inner class for cached elements
16721          * @class DragDropMgr.ElementWrapper
16722          * @for DragDropMgr
16723          * @private
16724          * @deprecated
16725          */
16726         ElementWrapper: function(el) {
16727                 /**
16728                  * The element
16729                  * @property el
16730                  */
16731                 this.el = el || null;
16732                 /**
16733                  * The element id
16734                  * @property id
16735                  */
16736                 this.id = this.el && el.id;
16737                 /**
16738                  * A reference to the style property
16739                  * @property css
16740                  */
16741                 this.css = this.el && el.style;
16742             },
16743
16744         /**
16745          * Returns the X position of an html element
16746          * @method getPosX
16747          * @param el the element for which to get the position
16748          * @return {int} the X coordinate
16749          * @for DragDropMgr
16750          * @deprecated use Roo.lib.Dom.getX instead
16751          * @static
16752          */
16753         getPosX: function(el) {
16754             return Roo.lib.Dom.getX(el);
16755         },
16756
16757         /**
16758          * Returns the Y position of an html element
16759          * @method getPosY
16760          * @param el the element for which to get the position
16761          * @return {int} the Y coordinate
16762          * @deprecated use Roo.lib.Dom.getY instead
16763          * @static
16764          */
16765         getPosY: function(el) {
16766             return Roo.lib.Dom.getY(el);
16767         },
16768
16769         /**
16770          * Swap two nodes.  In IE, we use the native method, for others we
16771          * emulate the IE behavior
16772          * @method swapNode
16773          * @param n1 the first node to swap
16774          * @param n2 the other node to swap
16775          * @static
16776          */
16777         swapNode: function(n1, n2) {
16778             if (n1.swapNode) {
16779                 n1.swapNode(n2);
16780             } else {
16781                 var p = n2.parentNode;
16782                 var s = n2.nextSibling;
16783
16784                 if (s == n1) {
16785                     p.insertBefore(n1, n2);
16786                 } else if (n2 == n1.nextSibling) {
16787                     p.insertBefore(n2, n1);
16788                 } else {
16789                     n1.parentNode.replaceChild(n2, n1);
16790                     p.insertBefore(n1, s);
16791                 }
16792             }
16793         },
16794
16795         /**
16796          * Returns the current scroll position
16797          * @method getScroll
16798          * @private
16799          * @static
16800          */
16801         getScroll: function () {
16802             var t, l, dde=document.documentElement, db=document.body;
16803             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16804                 t = dde.scrollTop;
16805                 l = dde.scrollLeft;
16806             } else if (db) {
16807                 t = db.scrollTop;
16808                 l = db.scrollLeft;
16809             } else {
16810
16811             }
16812             return { top: t, left: l };
16813         },
16814
16815         /**
16816          * Returns the specified element style property
16817          * @method getStyle
16818          * @param {HTMLElement} el          the element
16819          * @param {string}      styleProp   the style property
16820          * @return {string} The value of the style property
16821          * @deprecated use Roo.lib.Dom.getStyle
16822          * @static
16823          */
16824         getStyle: function(el, styleProp) {
16825             return Roo.fly(el).getStyle(styleProp);
16826         },
16827
16828         /**
16829          * Gets the scrollTop
16830          * @method getScrollTop
16831          * @return {int} the document's scrollTop
16832          * @static
16833          */
16834         getScrollTop: function () { return this.getScroll().top; },
16835
16836         /**
16837          * Gets the scrollLeft
16838          * @method getScrollLeft
16839          * @return {int} the document's scrollTop
16840          * @static
16841          */
16842         getScrollLeft: function () { return this.getScroll().left; },
16843
16844         /**
16845          * Sets the x/y position of an element to the location of the
16846          * target element.
16847          * @method moveToEl
16848          * @param {HTMLElement} moveEl      The element to move
16849          * @param {HTMLElement} targetEl    The position reference element
16850          * @static
16851          */
16852         moveToEl: function (moveEl, targetEl) {
16853             var aCoord = Roo.lib.Dom.getXY(targetEl);
16854             Roo.lib.Dom.setXY(moveEl, aCoord);
16855         },
16856
16857         /**
16858          * Numeric array sort function
16859          * @method numericSort
16860          * @static
16861          */
16862         numericSort: function(a, b) { return (a - b); },
16863
16864         /**
16865          * Internal counter
16866          * @property _timeoutCount
16867          * @private
16868          * @static
16869          */
16870         _timeoutCount: 0,
16871
16872         /**
16873          * Trying to make the load order less important.  Without this we get
16874          * an error if this file is loaded before the Event Utility.
16875          * @method _addListeners
16876          * @private
16877          * @static
16878          */
16879         _addListeners: function() {
16880             var DDM = Roo.dd.DDM;
16881             if ( Roo.lib.Event && document ) {
16882                 DDM._onLoad();
16883             } else {
16884                 if (DDM._timeoutCount > 2000) {
16885                 } else {
16886                     setTimeout(DDM._addListeners, 10);
16887                     if (document && document.body) {
16888                         DDM._timeoutCount += 1;
16889                     }
16890                 }
16891             }
16892         },
16893
16894         /**
16895          * Recursively searches the immediate parent and all child nodes for
16896          * the handle element in order to determine wheter or not it was
16897          * clicked.
16898          * @method handleWasClicked
16899          * @param node the html element to inspect
16900          * @static
16901          */
16902         handleWasClicked: function(node, id) {
16903             if (this.isHandle(id, node.id)) {
16904                 return true;
16905             } else {
16906                 // check to see if this is a text node child of the one we want
16907                 var p = node.parentNode;
16908
16909                 while (p) {
16910                     if (this.isHandle(id, p.id)) {
16911                         return true;
16912                     } else {
16913                         p = p.parentNode;
16914                     }
16915                 }
16916             }
16917
16918             return false;
16919         }
16920
16921     };
16922
16923 }();
16924
16925 // shorter alias, save a few bytes
16926 Roo.dd.DDM = Roo.dd.DragDropMgr;
16927 Roo.dd.DDM._addListeners();
16928
16929 }/*
16930  * Based on:
16931  * Ext JS Library 1.1.1
16932  * Copyright(c) 2006-2007, Ext JS, LLC.
16933  *
16934  * Originally Released Under LGPL - original licence link has changed is not relivant.
16935  *
16936  * Fork - LGPL
16937  * <script type="text/javascript">
16938  */
16939
16940 /**
16941  * @class Roo.dd.DD
16942  * A DragDrop implementation where the linked element follows the
16943  * mouse cursor during a drag.
16944  * @extends Roo.dd.DragDrop
16945  * @constructor
16946  * @param {String} id the id of the linked element
16947  * @param {String} sGroup the group of related DragDrop items
16948  * @param {object} config an object containing configurable attributes
16949  *                Valid properties for DD:
16950  *                    scroll
16951  */
16952 Roo.dd.DD = function(id, sGroup, config) {
16953     if (id) {
16954         this.init(id, sGroup, config);
16955     }
16956 };
16957
16958 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16959
16960     /**
16961      * When set to true, the utility automatically tries to scroll the browser
16962      * window wehn a drag and drop element is dragged near the viewport boundary.
16963      * Defaults to true.
16964      * @property scroll
16965      * @type boolean
16966      */
16967     scroll: true,
16968
16969     /**
16970      * Sets the pointer offset to the distance between the linked element's top
16971      * left corner and the location the element was clicked
16972      * @method autoOffset
16973      * @param {int} iPageX the X coordinate of the click
16974      * @param {int} iPageY the Y coordinate of the click
16975      */
16976     autoOffset: function(iPageX, iPageY) {
16977         var x = iPageX - this.startPageX;
16978         var y = iPageY - this.startPageY;
16979         this.setDelta(x, y);
16980     },
16981
16982     /**
16983      * Sets the pointer offset.  You can call this directly to force the
16984      * offset to be in a particular location (e.g., pass in 0,0 to set it
16985      * to the center of the object)
16986      * @method setDelta
16987      * @param {int} iDeltaX the distance from the left
16988      * @param {int} iDeltaY the distance from the top
16989      */
16990     setDelta: function(iDeltaX, iDeltaY) {
16991         this.deltaX = iDeltaX;
16992         this.deltaY = iDeltaY;
16993     },
16994
16995     /**
16996      * Sets the drag element to the location of the mousedown or click event,
16997      * maintaining the cursor location relative to the location on the element
16998      * that was clicked.  Override this if you want to place the element in a
16999      * location other than where the cursor is.
17000      * @method setDragElPos
17001      * @param {int} iPageX the X coordinate of the mousedown or drag event
17002      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17003      */
17004     setDragElPos: function(iPageX, iPageY) {
17005         // the first time we do this, we are going to check to make sure
17006         // the element has css positioning
17007
17008         var el = this.getDragEl();
17009         this.alignElWithMouse(el, iPageX, iPageY);
17010     },
17011
17012     /**
17013      * Sets the element to the location of the mousedown or click event,
17014      * maintaining the cursor location relative to the location on the element
17015      * that was clicked.  Override this if you want to place the element in a
17016      * location other than where the cursor is.
17017      * @method alignElWithMouse
17018      * @param {HTMLElement} el the element to move
17019      * @param {int} iPageX the X coordinate of the mousedown or drag event
17020      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17021      */
17022     alignElWithMouse: function(el, iPageX, iPageY) {
17023         var oCoord = this.getTargetCoord(iPageX, iPageY);
17024         var fly = el.dom ? el : Roo.fly(el);
17025         if (!this.deltaSetXY) {
17026             var aCoord = [oCoord.x, oCoord.y];
17027             fly.setXY(aCoord);
17028             var newLeft = fly.getLeft(true);
17029             var newTop  = fly.getTop(true);
17030             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17031         } else {
17032             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17033         }
17034
17035         this.cachePosition(oCoord.x, oCoord.y);
17036         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17037         return oCoord;
17038     },
17039
17040     /**
17041      * Saves the most recent position so that we can reset the constraints and
17042      * tick marks on-demand.  We need to know this so that we can calculate the
17043      * number of pixels the element is offset from its original position.
17044      * @method cachePosition
17045      * @param iPageX the current x position (optional, this just makes it so we
17046      * don't have to look it up again)
17047      * @param iPageY the current y position (optional, this just makes it so we
17048      * don't have to look it up again)
17049      */
17050     cachePosition: function(iPageX, iPageY) {
17051         if (iPageX) {
17052             this.lastPageX = iPageX;
17053             this.lastPageY = iPageY;
17054         } else {
17055             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17056             this.lastPageX = aCoord[0];
17057             this.lastPageY = aCoord[1];
17058         }
17059     },
17060
17061     /**
17062      * Auto-scroll the window if the dragged object has been moved beyond the
17063      * visible window boundary.
17064      * @method autoScroll
17065      * @param {int} x the drag element's x position
17066      * @param {int} y the drag element's y position
17067      * @param {int} h the height of the drag element
17068      * @param {int} w the width of the drag element
17069      * @private
17070      */
17071     autoScroll: function(x, y, h, w) {
17072
17073         if (this.scroll) {
17074             // The client height
17075             var clientH = Roo.lib.Dom.getViewWidth();
17076
17077             // The client width
17078             var clientW = Roo.lib.Dom.getViewHeight();
17079
17080             // The amt scrolled down
17081             var st = this.DDM.getScrollTop();
17082
17083             // The amt scrolled right
17084             var sl = this.DDM.getScrollLeft();
17085
17086             // Location of the bottom of the element
17087             var bot = h + y;
17088
17089             // Location of the right of the element
17090             var right = w + x;
17091
17092             // The distance from the cursor to the bottom of the visible area,
17093             // adjusted so that we don't scroll if the cursor is beyond the
17094             // element drag constraints
17095             var toBot = (clientH + st - y - this.deltaY);
17096
17097             // The distance from the cursor to the right of the visible area
17098             var toRight = (clientW + sl - x - this.deltaX);
17099
17100
17101             // How close to the edge the cursor must be before we scroll
17102             // var thresh = (document.all) ? 100 : 40;
17103             var thresh = 40;
17104
17105             // How many pixels to scroll per autoscroll op.  This helps to reduce
17106             // clunky scrolling. IE is more sensitive about this ... it needs this
17107             // value to be higher.
17108             var scrAmt = (document.all) ? 80 : 30;
17109
17110             // Scroll down if we are near the bottom of the visible page and the
17111             // obj extends below the crease
17112             if ( bot > clientH && toBot < thresh ) {
17113                 window.scrollTo(sl, st + scrAmt);
17114             }
17115
17116             // Scroll up if the window is scrolled down and the top of the object
17117             // goes above the top border
17118             if ( y < st && st > 0 && y - st < thresh ) {
17119                 window.scrollTo(sl, st - scrAmt);
17120             }
17121
17122             // Scroll right if the obj is beyond the right border and the cursor is
17123             // near the border.
17124             if ( right > clientW && toRight < thresh ) {
17125                 window.scrollTo(sl + scrAmt, st);
17126             }
17127
17128             // Scroll left if the window has been scrolled to the right and the obj
17129             // extends past the left border
17130             if ( x < sl && sl > 0 && x - sl < thresh ) {
17131                 window.scrollTo(sl - scrAmt, st);
17132             }
17133         }
17134     },
17135
17136     /**
17137      * Finds the location the element should be placed if we want to move
17138      * it to where the mouse location less the click offset would place us.
17139      * @method getTargetCoord
17140      * @param {int} iPageX the X coordinate of the click
17141      * @param {int} iPageY the Y coordinate of the click
17142      * @return an object that contains the coordinates (Object.x and Object.y)
17143      * @private
17144      */
17145     getTargetCoord: function(iPageX, iPageY) {
17146
17147
17148         var x = iPageX - this.deltaX;
17149         var y = iPageY - this.deltaY;
17150
17151         if (this.constrainX) {
17152             if (x < this.minX) { x = this.minX; }
17153             if (x > this.maxX) { x = this.maxX; }
17154         }
17155
17156         if (this.constrainY) {
17157             if (y < this.minY) { y = this.minY; }
17158             if (y > this.maxY) { y = this.maxY; }
17159         }
17160
17161         x = this.getTick(x, this.xTicks);
17162         y = this.getTick(y, this.yTicks);
17163
17164
17165         return {x:x, y:y};
17166     },
17167
17168     /*
17169      * Sets up config options specific to this class. Overrides
17170      * Roo.dd.DragDrop, but all versions of this method through the
17171      * inheritance chain are called
17172      */
17173     applyConfig: function() {
17174         Roo.dd.DD.superclass.applyConfig.call(this);
17175         this.scroll = (this.config.scroll !== false);
17176     },
17177
17178     /*
17179      * Event that fires prior to the onMouseDown event.  Overrides
17180      * Roo.dd.DragDrop.
17181      */
17182     b4MouseDown: function(e) {
17183         // this.resetConstraints();
17184         this.autoOffset(e.getPageX(),
17185                             e.getPageY());
17186     },
17187
17188     /*
17189      * Event that fires prior to the onDrag event.  Overrides
17190      * Roo.dd.DragDrop.
17191      */
17192     b4Drag: function(e) {
17193         this.setDragElPos(e.getPageX(),
17194                             e.getPageY());
17195     },
17196
17197     toString: function() {
17198         return ("DD " + this.id);
17199     }
17200
17201     //////////////////////////////////////////////////////////////////////////
17202     // Debugging ygDragDrop events that can be overridden
17203     //////////////////////////////////////////////////////////////////////////
17204     /*
17205     startDrag: function(x, y) {
17206     },
17207
17208     onDrag: function(e) {
17209     },
17210
17211     onDragEnter: function(e, id) {
17212     },
17213
17214     onDragOver: function(e, id) {
17215     },
17216
17217     onDragOut: function(e, id) {
17218     },
17219
17220     onDragDrop: function(e, id) {
17221     },
17222
17223     endDrag: function(e) {
17224     }
17225
17226     */
17227
17228 });/*
17229  * Based on:
17230  * Ext JS Library 1.1.1
17231  * Copyright(c) 2006-2007, Ext JS, LLC.
17232  *
17233  * Originally Released Under LGPL - original licence link has changed is not relivant.
17234  *
17235  * Fork - LGPL
17236  * <script type="text/javascript">
17237  */
17238
17239 /**
17240  * @class Roo.dd.DDProxy
17241  * A DragDrop implementation that inserts an empty, bordered div into
17242  * the document that follows the cursor during drag operations.  At the time of
17243  * the click, the frame div is resized to the dimensions of the linked html
17244  * element, and moved to the exact location of the linked element.
17245  *
17246  * References to the "frame" element refer to the single proxy element that
17247  * was created to be dragged in place of all DDProxy elements on the
17248  * page.
17249  *
17250  * @extends Roo.dd.DD
17251  * @constructor
17252  * @param {String} id the id of the linked html element
17253  * @param {String} sGroup the group of related DragDrop objects
17254  * @param {object} config an object containing configurable attributes
17255  *                Valid properties for DDProxy in addition to those in DragDrop:
17256  *                   resizeFrame, centerFrame, dragElId
17257  */
17258 Roo.dd.DDProxy = function(id, sGroup, config) {
17259     if (id) {
17260         this.init(id, sGroup, config);
17261         this.initFrame();
17262     }
17263 };
17264
17265 /**
17266  * The default drag frame div id
17267  * @property Roo.dd.DDProxy.dragElId
17268  * @type String
17269  * @static
17270  */
17271 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17272
17273 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17274
17275     /**
17276      * By default we resize the drag frame to be the same size as the element
17277      * we want to drag (this is to get the frame effect).  We can turn it off
17278      * if we want a different behavior.
17279      * @property resizeFrame
17280      * @type boolean
17281      */
17282     resizeFrame: true,
17283
17284     /**
17285      * By default the frame is positioned exactly where the drag element is, so
17286      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17287      * you do not have constraints on the obj is to have the drag frame centered
17288      * around the cursor.  Set centerFrame to true for this effect.
17289      * @property centerFrame
17290      * @type boolean
17291      */
17292     centerFrame: false,
17293
17294     /**
17295      * Creates the proxy element if it does not yet exist
17296      * @method createFrame
17297      */
17298     createFrame: function() {
17299         var self = this;
17300         var body = document.body;
17301
17302         if (!body || !body.firstChild) {
17303             setTimeout( function() { self.createFrame(); }, 50 );
17304             return;
17305         }
17306
17307         var div = this.getDragEl();
17308
17309         if (!div) {
17310             div    = document.createElement("div");
17311             div.id = this.dragElId;
17312             var s  = div.style;
17313
17314             s.position   = "absolute";
17315             s.visibility = "hidden";
17316             s.cursor     = "move";
17317             s.border     = "2px solid #aaa";
17318             s.zIndex     = 999;
17319
17320             // appendChild can blow up IE if invoked prior to the window load event
17321             // while rendering a table.  It is possible there are other scenarios
17322             // that would cause this to happen as well.
17323             body.insertBefore(div, body.firstChild);
17324         }
17325     },
17326
17327     /**
17328      * Initialization for the drag frame element.  Must be called in the
17329      * constructor of all subclasses
17330      * @method initFrame
17331      */
17332     initFrame: function() {
17333         this.createFrame();
17334     },
17335
17336     applyConfig: function() {
17337         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17338
17339         this.resizeFrame = (this.config.resizeFrame !== false);
17340         this.centerFrame = (this.config.centerFrame);
17341         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17342     },
17343
17344     /**
17345      * Resizes the drag frame to the dimensions of the clicked object, positions
17346      * it over the object, and finally displays it
17347      * @method showFrame
17348      * @param {int} iPageX X click position
17349      * @param {int} iPageY Y click position
17350      * @private
17351      */
17352     showFrame: function(iPageX, iPageY) {
17353         var el = this.getEl();
17354         var dragEl = this.getDragEl();
17355         var s = dragEl.style;
17356
17357         this._resizeProxy();
17358
17359         if (this.centerFrame) {
17360             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17361                            Math.round(parseInt(s.height, 10)/2) );
17362         }
17363
17364         this.setDragElPos(iPageX, iPageY);
17365
17366         Roo.fly(dragEl).show();
17367     },
17368
17369     /**
17370      * The proxy is automatically resized to the dimensions of the linked
17371      * element when a drag is initiated, unless resizeFrame is set to false
17372      * @method _resizeProxy
17373      * @private
17374      */
17375     _resizeProxy: function() {
17376         if (this.resizeFrame) {
17377             var el = this.getEl();
17378             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17379         }
17380     },
17381
17382     // overrides Roo.dd.DragDrop
17383     b4MouseDown: function(e) {
17384         var x = e.getPageX();
17385         var y = e.getPageY();
17386         this.autoOffset(x, y);
17387         this.setDragElPos(x, y);
17388     },
17389
17390     // overrides Roo.dd.DragDrop
17391     b4StartDrag: function(x, y) {
17392         // show the drag frame
17393         this.showFrame(x, y);
17394     },
17395
17396     // overrides Roo.dd.DragDrop
17397     b4EndDrag: function(e) {
17398         Roo.fly(this.getDragEl()).hide();
17399     },
17400
17401     // overrides Roo.dd.DragDrop
17402     // By default we try to move the element to the last location of the frame.
17403     // This is so that the default behavior mirrors that of Roo.dd.DD.
17404     endDrag: function(e) {
17405
17406         var lel = this.getEl();
17407         var del = this.getDragEl();
17408
17409         // Show the drag frame briefly so we can get its position
17410         del.style.visibility = "";
17411
17412         this.beforeMove();
17413         // Hide the linked element before the move to get around a Safari
17414         // rendering bug.
17415         lel.style.visibility = "hidden";
17416         Roo.dd.DDM.moveToEl(lel, del);
17417         del.style.visibility = "hidden";
17418         lel.style.visibility = "";
17419
17420         this.afterDrag();
17421     },
17422
17423     beforeMove : function(){
17424
17425     },
17426
17427     afterDrag : function(){
17428
17429     },
17430
17431     toString: function() {
17432         return ("DDProxy " + this.id);
17433     }
17434
17435 });
17436 /*
17437  * Based on:
17438  * Ext JS Library 1.1.1
17439  * Copyright(c) 2006-2007, Ext JS, LLC.
17440  *
17441  * Originally Released Under LGPL - original licence link has changed is not relivant.
17442  *
17443  * Fork - LGPL
17444  * <script type="text/javascript">
17445  */
17446
17447  /**
17448  * @class Roo.dd.DDTarget
17449  * A DragDrop implementation that does not move, but can be a drop
17450  * target.  You would get the same result by simply omitting implementation
17451  * for the event callbacks, but this way we reduce the processing cost of the
17452  * event listener and the callbacks.
17453  * @extends Roo.dd.DragDrop
17454  * @constructor
17455  * @param {String} id the id of the element that is a drop target
17456  * @param {String} sGroup the group of related DragDrop objects
17457  * @param {object} config an object containing configurable attributes
17458  *                 Valid properties for DDTarget in addition to those in
17459  *                 DragDrop:
17460  *                    none
17461  */
17462 Roo.dd.DDTarget = function(id, sGroup, config) {
17463     if (id) {
17464         this.initTarget(id, sGroup, config);
17465     }
17466     if (config.listeners || config.events) { 
17467        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17468             listeners : config.listeners || {}, 
17469             events : config.events || {} 
17470         });    
17471     }
17472 };
17473
17474 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17475 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17476     toString: function() {
17477         return ("DDTarget " + this.id);
17478     }
17479 });
17480 /*
17481  * Based on:
17482  * Ext JS Library 1.1.1
17483  * Copyright(c) 2006-2007, Ext JS, LLC.
17484  *
17485  * Originally Released Under LGPL - original licence link has changed is not relivant.
17486  *
17487  * Fork - LGPL
17488  * <script type="text/javascript">
17489  */
17490  
17491
17492 /**
17493  * @class Roo.dd.ScrollManager
17494  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17495  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17496  * @singleton
17497  */
17498 Roo.dd.ScrollManager = function(){
17499     var ddm = Roo.dd.DragDropMgr;
17500     var els = {};
17501     var dragEl = null;
17502     var proc = {};
17503     
17504     var onStop = function(e){
17505         dragEl = null;
17506         clearProc();
17507     };
17508     
17509     var triggerRefresh = function(){
17510         if(ddm.dragCurrent){
17511              ddm.refreshCache(ddm.dragCurrent.groups);
17512         }
17513     };
17514     
17515     var doScroll = function(){
17516         if(ddm.dragCurrent){
17517             var dds = Roo.dd.ScrollManager;
17518             if(!dds.animate){
17519                 if(proc.el.scroll(proc.dir, dds.increment)){
17520                     triggerRefresh();
17521                 }
17522             }else{
17523                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17524             }
17525         }
17526     };
17527     
17528     var clearProc = function(){
17529         if(proc.id){
17530             clearInterval(proc.id);
17531         }
17532         proc.id = 0;
17533         proc.el = null;
17534         proc.dir = "";
17535     };
17536     
17537     var startProc = function(el, dir){
17538         clearProc();
17539         proc.el = el;
17540         proc.dir = dir;
17541         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17542     };
17543     
17544     var onFire = function(e, isDrop){
17545         if(isDrop || !ddm.dragCurrent){ return; }
17546         var dds = Roo.dd.ScrollManager;
17547         if(!dragEl || dragEl != ddm.dragCurrent){
17548             dragEl = ddm.dragCurrent;
17549             // refresh regions on drag start
17550             dds.refreshCache();
17551         }
17552         
17553         var xy = Roo.lib.Event.getXY(e);
17554         var pt = new Roo.lib.Point(xy[0], xy[1]);
17555         for(var id in els){
17556             var el = els[id], r = el._region;
17557             if(r && r.contains(pt) && el.isScrollable()){
17558                 if(r.bottom - pt.y <= dds.thresh){
17559                     if(proc.el != el){
17560                         startProc(el, "down");
17561                     }
17562                     return;
17563                 }else if(r.right - pt.x <= dds.thresh){
17564                     if(proc.el != el){
17565                         startProc(el, "left");
17566                     }
17567                     return;
17568                 }else if(pt.y - r.top <= dds.thresh){
17569                     if(proc.el != el){
17570                         startProc(el, "up");
17571                     }
17572                     return;
17573                 }else if(pt.x - r.left <= dds.thresh){
17574                     if(proc.el != el){
17575                         startProc(el, "right");
17576                     }
17577                     return;
17578                 }
17579             }
17580         }
17581         clearProc();
17582     };
17583     
17584     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17585     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17586     
17587     return {
17588         /**
17589          * Registers new overflow element(s) to auto scroll
17590          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17591          */
17592         register : function(el){
17593             if(el instanceof Array){
17594                 for(var i = 0, len = el.length; i < len; i++) {
17595                         this.register(el[i]);
17596                 }
17597             }else{
17598                 el = Roo.get(el);
17599                 els[el.id] = el;
17600             }
17601         },
17602         
17603         /**
17604          * Unregisters overflow element(s) so they are no longer scrolled
17605          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17606          */
17607         unregister : function(el){
17608             if(el instanceof Array){
17609                 for(var i = 0, len = el.length; i < len; i++) {
17610                         this.unregister(el[i]);
17611                 }
17612             }else{
17613                 el = Roo.get(el);
17614                 delete els[el.id];
17615             }
17616         },
17617         
17618         /**
17619          * The number of pixels from the edge of a container the pointer needs to be to 
17620          * trigger scrolling (defaults to 25)
17621          * @type Number
17622          */
17623         thresh : 25,
17624         
17625         /**
17626          * The number of pixels to scroll in each scroll increment (defaults to 50)
17627          * @type Number
17628          */
17629         increment : 100,
17630         
17631         /**
17632          * The frequency of scrolls in milliseconds (defaults to 500)
17633          * @type Number
17634          */
17635         frequency : 500,
17636         
17637         /**
17638          * True to animate the scroll (defaults to true)
17639          * @type Boolean
17640          */
17641         animate: true,
17642         
17643         /**
17644          * The animation duration in seconds - 
17645          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17646          * @type Number
17647          */
17648         animDuration: .4,
17649         
17650         /**
17651          * Manually trigger a cache refresh.
17652          */
17653         refreshCache : function(){
17654             for(var id in els){
17655                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17656                     els[id]._region = els[id].getRegion();
17657                 }
17658             }
17659         }
17660     };
17661 }();/*
17662  * Based on:
17663  * Ext JS Library 1.1.1
17664  * Copyright(c) 2006-2007, Ext JS, LLC.
17665  *
17666  * Originally Released Under LGPL - original licence link has changed is not relivant.
17667  *
17668  * Fork - LGPL
17669  * <script type="text/javascript">
17670  */
17671  
17672
17673 /**
17674  * @class Roo.dd.Registry
17675  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17676  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17677  * @singleton
17678  */
17679 Roo.dd.Registry = function(){
17680     var elements = {}; 
17681     var handles = {}; 
17682     var autoIdSeed = 0;
17683
17684     var getId = function(el, autogen){
17685         if(typeof el == "string"){
17686             return el;
17687         }
17688         var id = el.id;
17689         if(!id && autogen !== false){
17690             id = "roodd-" + (++autoIdSeed);
17691             el.id = id;
17692         }
17693         return id;
17694     };
17695     
17696     return {
17697     /**
17698      * Register a drag drop element
17699      * @param {String|HTMLElement} element The id or DOM node to register
17700      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17701      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17702      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17703      * populated in the data object (if applicable):
17704      * <pre>
17705 Value      Description<br />
17706 ---------  ------------------------------------------<br />
17707 handles    Array of DOM nodes that trigger dragging<br />
17708            for the element being registered<br />
17709 isHandle   True if the element passed in triggers<br />
17710            dragging itself, else false
17711 </pre>
17712      */
17713         register : function(el, data){
17714             data = data || {};
17715             if(typeof el == "string"){
17716                 el = document.getElementById(el);
17717             }
17718             data.ddel = el;
17719             elements[getId(el)] = data;
17720             if(data.isHandle !== false){
17721                 handles[data.ddel.id] = data;
17722             }
17723             if(data.handles){
17724                 var hs = data.handles;
17725                 for(var i = 0, len = hs.length; i < len; i++){
17726                         handles[getId(hs[i])] = data;
17727                 }
17728             }
17729         },
17730
17731     /**
17732      * Unregister a drag drop element
17733      * @param {String|HTMLElement}  element The id or DOM node to unregister
17734      */
17735         unregister : function(el){
17736             var id = getId(el, false);
17737             var data = elements[id];
17738             if(data){
17739                 delete elements[id];
17740                 if(data.handles){
17741                     var hs = data.handles;
17742                     for(var i = 0, len = hs.length; i < len; i++){
17743                         delete handles[getId(hs[i], false)];
17744                     }
17745                 }
17746             }
17747         },
17748
17749     /**
17750      * Returns the handle registered for a DOM Node by id
17751      * @param {String|HTMLElement} id The DOM node or id to look up
17752      * @return {Object} handle The custom handle data
17753      */
17754         getHandle : function(id){
17755             if(typeof id != "string"){ // must be element?
17756                 id = id.id;
17757             }
17758             return handles[id];
17759         },
17760
17761     /**
17762      * Returns the handle that is registered for the DOM node that is the target of the event
17763      * @param {Event} e The event
17764      * @return {Object} handle The custom handle data
17765      */
17766         getHandleFromEvent : function(e){
17767             var t = Roo.lib.Event.getTarget(e);
17768             return t ? handles[t.id] : null;
17769         },
17770
17771     /**
17772      * Returns a custom data object that is registered for a DOM node by id
17773      * @param {String|HTMLElement} id The DOM node or id to look up
17774      * @return {Object} data The custom data
17775      */
17776         getTarget : function(id){
17777             if(typeof id != "string"){ // must be element?
17778                 id = id.id;
17779             }
17780             return elements[id];
17781         },
17782
17783     /**
17784      * Returns a custom data object that is registered for the DOM node that is the target of the event
17785      * @param {Event} e The event
17786      * @return {Object} data The custom data
17787      */
17788         getTargetFromEvent : function(e){
17789             var t = Roo.lib.Event.getTarget(e);
17790             return t ? elements[t.id] || handles[t.id] : null;
17791         }
17792     };
17793 }();/*
17794  * Based on:
17795  * Ext JS Library 1.1.1
17796  * Copyright(c) 2006-2007, Ext JS, LLC.
17797  *
17798  * Originally Released Under LGPL - original licence link has changed is not relivant.
17799  *
17800  * Fork - LGPL
17801  * <script type="text/javascript">
17802  */
17803  
17804
17805 /**
17806  * @class Roo.dd.StatusProxy
17807  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17808  * default drag proxy used by all Roo.dd components.
17809  * @constructor
17810  * @param {Object} config
17811  */
17812 Roo.dd.StatusProxy = function(config){
17813     Roo.apply(this, config);
17814     this.id = this.id || Roo.id();
17815     this.el = new Roo.Layer({
17816         dh: {
17817             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17818                 {tag: "div", cls: "x-dd-drop-icon"},
17819                 {tag: "div", cls: "x-dd-drag-ghost"}
17820             ]
17821         }, 
17822         shadow: !config || config.shadow !== false
17823     });
17824     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17825     this.dropStatus = this.dropNotAllowed;
17826 };
17827
17828 Roo.dd.StatusProxy.prototype = {
17829     /**
17830      * @cfg {String} dropAllowed
17831      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17832      */
17833     dropAllowed : "x-dd-drop-ok",
17834     /**
17835      * @cfg {String} dropNotAllowed
17836      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17837      */
17838     dropNotAllowed : "x-dd-drop-nodrop",
17839
17840     /**
17841      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17842      * over the current target element.
17843      * @param {String} cssClass The css class for the new drop status indicator image
17844      */
17845     setStatus : function(cssClass){
17846         cssClass = cssClass || this.dropNotAllowed;
17847         if(this.dropStatus != cssClass){
17848             this.el.replaceClass(this.dropStatus, cssClass);
17849             this.dropStatus = cssClass;
17850         }
17851     },
17852
17853     /**
17854      * Resets the status indicator to the default dropNotAllowed value
17855      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17856      */
17857     reset : function(clearGhost){
17858         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17859         this.dropStatus = this.dropNotAllowed;
17860         if(clearGhost){
17861             this.ghost.update("");
17862         }
17863     },
17864
17865     /**
17866      * Updates the contents of the ghost element
17867      * @param {String} html The html that will replace the current innerHTML of the ghost element
17868      */
17869     update : function(html){
17870         if(typeof html == "string"){
17871             this.ghost.update(html);
17872         }else{
17873             this.ghost.update("");
17874             html.style.margin = "0";
17875             this.ghost.dom.appendChild(html);
17876         }
17877         // ensure float = none set?? cant remember why though.
17878         var el = this.ghost.dom.firstChild;
17879                 if(el){
17880                         Roo.fly(el).setStyle('float', 'none');
17881                 }
17882     },
17883     
17884     /**
17885      * Returns the underlying proxy {@link Roo.Layer}
17886      * @return {Roo.Layer} el
17887     */
17888     getEl : function(){
17889         return this.el;
17890     },
17891
17892     /**
17893      * Returns the ghost element
17894      * @return {Roo.Element} el
17895      */
17896     getGhost : function(){
17897         return this.ghost;
17898     },
17899
17900     /**
17901      * Hides the proxy
17902      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17903      */
17904     hide : function(clear){
17905         this.el.hide();
17906         if(clear){
17907             this.reset(true);
17908         }
17909     },
17910
17911     /**
17912      * Stops the repair animation if it's currently running
17913      */
17914     stop : function(){
17915         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17916             this.anim.stop();
17917         }
17918     },
17919
17920     /**
17921      * Displays this proxy
17922      */
17923     show : function(){
17924         this.el.show();
17925     },
17926
17927     /**
17928      * Force the Layer to sync its shadow and shim positions to the element
17929      */
17930     sync : function(){
17931         this.el.sync();
17932     },
17933
17934     /**
17935      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17936      * invalid drop operation by the item being dragged.
17937      * @param {Array} xy The XY position of the element ([x, y])
17938      * @param {Function} callback The function to call after the repair is complete
17939      * @param {Object} scope The scope in which to execute the callback
17940      */
17941     repair : function(xy, callback, scope){
17942         this.callback = callback;
17943         this.scope = scope;
17944         if(xy && this.animRepair !== false){
17945             this.el.addClass("x-dd-drag-repair");
17946             this.el.hideUnders(true);
17947             this.anim = this.el.shift({
17948                 duration: this.repairDuration || .5,
17949                 easing: 'easeOut',
17950                 xy: xy,
17951                 stopFx: true,
17952                 callback: this.afterRepair,
17953                 scope: this
17954             });
17955         }else{
17956             this.afterRepair();
17957         }
17958     },
17959
17960     // private
17961     afterRepair : function(){
17962         this.hide(true);
17963         if(typeof this.callback == "function"){
17964             this.callback.call(this.scope || this);
17965         }
17966         this.callback = null;
17967         this.scope = null;
17968     }
17969 };/*
17970  * Based on:
17971  * Ext JS Library 1.1.1
17972  * Copyright(c) 2006-2007, Ext JS, LLC.
17973  *
17974  * Originally Released Under LGPL - original licence link has changed is not relivant.
17975  *
17976  * Fork - LGPL
17977  * <script type="text/javascript">
17978  */
17979
17980 /**
17981  * @class Roo.dd.DragSource
17982  * @extends Roo.dd.DDProxy
17983  * A simple class that provides the basic implementation needed to make any element draggable.
17984  * @constructor
17985  * @param {String/HTMLElement/Element} el The container element
17986  * @param {Object} config
17987  */
17988 Roo.dd.DragSource = function(el, config){
17989     this.el = Roo.get(el);
17990     this.dragData = {};
17991     
17992     Roo.apply(this, config);
17993     
17994     if(!this.proxy){
17995         this.proxy = new Roo.dd.StatusProxy();
17996     }
17997
17998     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17999           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18000     
18001     this.dragging = false;
18002 };
18003
18004 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18005     /**
18006      * @cfg {String} dropAllowed
18007      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18008      */
18009     dropAllowed : "x-dd-drop-ok",
18010     /**
18011      * @cfg {String} dropNotAllowed
18012      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18013      */
18014     dropNotAllowed : "x-dd-drop-nodrop",
18015
18016     /**
18017      * Returns the data object associated with this drag source
18018      * @return {Object} data An object containing arbitrary data
18019      */
18020     getDragData : function(e){
18021         return this.dragData;
18022     },
18023
18024     // private
18025     onDragEnter : function(e, id){
18026         var target = Roo.dd.DragDropMgr.getDDById(id);
18027         this.cachedTarget = target;
18028         if(this.beforeDragEnter(target, e, id) !== false){
18029             if(target.isNotifyTarget){
18030                 var status = target.notifyEnter(this, e, this.dragData);
18031                 this.proxy.setStatus(status);
18032             }else{
18033                 this.proxy.setStatus(this.dropAllowed);
18034             }
18035             
18036             if(this.afterDragEnter){
18037                 /**
18038                  * An empty function by default, but provided so that you can perform a custom action
18039                  * when the dragged item enters the drop target by providing an implementation.
18040                  * @param {Roo.dd.DragDrop} target The drop target
18041                  * @param {Event} e The event object
18042                  * @param {String} id The id of the dragged element
18043                  * @method afterDragEnter
18044                  */
18045                 this.afterDragEnter(target, e, id);
18046             }
18047         }
18048     },
18049
18050     /**
18051      * An empty function by default, but provided so that you can perform a custom action
18052      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18053      * @param {Roo.dd.DragDrop} target The drop target
18054      * @param {Event} e The event object
18055      * @param {String} id The id of the dragged element
18056      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18057      */
18058     beforeDragEnter : function(target, e, id){
18059         return true;
18060     },
18061
18062     // private
18063     alignElWithMouse: function() {
18064         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18065         this.proxy.sync();
18066     },
18067
18068     // private
18069     onDragOver : function(e, id){
18070         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18071         if(this.beforeDragOver(target, e, id) !== false){
18072             if(target.isNotifyTarget){
18073                 var status = target.notifyOver(this, e, this.dragData);
18074                 this.proxy.setStatus(status);
18075             }
18076
18077             if(this.afterDragOver){
18078                 /**
18079                  * An empty function by default, but provided so that you can perform a custom action
18080                  * while the dragged item is over the drop target by providing an implementation.
18081                  * @param {Roo.dd.DragDrop} target The drop target
18082                  * @param {Event} e The event object
18083                  * @param {String} id The id of the dragged element
18084                  * @method afterDragOver
18085                  */
18086                 this.afterDragOver(target, e, id);
18087             }
18088         }
18089     },
18090
18091     /**
18092      * An empty function by default, but provided so that you can perform a custom action
18093      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18094      * @param {Roo.dd.DragDrop} target The drop target
18095      * @param {Event} e The event object
18096      * @param {String} id The id of the dragged element
18097      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18098      */
18099     beforeDragOver : function(target, e, id){
18100         return true;
18101     },
18102
18103     // private
18104     onDragOut : function(e, id){
18105         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18106         if(this.beforeDragOut(target, e, id) !== false){
18107             if(target.isNotifyTarget){
18108                 target.notifyOut(this, e, this.dragData);
18109             }
18110             this.proxy.reset();
18111             if(this.afterDragOut){
18112                 /**
18113                  * An empty function by default, but provided so that you can perform a custom action
18114                  * after the dragged item is dragged out of the target without dropping.
18115                  * @param {Roo.dd.DragDrop} target The drop target
18116                  * @param {Event} e The event object
18117                  * @param {String} id The id of the dragged element
18118                  * @method afterDragOut
18119                  */
18120                 this.afterDragOut(target, e, id);
18121             }
18122         }
18123         this.cachedTarget = null;
18124     },
18125
18126     /**
18127      * An empty function by default, but provided so that you can perform a custom action before the dragged
18128      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18129      * @param {Roo.dd.DragDrop} target The drop target
18130      * @param {Event} e The event object
18131      * @param {String} id The id of the dragged element
18132      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18133      */
18134     beforeDragOut : function(target, e, id){
18135         return true;
18136     },
18137     
18138     // private
18139     onDragDrop : function(e, id){
18140         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18141         if(this.beforeDragDrop(target, e, id) !== false){
18142             if(target.isNotifyTarget){
18143                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18144                     this.onValidDrop(target, e, id);
18145                 }else{
18146                     this.onInvalidDrop(target, e, id);
18147                 }
18148             }else{
18149                 this.onValidDrop(target, e, id);
18150             }
18151             
18152             if(this.afterDragDrop){
18153                 /**
18154                  * An empty function by default, but provided so that you can perform a custom action
18155                  * after a valid drag drop has occurred by providing an implementation.
18156                  * @param {Roo.dd.DragDrop} target The drop target
18157                  * @param {Event} e The event object
18158                  * @param {String} id The id of the dropped element
18159                  * @method afterDragDrop
18160                  */
18161                 this.afterDragDrop(target, e, id);
18162             }
18163         }
18164         delete this.cachedTarget;
18165     },
18166
18167     /**
18168      * An empty function by default, but provided so that you can perform a custom action before the dragged
18169      * item is dropped onto the target and optionally cancel the onDragDrop.
18170      * @param {Roo.dd.DragDrop} target The drop target
18171      * @param {Event} e The event object
18172      * @param {String} id The id of the dragged element
18173      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18174      */
18175     beforeDragDrop : function(target, e, id){
18176         return true;
18177     },
18178
18179     // private
18180     onValidDrop : function(target, e, id){
18181         this.hideProxy();
18182         if(this.afterValidDrop){
18183             /**
18184              * An empty function by default, but provided so that you can perform a custom action
18185              * after a valid drop has occurred by providing an implementation.
18186              * @param {Object} target The target DD 
18187              * @param {Event} e The event object
18188              * @param {String} id The id of the dropped element
18189              * @method afterInvalidDrop
18190              */
18191             this.afterValidDrop(target, e, id);
18192         }
18193     },
18194
18195     // private
18196     getRepairXY : function(e, data){
18197         return this.el.getXY();  
18198     },
18199
18200     // private
18201     onInvalidDrop : function(target, e, id){
18202         this.beforeInvalidDrop(target, e, id);
18203         if(this.cachedTarget){
18204             if(this.cachedTarget.isNotifyTarget){
18205                 this.cachedTarget.notifyOut(this, e, this.dragData);
18206             }
18207             this.cacheTarget = null;
18208         }
18209         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18210
18211         if(this.afterInvalidDrop){
18212             /**
18213              * An empty function by default, but provided so that you can perform a custom action
18214              * after an invalid drop has occurred by providing an implementation.
18215              * @param {Event} e The event object
18216              * @param {String} id The id of the dropped element
18217              * @method afterInvalidDrop
18218              */
18219             this.afterInvalidDrop(e, id);
18220         }
18221     },
18222
18223     // private
18224     afterRepair : function(){
18225         if(Roo.enableFx){
18226             this.el.highlight(this.hlColor || "c3daf9");
18227         }
18228         this.dragging = false;
18229     },
18230
18231     /**
18232      * An empty function by default, but provided so that you can perform a custom action after an invalid
18233      * drop has occurred.
18234      * @param {Roo.dd.DragDrop} target The drop target
18235      * @param {Event} e The event object
18236      * @param {String} id The id of the dragged element
18237      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18238      */
18239     beforeInvalidDrop : function(target, e, id){
18240         return true;
18241     },
18242
18243     // private
18244     handleMouseDown : function(e){
18245         if(this.dragging) {
18246             return;
18247         }
18248         var data = this.getDragData(e);
18249         if(data && this.onBeforeDrag(data, e) !== false){
18250             this.dragData = data;
18251             this.proxy.stop();
18252             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18253         } 
18254     },
18255
18256     /**
18257      * An empty function by default, but provided so that you can perform a custom action before the initial
18258      * drag event begins and optionally cancel it.
18259      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18260      * @param {Event} e The event object
18261      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18262      */
18263     onBeforeDrag : function(data, e){
18264         return true;
18265     },
18266
18267     /**
18268      * An empty function by default, but provided so that you can perform a custom action once the initial
18269      * drag event has begun.  The drag cannot be canceled from this function.
18270      * @param {Number} x The x position of the click on the dragged object
18271      * @param {Number} y The y position of the click on the dragged object
18272      */
18273     onStartDrag : Roo.emptyFn,
18274
18275     // private - YUI override
18276     startDrag : function(x, y){
18277         this.proxy.reset();
18278         this.dragging = true;
18279         this.proxy.update("");
18280         this.onInitDrag(x, y);
18281         this.proxy.show();
18282     },
18283
18284     // private
18285     onInitDrag : function(x, y){
18286         var clone = this.el.dom.cloneNode(true);
18287         clone.id = Roo.id(); // prevent duplicate ids
18288         this.proxy.update(clone);
18289         this.onStartDrag(x, y);
18290         return true;
18291     },
18292
18293     /**
18294      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18295      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18296      */
18297     getProxy : function(){
18298         return this.proxy;  
18299     },
18300
18301     /**
18302      * Hides the drag source's {@link Roo.dd.StatusProxy}
18303      */
18304     hideProxy : function(){
18305         this.proxy.hide();  
18306         this.proxy.reset(true);
18307         this.dragging = false;
18308     },
18309
18310     // private
18311     triggerCacheRefresh : function(){
18312         Roo.dd.DDM.refreshCache(this.groups);
18313     },
18314
18315     // private - override to prevent hiding
18316     b4EndDrag: function(e) {
18317     },
18318
18319     // private - override to prevent moving
18320     endDrag : function(e){
18321         this.onEndDrag(this.dragData, e);
18322     },
18323
18324     // private
18325     onEndDrag : function(data, e){
18326     },
18327     
18328     // private - pin to cursor
18329     autoOffset : function(x, y) {
18330         this.setDelta(-12, -20);
18331     }    
18332 });/*
18333  * Based on:
18334  * Ext JS Library 1.1.1
18335  * Copyright(c) 2006-2007, Ext JS, LLC.
18336  *
18337  * Originally Released Under LGPL - original licence link has changed is not relivant.
18338  *
18339  * Fork - LGPL
18340  * <script type="text/javascript">
18341  */
18342
18343
18344 /**
18345  * @class Roo.dd.DropTarget
18346  * @extends Roo.dd.DDTarget
18347  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18348  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18349  * @constructor
18350  * @param {String/HTMLElement/Element} el The container element
18351  * @param {Object} config
18352  */
18353 Roo.dd.DropTarget = function(el, config){
18354     this.el = Roo.get(el);
18355     
18356     var listeners = false; ;
18357     if (config && config.listeners) {
18358         listeners= config.listeners;
18359         delete config.listeners;
18360     }
18361     Roo.apply(this, config);
18362     
18363     if(this.containerScroll){
18364         Roo.dd.ScrollManager.register(this.el);
18365     }
18366     this.addEvents( {
18367          /**
18368          * @scope Roo.dd.DropTarget
18369          */
18370          
18371          /**
18372          * @event enter
18373          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18374          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18375          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18376          * 
18377          * IMPORTANT : it should set this.overClass and this.dropAllowed
18378          * 
18379          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18380          * @param {Event} e The event
18381          * @param {Object} data An object containing arbitrary data supplied by the drag source
18382          */
18383         "enter" : true,
18384         
18385          /**
18386          * @event over
18387          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18388          * This method will be called on every mouse movement while the drag source is over the drop target.
18389          * This default implementation simply returns the dropAllowed config value.
18390          * 
18391          * IMPORTANT : it should set this.dropAllowed
18392          * 
18393          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18394          * @param {Event} e The event
18395          * @param {Object} data An object containing arbitrary data supplied by the drag source
18396          
18397          */
18398         "over" : true,
18399         /**
18400          * @event out
18401          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18402          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18403          * overClass (if any) from the drop element.
18404          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18405          * @param {Event} e The event
18406          * @param {Object} data An object containing arbitrary data supplied by the drag source
18407          */
18408          "out" : true,
18409          
18410         /**
18411          * @event drop
18412          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18413          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18414          * implementation that does something to process the drop event and returns true so that the drag source's
18415          * repair action does not run.
18416          * 
18417          * IMPORTANT : it should set this.success
18418          * 
18419          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18420          * @param {Event} e The event
18421          * @param {Object} data An object containing arbitrary data supplied by the drag source
18422         */
18423          "drop" : true
18424     });
18425             
18426      
18427     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18428         this.el.dom, 
18429         this.ddGroup || this.group,
18430         {
18431             isTarget: true,
18432             listeners : listeners || {} 
18433            
18434         
18435         }
18436     );
18437
18438 };
18439
18440 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18441     /**
18442      * @cfg {String} overClass
18443      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18444      */
18445      /**
18446      * @cfg {String} ddGroup
18447      * The drag drop group to handle drop events for
18448      */
18449      
18450     /**
18451      * @cfg {String} dropAllowed
18452      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18453      */
18454     dropAllowed : "x-dd-drop-ok",
18455     /**
18456      * @cfg {String} dropNotAllowed
18457      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18458      */
18459     dropNotAllowed : "x-dd-drop-nodrop",
18460     /**
18461      * @cfg {boolean} success
18462      * set this after drop listener.. 
18463      */
18464     success : false,
18465     /**
18466      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18467      * if the drop point is valid for over/enter..
18468      */
18469     valid : false,
18470     // private
18471     isTarget : true,
18472
18473     // private
18474     isNotifyTarget : true,
18475     
18476     /**
18477      * @hide
18478      */
18479     notifyEnter : function(dd, e, data)
18480     {
18481         this.valid = true;
18482         this.fireEvent('enter', dd, e, data);
18483         if(this.overClass){
18484             this.el.addClass(this.overClass);
18485         }
18486         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18487             this.valid ? this.dropAllowed : this.dropNotAllowed
18488         );
18489     },
18490
18491     /**
18492      * @hide
18493      */
18494     notifyOver : function(dd, e, data)
18495     {
18496         this.valid = true;
18497         this.fireEvent('over', dd, e, data);
18498         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18499             this.valid ? this.dropAllowed : this.dropNotAllowed
18500         );
18501     },
18502
18503     /**
18504      * @hide
18505      */
18506     notifyOut : function(dd, e, data)
18507     {
18508         this.fireEvent('out', dd, e, data);
18509         if(this.overClass){
18510             this.el.removeClass(this.overClass);
18511         }
18512     },
18513
18514     /**
18515      * @hide
18516      */
18517     notifyDrop : function(dd, e, data)
18518     {
18519         this.success = false;
18520         this.fireEvent('drop', dd, e, data);
18521         return this.success;
18522     }
18523 });/*
18524  * Based on:
18525  * Ext JS Library 1.1.1
18526  * Copyright(c) 2006-2007, Ext JS, LLC.
18527  *
18528  * Originally Released Under LGPL - original licence link has changed is not relivant.
18529  *
18530  * Fork - LGPL
18531  * <script type="text/javascript">
18532  */
18533
18534
18535 /**
18536  * @class Roo.dd.DragZone
18537  * @extends Roo.dd.DragSource
18538  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18539  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18540  * @constructor
18541  * @param {String/HTMLElement/Element} el The container element
18542  * @param {Object} config
18543  */
18544 Roo.dd.DragZone = function(el, config){
18545     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18546     if(this.containerScroll){
18547         Roo.dd.ScrollManager.register(this.el);
18548     }
18549 };
18550
18551 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18552     /**
18553      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18554      * for auto scrolling during drag operations.
18555      */
18556     /**
18557      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18558      * method after a failed drop (defaults to "c3daf9" - light blue)
18559      */
18560
18561     /**
18562      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18563      * for a valid target to drag based on the mouse down. Override this method
18564      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18565      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18566      * @param {EventObject} e The mouse down event
18567      * @return {Object} The dragData
18568      */
18569     getDragData : function(e){
18570         return Roo.dd.Registry.getHandleFromEvent(e);
18571     },
18572     
18573     /**
18574      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18575      * this.dragData.ddel
18576      * @param {Number} x The x position of the click on the dragged object
18577      * @param {Number} y The y position of the click on the dragged object
18578      * @return {Boolean} true to continue the drag, false to cancel
18579      */
18580     onInitDrag : function(x, y){
18581         this.proxy.update(this.dragData.ddel.cloneNode(true));
18582         this.onStartDrag(x, y);
18583         return true;
18584     },
18585     
18586     /**
18587      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18588      */
18589     afterRepair : function(){
18590         if(Roo.enableFx){
18591             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18592         }
18593         this.dragging = false;
18594     },
18595
18596     /**
18597      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18598      * the XY of this.dragData.ddel
18599      * @param {EventObject} e The mouse up event
18600      * @return {Array} The xy location (e.g. [100, 200])
18601      */
18602     getRepairXY : function(e){
18603         return Roo.Element.fly(this.dragData.ddel).getXY();  
18604     }
18605 });/*
18606  * Based on:
18607  * Ext JS Library 1.1.1
18608  * Copyright(c) 2006-2007, Ext JS, LLC.
18609  *
18610  * Originally Released Under LGPL - original licence link has changed is not relivant.
18611  *
18612  * Fork - LGPL
18613  * <script type="text/javascript">
18614  */
18615 /**
18616  * @class Roo.dd.DropZone
18617  * @extends Roo.dd.DropTarget
18618  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18619  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18620  * @constructor
18621  * @param {String/HTMLElement/Element} el The container element
18622  * @param {Object} config
18623  */
18624 Roo.dd.DropZone = function(el, config){
18625     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18626 };
18627
18628 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18629     /**
18630      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18631      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18632      * provide your own custom lookup.
18633      * @param {Event} e The event
18634      * @return {Object} data The custom data
18635      */
18636     getTargetFromEvent : function(e){
18637         return Roo.dd.Registry.getTargetFromEvent(e);
18638     },
18639
18640     /**
18641      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18642      * that it has registered.  This method has no default implementation and should be overridden to provide
18643      * node-specific processing if necessary.
18644      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18645      * {@link #getTargetFromEvent} for this node)
18646      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18647      * @param {Event} e The event
18648      * @param {Object} data An object containing arbitrary data supplied by the drag source
18649      */
18650     onNodeEnter : function(n, dd, e, data){
18651         
18652     },
18653
18654     /**
18655      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18656      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18657      * overridden to provide the proper feedback.
18658      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18659      * {@link #getTargetFromEvent} for this node)
18660      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18661      * @param {Event} e The event
18662      * @param {Object} data An object containing arbitrary data supplied by the drag source
18663      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18664      * underlying {@link Roo.dd.StatusProxy} can be updated
18665      */
18666     onNodeOver : function(n, dd, e, data){
18667         return this.dropAllowed;
18668     },
18669
18670     /**
18671      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18672      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18673      * node-specific processing if necessary.
18674      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18675      * {@link #getTargetFromEvent} for this node)
18676      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18677      * @param {Event} e The event
18678      * @param {Object} data An object containing arbitrary data supplied by the drag source
18679      */
18680     onNodeOut : function(n, dd, e, data){
18681         
18682     },
18683
18684     /**
18685      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18686      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18687      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18688      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18689      * {@link #getTargetFromEvent} for this node)
18690      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18691      * @param {Event} e The event
18692      * @param {Object} data An object containing arbitrary data supplied by the drag source
18693      * @return {Boolean} True if the drop was valid, else false
18694      */
18695     onNodeDrop : function(n, dd, e, data){
18696         return false;
18697     },
18698
18699     /**
18700      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18701      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18702      * it should be overridden to provide the proper feedback if necessary.
18703      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18704      * @param {Event} e The event
18705      * @param {Object} data An object containing arbitrary data supplied by the drag source
18706      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18707      * underlying {@link Roo.dd.StatusProxy} can be updated
18708      */
18709     onContainerOver : function(dd, e, data){
18710         return this.dropNotAllowed;
18711     },
18712
18713     /**
18714      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18715      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18716      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18717      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18718      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18719      * @param {Event} e The event
18720      * @param {Object} data An object containing arbitrary data supplied by the drag source
18721      * @return {Boolean} True if the drop was valid, else false
18722      */
18723     onContainerDrop : function(dd, e, data){
18724         return false;
18725     },
18726
18727     /**
18728      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18729      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18730      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18731      * you should override this method and provide a custom implementation.
18732      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18733      * @param {Event} e The event
18734      * @param {Object} data An object containing arbitrary data supplied by the drag source
18735      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18736      * underlying {@link Roo.dd.StatusProxy} can be updated
18737      */
18738     notifyEnter : function(dd, e, data){
18739         return this.dropNotAllowed;
18740     },
18741
18742     /**
18743      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18744      * This method will be called on every mouse movement while the drag source is over the drop zone.
18745      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18746      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18747      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18748      * registered node, it will call {@link #onContainerOver}.
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 {String} status The CSS class that communicates the drop status back to the source so that the
18753      * underlying {@link Roo.dd.StatusProxy} can be updated
18754      */
18755     notifyOver : function(dd, e, data){
18756         var n = this.getTargetFromEvent(e);
18757         if(!n){ // not over valid drop target
18758             if(this.lastOverNode){
18759                 this.onNodeOut(this.lastOverNode, dd, e, data);
18760                 this.lastOverNode = null;
18761             }
18762             return this.onContainerOver(dd, e, data);
18763         }
18764         if(this.lastOverNode != n){
18765             if(this.lastOverNode){
18766                 this.onNodeOut(this.lastOverNode, dd, e, data);
18767             }
18768             this.onNodeEnter(n, dd, e, data);
18769             this.lastOverNode = n;
18770         }
18771         return this.onNodeOver(n, dd, e, data);
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18776      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18777      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18778      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18779      * @param {Event} e The event
18780      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18781      */
18782     notifyOut : function(dd, e, data){
18783         if(this.lastOverNode){
18784             this.onNodeOut(this.lastOverNode, dd, e, data);
18785             this.lastOverNode = null;
18786         }
18787     },
18788
18789     /**
18790      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18791      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18792      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18793      * otherwise it will call {@link #onContainerDrop}.
18794      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18795      * @param {Event} e The event
18796      * @param {Object} data An object containing arbitrary data supplied by the drag source
18797      * @return {Boolean} True if the drop was valid, else false
18798      */
18799     notifyDrop : function(dd, e, data){
18800         if(this.lastOverNode){
18801             this.onNodeOut(this.lastOverNode, dd, e, data);
18802             this.lastOverNode = null;
18803         }
18804         var n = this.getTargetFromEvent(e);
18805         return n ?
18806             this.onNodeDrop(n, dd, e, data) :
18807             this.onContainerDrop(dd, e, data);
18808     },
18809
18810     // private
18811     triggerCacheRefresh : function(){
18812         Roo.dd.DDM.refreshCache(this.groups);
18813     }  
18814 });/*
18815  * Based on:
18816  * Ext JS Library 1.1.1
18817  * Copyright(c) 2006-2007, Ext JS, LLC.
18818  *
18819  * Originally Released Under LGPL - original licence link has changed is not relivant.
18820  *
18821  * Fork - LGPL
18822  * <script type="text/javascript">
18823  */
18824
18825
18826 /**
18827  * @class Roo.data.SortTypes
18828  * @singleton
18829  * Defines the default sorting (casting?) comparison functions used when sorting data.
18830  */
18831 Roo.data.SortTypes = {
18832     /**
18833      * Default sort that does nothing
18834      * @param {Mixed} s The value being converted
18835      * @return {Mixed} The comparison value
18836      */
18837     none : function(s){
18838         return s;
18839     },
18840     
18841     /**
18842      * The regular expression used to strip tags
18843      * @type {RegExp}
18844      * @property
18845      */
18846     stripTagsRE : /<\/?[^>]+>/gi,
18847     
18848     /**
18849      * Strips all HTML tags to sort on text only
18850      * @param {Mixed} s The value being converted
18851      * @return {String} The comparison value
18852      */
18853     asText : function(s){
18854         return String(s).replace(this.stripTagsRE, "");
18855     },
18856     
18857     /**
18858      * Strips all HTML tags to sort on text only - Case insensitive
18859      * @param {Mixed} s The value being converted
18860      * @return {String} The comparison value
18861      */
18862     asUCText : function(s){
18863         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18864     },
18865     
18866     /**
18867      * Case insensitive string
18868      * @param {Mixed} s The value being converted
18869      * @return {String} The comparison value
18870      */
18871     asUCString : function(s) {
18872         return String(s).toUpperCase();
18873     },
18874     
18875     /**
18876      * Date sorting
18877      * @param {Mixed} s The value being converted
18878      * @return {Number} The comparison value
18879      */
18880     asDate : function(s) {
18881         if(!s){
18882             return 0;
18883         }
18884         if(s instanceof Date){
18885             return s.getTime();
18886         }
18887         return Date.parse(String(s));
18888     },
18889     
18890     /**
18891      * Float sorting
18892      * @param {Mixed} s The value being converted
18893      * @return {Float} The comparison value
18894      */
18895     asFloat : function(s) {
18896         var val = parseFloat(String(s).replace(/,/g, ""));
18897         if(isNaN(val)) val = 0;
18898         return val;
18899     },
18900     
18901     /**
18902      * Integer sorting
18903      * @param {Mixed} s The value being converted
18904      * @return {Number} The comparison value
18905      */
18906     asInt : function(s) {
18907         var val = parseInt(String(s).replace(/,/g, ""));
18908         if(isNaN(val)) val = 0;
18909         return val;
18910     }
18911 };/*
18912  * Based on:
18913  * Ext JS Library 1.1.1
18914  * Copyright(c) 2006-2007, Ext JS, LLC.
18915  *
18916  * Originally Released Under LGPL - original licence link has changed is not relivant.
18917  *
18918  * Fork - LGPL
18919  * <script type="text/javascript">
18920  */
18921
18922 /**
18923 * @class Roo.data.Record
18924  * Instances of this class encapsulate both record <em>definition</em> information, and record
18925  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18926  * to access Records cached in an {@link Roo.data.Store} object.<br>
18927  * <p>
18928  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18929  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18930  * objects.<br>
18931  * <p>
18932  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18933  * @constructor
18934  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18935  * {@link #create}. The parameters are the same.
18936  * @param {Array} data An associative Array of data values keyed by the field name.
18937  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18938  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18939  * not specified an integer id is generated.
18940  */
18941 Roo.data.Record = function(data, id){
18942     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18943     this.data = data;
18944 };
18945
18946 /**
18947  * Generate a constructor for a specific record layout.
18948  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18949  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18950  * Each field definition object may contain the following properties: <ul>
18951  * <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,
18952  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18953  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18954  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18955  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18956  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18957  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18958  * this may be omitted.</p></li>
18959  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18960  * <ul><li>auto (Default, implies no conversion)</li>
18961  * <li>string</li>
18962  * <li>int</li>
18963  * <li>float</li>
18964  * <li>boolean</li>
18965  * <li>date</li></ul></p></li>
18966  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18967  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18968  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18969  * by the Reader into an object that will be stored in the Record. It is passed the
18970  * following parameters:<ul>
18971  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18972  * </ul></p></li>
18973  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18974  * </ul>
18975  * <br>usage:<br><pre><code>
18976 var TopicRecord = Roo.data.Record.create(
18977     {name: 'title', mapping: 'topic_title'},
18978     {name: 'author', mapping: 'username'},
18979     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18980     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18981     {name: 'lastPoster', mapping: 'user2'},
18982     {name: 'excerpt', mapping: 'post_text'}
18983 );
18984
18985 var myNewRecord = new TopicRecord({
18986     title: 'Do my job please',
18987     author: 'noobie',
18988     totalPosts: 1,
18989     lastPost: new Date(),
18990     lastPoster: 'Animal',
18991     excerpt: 'No way dude!'
18992 });
18993 myStore.add(myNewRecord);
18994 </code></pre>
18995  * @method create
18996  * @static
18997  */
18998 Roo.data.Record.create = function(o){
18999     var f = function(){
19000         f.superclass.constructor.apply(this, arguments);
19001     };
19002     Roo.extend(f, Roo.data.Record);
19003     var p = f.prototype;
19004     p.fields = new Roo.util.MixedCollection(false, function(field){
19005         return field.name;
19006     });
19007     for(var i = 0, len = o.length; i < len; i++){
19008         p.fields.add(new Roo.data.Field(o[i]));
19009     }
19010     f.getField = function(name){
19011         return p.fields.get(name);  
19012     };
19013     return f;
19014 };
19015
19016 Roo.data.Record.AUTO_ID = 1000;
19017 Roo.data.Record.EDIT = 'edit';
19018 Roo.data.Record.REJECT = 'reject';
19019 Roo.data.Record.COMMIT = 'commit';
19020
19021 Roo.data.Record.prototype = {
19022     /**
19023      * Readonly flag - true if this record has been modified.
19024      * @type Boolean
19025      */
19026     dirty : false,
19027     editing : false,
19028     error: null,
19029     modified: null,
19030
19031     // private
19032     join : function(store){
19033         this.store = store;
19034     },
19035
19036     /**
19037      * Set the named field to the specified value.
19038      * @param {String} name The name of the field to set.
19039      * @param {Object} value The value to set the field to.
19040      */
19041     set : function(name, value){
19042         if(this.data[name] == value){
19043             return;
19044         }
19045         this.dirty = true;
19046         if(!this.modified){
19047             this.modified = {};
19048         }
19049         if(typeof this.modified[name] == 'undefined'){
19050             this.modified[name] = this.data[name];
19051         }
19052         this.data[name] = value;
19053         if(!this.editing && this.store){
19054             this.store.afterEdit(this);
19055         }       
19056     },
19057
19058     /**
19059      * Get the value of the named field.
19060      * @param {String} name The name of the field to get the value of.
19061      * @return {Object} The value of the field.
19062      */
19063     get : function(name){
19064         return this.data[name]; 
19065     },
19066
19067     // private
19068     beginEdit : function(){
19069         this.editing = true;
19070         this.modified = {}; 
19071     },
19072
19073     // private
19074     cancelEdit : function(){
19075         this.editing = false;
19076         delete this.modified;
19077     },
19078
19079     // private
19080     endEdit : function(){
19081         this.editing = false;
19082         if(this.dirty && this.store){
19083             this.store.afterEdit(this);
19084         }
19085     },
19086
19087     /**
19088      * Usually called by the {@link Roo.data.Store} which owns the Record.
19089      * Rejects all changes made to the Record since either creation, or the last commit operation.
19090      * Modified fields are reverted to their original values.
19091      * <p>
19092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19093      * of reject operations.
19094      */
19095     reject : function(){
19096         var m = this.modified;
19097         for(var n in m){
19098             if(typeof m[n] != "function"){
19099                 this.data[n] = m[n];
19100             }
19101         }
19102         this.dirty = false;
19103         delete this.modified;
19104         this.editing = false;
19105         if(this.store){
19106             this.store.afterReject(this);
19107         }
19108     },
19109
19110     /**
19111      * Usually called by the {@link Roo.data.Store} which owns the Record.
19112      * Commits all changes made to the Record since either creation, or the last commit operation.
19113      * <p>
19114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19115      * of commit operations.
19116      */
19117     commit : function(){
19118         this.dirty = false;
19119         delete this.modified;
19120         this.editing = false;
19121         if(this.store){
19122             this.store.afterCommit(this);
19123         }
19124     },
19125
19126     // private
19127     hasError : function(){
19128         return this.error != null;
19129     },
19130
19131     // private
19132     clearError : function(){
19133         this.error = null;
19134     },
19135
19136     /**
19137      * Creates a copy of this record.
19138      * @param {String} id (optional) A new record id if you don't want to use this record's id
19139      * @return {Record}
19140      */
19141     copy : function(newId) {
19142         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19143     }
19144 };/*
19145  * Based on:
19146  * Ext JS Library 1.1.1
19147  * Copyright(c) 2006-2007, Ext JS, LLC.
19148  *
19149  * Originally Released Under LGPL - original licence link has changed is not relivant.
19150  *
19151  * Fork - LGPL
19152  * <script type="text/javascript">
19153  */
19154
19155
19156
19157 /**
19158  * @class Roo.data.Store
19159  * @extends Roo.util.Observable
19160  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19161  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19162  * <p>
19163  * 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
19164  * has no knowledge of the format of the data returned by the Proxy.<br>
19165  * <p>
19166  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19167  * instances from the data object. These records are cached and made available through accessor functions.
19168  * @constructor
19169  * Creates a new Store.
19170  * @param {Object} config A config object containing the objects needed for the Store to access data,
19171  * and read the data into Records.
19172  */
19173 Roo.data.Store = function(config){
19174     this.data = new Roo.util.MixedCollection(false);
19175     this.data.getKey = function(o){
19176         return o.id;
19177     };
19178     this.baseParams = {};
19179     // private
19180     this.paramNames = {
19181         "start" : "start",
19182         "limit" : "limit",
19183         "sort" : "sort",
19184         "dir" : "dir",
19185         "multisort" : "_multisort"
19186     };
19187
19188     if(config && config.data){
19189         this.inlineData = config.data;
19190         delete config.data;
19191     }
19192
19193     Roo.apply(this, config);
19194     
19195     if(this.reader){ // reader passed
19196         this.reader = Roo.factory(this.reader, Roo.data);
19197         this.reader.xmodule = this.xmodule || false;
19198         if(!this.recordType){
19199             this.recordType = this.reader.recordType;
19200         }
19201         if(this.reader.onMetaChange){
19202             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19203         }
19204     }
19205
19206     if(this.recordType){
19207         this.fields = this.recordType.prototype.fields;
19208     }
19209     this.modified = [];
19210
19211     this.addEvents({
19212         /**
19213          * @event datachanged
19214          * Fires when the data cache has changed, and a widget which is using this Store
19215          * as a Record cache should refresh its view.
19216          * @param {Store} this
19217          */
19218         datachanged : true,
19219         /**
19220          * @event metachange
19221          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19222          * @param {Store} this
19223          * @param {Object} meta The JSON metadata
19224          */
19225         metachange : true,
19226         /**
19227          * @event add
19228          * Fires when Records have been added to the Store
19229          * @param {Store} this
19230          * @param {Roo.data.Record[]} records The array of Records added
19231          * @param {Number} index The index at which the record(s) were added
19232          */
19233         add : true,
19234         /**
19235          * @event remove
19236          * Fires when a Record has been removed from the Store
19237          * @param {Store} this
19238          * @param {Roo.data.Record} record The Record that was removed
19239          * @param {Number} index The index at which the record was removed
19240          */
19241         remove : true,
19242         /**
19243          * @event update
19244          * Fires when a Record has been updated
19245          * @param {Store} this
19246          * @param {Roo.data.Record} record The Record that was updated
19247          * @param {String} operation The update operation being performed.  Value may be one of:
19248          * <pre><code>
19249  Roo.data.Record.EDIT
19250  Roo.data.Record.REJECT
19251  Roo.data.Record.COMMIT
19252          * </code></pre>
19253          */
19254         update : true,
19255         /**
19256          * @event clear
19257          * Fires when the data cache has been cleared.
19258          * @param {Store} this
19259          */
19260         clear : true,
19261         /**
19262          * @event beforeload
19263          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19264          * the load action will be canceled.
19265          * @param {Store} this
19266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19267          */
19268         beforeload : true,
19269         /**
19270          * @event load
19271          * Fires after a new set of Records has been loaded.
19272          * @param {Store} this
19273          * @param {Roo.data.Record[]} records The Records that were loaded
19274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19275          */
19276         load : true,
19277         /**
19278          * @event loadexception
19279          * Fires if an exception occurs in the Proxy during loading.
19280          * Called with the signature of the Proxy's "loadexception" event.
19281          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19282          * 
19283          * @param {Proxy} 
19284          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19285          * @param {Object} load options 
19286          * @param {Object} jsonData from your request (normally this contains the Exception)
19287          */
19288         loadexception : true
19289     });
19290     
19291     if(this.proxy){
19292         this.proxy = Roo.factory(this.proxy, Roo.data);
19293         this.proxy.xmodule = this.xmodule || false;
19294         this.relayEvents(this.proxy,  ["loadexception"]);
19295     }
19296     this.sortToggle = {};
19297     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19298
19299     Roo.data.Store.superclass.constructor.call(this);
19300
19301     if(this.inlineData){
19302         this.loadData(this.inlineData);
19303         delete this.inlineData;
19304     }
19305 };
19306 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19307      /**
19308     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19309     * without a remote query - used by combo/forms at present.
19310     */
19311     
19312     /**
19313     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19314     */
19315     /**
19316     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19317     */
19318     /**
19319     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19320     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19321     */
19322     /**
19323     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19324     * on any HTTP request
19325     */
19326     /**
19327     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19328     */
19329     /**
19330     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19331     */
19332     multiSort: false,
19333     /**
19334     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19335     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19336     */
19337     remoteSort : false,
19338
19339     /**
19340     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19341      * loaded or when a record is removed. (defaults to false).
19342     */
19343     pruneModifiedRecords : false,
19344
19345     // private
19346     lastOptions : null,
19347
19348     /**
19349      * Add Records to the Store and fires the add event.
19350      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19351      */
19352     add : function(records){
19353         records = [].concat(records);
19354         for(var i = 0, len = records.length; i < len; i++){
19355             records[i].join(this);
19356         }
19357         var index = this.data.length;
19358         this.data.addAll(records);
19359         this.fireEvent("add", this, records, index);
19360     },
19361
19362     /**
19363      * Remove a Record from the Store and fires the remove event.
19364      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19365      */
19366     remove : function(record){
19367         var index = this.data.indexOf(record);
19368         this.data.removeAt(index);
19369         if(this.pruneModifiedRecords){
19370             this.modified.remove(record);
19371         }
19372         this.fireEvent("remove", this, record, index);
19373     },
19374
19375     /**
19376      * Remove all Records from the Store and fires the clear event.
19377      */
19378     removeAll : function(){
19379         this.data.clear();
19380         if(this.pruneModifiedRecords){
19381             this.modified = [];
19382         }
19383         this.fireEvent("clear", this);
19384     },
19385
19386     /**
19387      * Inserts Records to the Store at the given index and fires the add event.
19388      * @param {Number} index The start index at which to insert the passed Records.
19389      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19390      */
19391     insert : function(index, records){
19392         records = [].concat(records);
19393         for(var i = 0, len = records.length; i < len; i++){
19394             this.data.insert(index, records[i]);
19395             records[i].join(this);
19396         }
19397         this.fireEvent("add", this, records, index);
19398     },
19399
19400     /**
19401      * Get the index within the cache of the passed Record.
19402      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19403      * @return {Number} The index of the passed Record. Returns -1 if not found.
19404      */
19405     indexOf : function(record){
19406         return this.data.indexOf(record);
19407     },
19408
19409     /**
19410      * Get the index within the cache of the Record with the passed id.
19411      * @param {String} id The id of the Record to find.
19412      * @return {Number} The index of the Record. Returns -1 if not found.
19413      */
19414     indexOfId : function(id){
19415         return this.data.indexOfKey(id);
19416     },
19417
19418     /**
19419      * Get the Record with the specified id.
19420      * @param {String} id The id of the Record to find.
19421      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19422      */
19423     getById : function(id){
19424         return this.data.key(id);
19425     },
19426
19427     /**
19428      * Get the Record at the specified index.
19429      * @param {Number} index The index of the Record to find.
19430      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19431      */
19432     getAt : function(index){
19433         return this.data.itemAt(index);
19434     },
19435
19436     /**
19437      * Returns a range of Records between specified indices.
19438      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19439      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19440      * @return {Roo.data.Record[]} An array of Records
19441      */
19442     getRange : function(start, end){
19443         return this.data.getRange(start, end);
19444     },
19445
19446     // private
19447     storeOptions : function(o){
19448         o = Roo.apply({}, o);
19449         delete o.callback;
19450         delete o.scope;
19451         this.lastOptions = o;
19452     },
19453
19454     /**
19455      * Loads the Record cache from the configured Proxy using the configured Reader.
19456      * <p>
19457      * If using remote paging, then the first load call must specify the <em>start</em>
19458      * and <em>limit</em> properties in the options.params property to establish the initial
19459      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19460      * <p>
19461      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19462      * and this call will return before the new data has been loaded. Perform any post-processing
19463      * in a callback function, or in a "load" event handler.</strong>
19464      * <p>
19465      * @param {Object} options An object containing properties which control loading options:<ul>
19466      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19467      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19468      * passed the following arguments:<ul>
19469      * <li>r : Roo.data.Record[]</li>
19470      * <li>options: Options object from the load call</li>
19471      * <li>success: Boolean success indicator</li></ul></li>
19472      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19473      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19474      * </ul>
19475      */
19476     load : function(options){
19477         options = options || {};
19478         if(this.fireEvent("beforeload", this, options) !== false){
19479             this.storeOptions(options);
19480             var p = Roo.apply(options.params || {}, this.baseParams);
19481             // if meta was not loaded from remote source.. try requesting it.
19482             if (!this.reader.metaFromRemote) {
19483                 p._requestMeta = 1;
19484             }
19485             if(this.sortInfo && this.remoteSort){
19486                 var pn = this.paramNames;
19487                 p[pn["sort"]] = this.sortInfo.field;
19488                 p[pn["dir"]] = this.sortInfo.direction;
19489             }
19490             if (this.multiSort) {
19491                 var pn = this.paramNames;
19492                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19493             }
19494             
19495             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19496         }
19497     },
19498
19499     /**
19500      * Reloads the Record cache from the configured Proxy using the configured Reader and
19501      * the options from the last load operation performed.
19502      * @param {Object} options (optional) An object containing properties which may override the options
19503      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19504      * the most recently used options are reused).
19505      */
19506     reload : function(options){
19507         this.load(Roo.applyIf(options||{}, this.lastOptions));
19508     },
19509
19510     // private
19511     // Called as a callback by the Reader during a load operation.
19512     loadRecords : function(o, options, success){
19513         if(!o || success === false){
19514             if(success !== false){
19515                 this.fireEvent("load", this, [], options);
19516             }
19517             if(options.callback){
19518                 options.callback.call(options.scope || this, [], options, false);
19519             }
19520             return;
19521         }
19522         // if data returned failure - throw an exception.
19523         if (o.success === false) {
19524             // show a message if no listener is registered.
19525             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19526                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19527             }
19528             // loadmask wil be hooked into this..
19529             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19530             return;
19531         }
19532         var r = o.records, t = o.totalRecords || r.length;
19533         if(!options || options.add !== true){
19534             if(this.pruneModifiedRecords){
19535                 this.modified = [];
19536             }
19537             for(var i = 0, len = r.length; i < len; i++){
19538                 r[i].join(this);
19539             }
19540             if(this.snapshot){
19541                 this.data = this.snapshot;
19542                 delete this.snapshot;
19543             }
19544             this.data.clear();
19545             this.data.addAll(r);
19546             this.totalLength = t;
19547             this.applySort();
19548             this.fireEvent("datachanged", this);
19549         }else{
19550             this.totalLength = Math.max(t, this.data.length+r.length);
19551             this.add(r);
19552         }
19553         this.fireEvent("load", this, r, options);
19554         if(options.callback){
19555             options.callback.call(options.scope || this, r, options, true);
19556         }
19557     },
19558
19559
19560     /**
19561      * Loads data from a passed data block. A Reader which understands the format of the data
19562      * must have been configured in the constructor.
19563      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19564      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19565      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19566      */
19567     loadData : function(o, append){
19568         var r = this.reader.readRecords(o);
19569         this.loadRecords(r, {add: append}, true);
19570     },
19571
19572     /**
19573      * Gets the number of cached records.
19574      * <p>
19575      * <em>If using paging, this may not be the total size of the dataset. If the data object
19576      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19577      * the data set size</em>
19578      */
19579     getCount : function(){
19580         return this.data.length || 0;
19581     },
19582
19583     /**
19584      * Gets the total number of records in the dataset as returned by the server.
19585      * <p>
19586      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19587      * the dataset size</em>
19588      */
19589     getTotalCount : function(){
19590         return this.totalLength || 0;
19591     },
19592
19593     /**
19594      * Returns the sort state of the Store as an object with two properties:
19595      * <pre><code>
19596  field {String} The name of the field by which the Records are sorted
19597  direction {String} The sort order, "ASC" or "DESC"
19598      * </code></pre>
19599      */
19600     getSortState : function(){
19601         return this.sortInfo;
19602     },
19603
19604     // private
19605     applySort : function(){
19606         if(this.sortInfo && !this.remoteSort){
19607             var s = this.sortInfo, f = s.field;
19608             var st = this.fields.get(f).sortType;
19609             var fn = function(r1, r2){
19610                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19611                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19612             };
19613             this.data.sort(s.direction, fn);
19614             if(this.snapshot && this.snapshot != this.data){
19615                 this.snapshot.sort(s.direction, fn);
19616             }
19617         }
19618     },
19619
19620     /**
19621      * Sets the default sort column and order to be used by the next load operation.
19622      * @param {String} fieldName The name of the field to sort by.
19623      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19624      */
19625     setDefaultSort : function(field, dir){
19626         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19627     },
19628
19629     /**
19630      * Sort the Records.
19631      * If remote sorting is used, the sort is performed on the server, and the cache is
19632      * reloaded. If local sorting is used, the cache is sorted internally.
19633      * @param {String} fieldName The name of the field to sort by.
19634      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19635      */
19636     sort : function(fieldName, dir){
19637         var f = this.fields.get(fieldName);
19638         if(!dir){
19639             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19640             
19641             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19642                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19643             }else{
19644                 dir = f.sortDir;
19645             }
19646         }
19647         this.sortToggle[f.name] = dir;
19648         this.sortInfo = {field: f.name, direction: dir};
19649         if(!this.remoteSort){
19650             this.applySort();
19651             this.fireEvent("datachanged", this);
19652         }else{
19653             this.load(this.lastOptions);
19654         }
19655     },
19656
19657     /**
19658      * Calls the specified function for each of the Records in the cache.
19659      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19660      * Returning <em>false</em> aborts and exits the iteration.
19661      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19662      */
19663     each : function(fn, scope){
19664         this.data.each(fn, scope);
19665     },
19666
19667     /**
19668      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19669      * (e.g., during paging).
19670      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19671      */
19672     getModifiedRecords : function(){
19673         return this.modified;
19674     },
19675
19676     // private
19677     createFilterFn : function(property, value, anyMatch){
19678         if(!value.exec){ // not a regex
19679             value = String(value);
19680             if(value.length == 0){
19681                 return false;
19682             }
19683             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19684         }
19685         return function(r){
19686             return value.test(r.data[property]);
19687         };
19688     },
19689
19690     /**
19691      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19692      * @param {String} property A field on your records
19693      * @param {Number} start The record index to start at (defaults to 0)
19694      * @param {Number} end The last record index to include (defaults to length - 1)
19695      * @return {Number} The sum
19696      */
19697     sum : function(property, start, end){
19698         var rs = this.data.items, v = 0;
19699         start = start || 0;
19700         end = (end || end === 0) ? end : rs.length-1;
19701
19702         for(var i = start; i <= end; i++){
19703             v += (rs[i].data[property] || 0);
19704         }
19705         return v;
19706     },
19707
19708     /**
19709      * Filter the records by a specified property.
19710      * @param {String} field A field on your records
19711      * @param {String/RegExp} value Either a string that the field
19712      * should start with or a RegExp to test against the field
19713      * @param {Boolean} anyMatch True to match any part not just the beginning
19714      */
19715     filter : function(property, value, anyMatch){
19716         var fn = this.createFilterFn(property, value, anyMatch);
19717         return fn ? this.filterBy(fn) : this.clearFilter();
19718     },
19719
19720     /**
19721      * Filter by a function. The specified function will be called with each
19722      * record in this data source. If the function returns true the record is included,
19723      * otherwise it is filtered.
19724      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19725      * @param {Object} scope (optional) The scope of the function (defaults to this)
19726      */
19727     filterBy : function(fn, scope){
19728         this.snapshot = this.snapshot || this.data;
19729         this.data = this.queryBy(fn, scope||this);
19730         this.fireEvent("datachanged", this);
19731     },
19732
19733     /**
19734      * Query the records by a specified property.
19735      * @param {String} field A field on your records
19736      * @param {String/RegExp} value Either a string that the field
19737      * should start with or a RegExp to test against the field
19738      * @param {Boolean} anyMatch True to match any part not just the beginning
19739      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19740      */
19741     query : function(property, value, anyMatch){
19742         var fn = this.createFilterFn(property, value, anyMatch);
19743         return fn ? this.queryBy(fn) : this.data.clone();
19744     },
19745
19746     /**
19747      * Query by a function. The specified function will be called with each
19748      * record in this data source. If the function returns true the record is included
19749      * in the results.
19750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19751      * @param {Object} scope (optional) The scope of the function (defaults to this)
19752       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19753      **/
19754     queryBy : function(fn, scope){
19755         var data = this.snapshot || this.data;
19756         return data.filterBy(fn, scope||this);
19757     },
19758
19759     /**
19760      * Collects unique values for a particular dataIndex from this store.
19761      * @param {String} dataIndex The property to collect
19762      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19763      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19764      * @return {Array} An array of the unique values
19765      **/
19766     collect : function(dataIndex, allowNull, bypassFilter){
19767         var d = (bypassFilter === true && this.snapshot) ?
19768                 this.snapshot.items : this.data.items;
19769         var v, sv, r = [], l = {};
19770         for(var i = 0, len = d.length; i < len; i++){
19771             v = d[i].data[dataIndex];
19772             sv = String(v);
19773             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19774                 l[sv] = true;
19775                 r[r.length] = v;
19776             }
19777         }
19778         return r;
19779     },
19780
19781     /**
19782      * Revert to a view of the Record cache with no filtering applied.
19783      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19784      */
19785     clearFilter : function(suppressEvent){
19786         if(this.snapshot && this.snapshot != this.data){
19787             this.data = this.snapshot;
19788             delete this.snapshot;
19789             if(suppressEvent !== true){
19790                 this.fireEvent("datachanged", this);
19791             }
19792         }
19793     },
19794
19795     // private
19796     afterEdit : function(record){
19797         if(this.modified.indexOf(record) == -1){
19798             this.modified.push(record);
19799         }
19800         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19801     },
19802     
19803     // private
19804     afterReject : function(record){
19805         this.modified.remove(record);
19806         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19807     },
19808
19809     // private
19810     afterCommit : function(record){
19811         this.modified.remove(record);
19812         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19813     },
19814
19815     /**
19816      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19817      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19818      */
19819     commitChanges : function(){
19820         var m = this.modified.slice(0);
19821         this.modified = [];
19822         for(var i = 0, len = m.length; i < len; i++){
19823             m[i].commit();
19824         }
19825     },
19826
19827     /**
19828      * Cancel outstanding changes on all changed records.
19829      */
19830     rejectChanges : function(){
19831         var m = this.modified.slice(0);
19832         this.modified = [];
19833         for(var i = 0, len = m.length; i < len; i++){
19834             m[i].reject();
19835         }
19836     },
19837
19838     onMetaChange : function(meta, rtype, o){
19839         this.recordType = rtype;
19840         this.fields = rtype.prototype.fields;
19841         delete this.snapshot;
19842         this.sortInfo = meta.sortInfo || this.sortInfo;
19843         this.modified = [];
19844         this.fireEvent('metachange', this, this.reader.meta);
19845     }
19846 });/*
19847  * Based on:
19848  * Ext JS Library 1.1.1
19849  * Copyright(c) 2006-2007, Ext JS, LLC.
19850  *
19851  * Originally Released Under LGPL - original licence link has changed is not relivant.
19852  *
19853  * Fork - LGPL
19854  * <script type="text/javascript">
19855  */
19856
19857 /**
19858  * @class Roo.data.SimpleStore
19859  * @extends Roo.data.Store
19860  * Small helper class to make creating Stores from Array data easier.
19861  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19862  * @cfg {Array} fields An array of field definition objects, or field name strings.
19863  * @cfg {Array} data The multi-dimensional array of data
19864  * @constructor
19865  * @param {Object} config
19866  */
19867 Roo.data.SimpleStore = function(config){
19868     Roo.data.SimpleStore.superclass.constructor.call(this, {
19869         isLocal : true,
19870         reader: new Roo.data.ArrayReader({
19871                 id: config.id
19872             },
19873             Roo.data.Record.create(config.fields)
19874         ),
19875         proxy : new Roo.data.MemoryProxy(config.data)
19876     });
19877     this.load();
19878 };
19879 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19880  * Based on:
19881  * Ext JS Library 1.1.1
19882  * Copyright(c) 2006-2007, Ext JS, LLC.
19883  *
19884  * Originally Released Under LGPL - original licence link has changed is not relivant.
19885  *
19886  * Fork - LGPL
19887  * <script type="text/javascript">
19888  */
19889
19890 /**
19891 /**
19892  * @extends Roo.data.Store
19893  * @class Roo.data.JsonStore
19894  * Small helper class to make creating Stores for JSON data easier. <br/>
19895 <pre><code>
19896 var store = new Roo.data.JsonStore({
19897     url: 'get-images.php',
19898     root: 'images',
19899     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19900 });
19901 </code></pre>
19902  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19903  * JsonReader and HttpProxy (unless inline data is provided).</b>
19904  * @cfg {Array} fields An array of field definition objects, or field name strings.
19905  * @constructor
19906  * @param {Object} config
19907  */
19908 Roo.data.JsonStore = function(c){
19909     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19910         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19911         reader: new Roo.data.JsonReader(c, c.fields)
19912     }));
19913 };
19914 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19915  * Based on:
19916  * Ext JS Library 1.1.1
19917  * Copyright(c) 2006-2007, Ext JS, LLC.
19918  *
19919  * Originally Released Under LGPL - original licence link has changed is not relivant.
19920  *
19921  * Fork - LGPL
19922  * <script type="text/javascript">
19923  */
19924
19925  
19926 Roo.data.Field = function(config){
19927     if(typeof config == "string"){
19928         config = {name: config};
19929     }
19930     Roo.apply(this, config);
19931     
19932     if(!this.type){
19933         this.type = "auto";
19934     }
19935     
19936     var st = Roo.data.SortTypes;
19937     // named sortTypes are supported, here we look them up
19938     if(typeof this.sortType == "string"){
19939         this.sortType = st[this.sortType];
19940     }
19941     
19942     // set default sortType for strings and dates
19943     if(!this.sortType){
19944         switch(this.type){
19945             case "string":
19946                 this.sortType = st.asUCString;
19947                 break;
19948             case "date":
19949                 this.sortType = st.asDate;
19950                 break;
19951             default:
19952                 this.sortType = st.none;
19953         }
19954     }
19955
19956     // define once
19957     var stripRe = /[\$,%]/g;
19958
19959     // prebuilt conversion function for this field, instead of
19960     // switching every time we're reading a value
19961     if(!this.convert){
19962         var cv, dateFormat = this.dateFormat;
19963         switch(this.type){
19964             case "":
19965             case "auto":
19966             case undefined:
19967                 cv = function(v){ return v; };
19968                 break;
19969             case "string":
19970                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19971                 break;
19972             case "int":
19973                 cv = function(v){
19974                     return v !== undefined && v !== null && v !== '' ?
19975                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19976                     };
19977                 break;
19978             case "float":
19979                 cv = function(v){
19980                     return v !== undefined && v !== null && v !== '' ?
19981                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19982                     };
19983                 break;
19984             case "bool":
19985             case "boolean":
19986                 cv = function(v){ return v === true || v === "true" || v == 1; };
19987                 break;
19988             case "date":
19989                 cv = function(v){
19990                     if(!v){
19991                         return '';
19992                     }
19993                     if(v instanceof Date){
19994                         return v;
19995                     }
19996                     if(dateFormat){
19997                         if(dateFormat == "timestamp"){
19998                             return new Date(v*1000);
19999                         }
20000                         return Date.parseDate(v, dateFormat);
20001                     }
20002                     var parsed = Date.parse(v);
20003                     return parsed ? new Date(parsed) : null;
20004                 };
20005              break;
20006             
20007         }
20008         this.convert = cv;
20009     }
20010 };
20011
20012 Roo.data.Field.prototype = {
20013     dateFormat: null,
20014     defaultValue: "",
20015     mapping: null,
20016     sortType : null,
20017     sortDir : "ASC"
20018 };/*
20019  * Based on:
20020  * Ext JS Library 1.1.1
20021  * Copyright(c) 2006-2007, Ext JS, LLC.
20022  *
20023  * Originally Released Under LGPL - original licence link has changed is not relivant.
20024  *
20025  * Fork - LGPL
20026  * <script type="text/javascript">
20027  */
20028  
20029 // Base class for reading structured data from a data source.  This class is intended to be
20030 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20031
20032 /**
20033  * @class Roo.data.DataReader
20034  * Base class for reading structured data from a data source.  This class is intended to be
20035  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20036  */
20037
20038 Roo.data.DataReader = function(meta, recordType){
20039     
20040     this.meta = meta;
20041     
20042     this.recordType = recordType instanceof Array ? 
20043         Roo.data.Record.create(recordType) : recordType;
20044 };
20045
20046 Roo.data.DataReader.prototype = {
20047      /**
20048      * Create an empty record
20049      * @param {Object} data (optional) - overlay some values
20050      * @return {Roo.data.Record} record created.
20051      */
20052     newRow :  function(d) {
20053         var da =  {};
20054         this.recordType.prototype.fields.each(function(c) {
20055             switch( c.type) {
20056                 case 'int' : da[c.name] = 0; break;
20057                 case 'date' : da[c.name] = new Date(); break;
20058                 case 'float' : da[c.name] = 0.0; break;
20059                 case 'boolean' : da[c.name] = false; break;
20060                 default : da[c.name] = ""; break;
20061             }
20062             
20063         });
20064         return new this.recordType(Roo.apply(da, d));
20065     }
20066     
20067 };/*
20068  * Based on:
20069  * Ext JS Library 1.1.1
20070  * Copyright(c) 2006-2007, Ext JS, LLC.
20071  *
20072  * Originally Released Under LGPL - original licence link has changed is not relivant.
20073  *
20074  * Fork - LGPL
20075  * <script type="text/javascript">
20076  */
20077
20078 /**
20079  * @class Roo.data.DataProxy
20080  * @extends Roo.data.Observable
20081  * This class is an abstract base class for implementations which provide retrieval of
20082  * unformatted data objects.<br>
20083  * <p>
20084  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20085  * (of the appropriate type which knows how to parse the data object) to provide a block of
20086  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20087  * <p>
20088  * Custom implementations must implement the load method as described in
20089  * {@link Roo.data.HttpProxy#load}.
20090  */
20091 Roo.data.DataProxy = function(){
20092     this.addEvents({
20093         /**
20094          * @event beforeload
20095          * Fires before a network request is made to retrieve a data object.
20096          * @param {Object} This DataProxy object.
20097          * @param {Object} params The params parameter to the load function.
20098          */
20099         beforeload : true,
20100         /**
20101          * @event load
20102          * Fires before the load method's callback is called.
20103          * @param {Object} This DataProxy object.
20104          * @param {Object} o The data object.
20105          * @param {Object} arg The callback argument object passed to the load function.
20106          */
20107         load : true,
20108         /**
20109          * @event loadexception
20110          * Fires if an Exception occurs during data retrieval.
20111          * @param {Object} This DataProxy object.
20112          * @param {Object} o The data object.
20113          * @param {Object} arg The callback argument object passed to the load function.
20114          * @param {Object} e The Exception.
20115          */
20116         loadexception : true
20117     });
20118     Roo.data.DataProxy.superclass.constructor.call(this);
20119 };
20120
20121 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20122
20123     /**
20124      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20125      */
20126 /*
20127  * Based on:
20128  * Ext JS Library 1.1.1
20129  * Copyright(c) 2006-2007, Ext JS, LLC.
20130  *
20131  * Originally Released Under LGPL - original licence link has changed is not relivant.
20132  *
20133  * Fork - LGPL
20134  * <script type="text/javascript">
20135  */
20136 /**
20137  * @class Roo.data.MemoryProxy
20138  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20139  * to the Reader when its load method is called.
20140  * @constructor
20141  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20142  */
20143 Roo.data.MemoryProxy = function(data){
20144     if (data.data) {
20145         data = data.data;
20146     }
20147     Roo.data.MemoryProxy.superclass.constructor.call(this);
20148     this.data = data;
20149 };
20150
20151 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20152     /**
20153      * Load data from the requested source (in this case an in-memory
20154      * data object passed to the constructor), read the data object into
20155      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20156      * process that block using the passed callback.
20157      * @param {Object} params This parameter is not used by the MemoryProxy class.
20158      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20159      * object into a block of Roo.data.Records.
20160      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20161      * The function must be passed <ul>
20162      * <li>The Record block object</li>
20163      * <li>The "arg" argument from the load function</li>
20164      * <li>A boolean success indicator</li>
20165      * </ul>
20166      * @param {Object} scope The scope in which to call the callback
20167      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20168      */
20169     load : function(params, reader, callback, scope, arg){
20170         params = params || {};
20171         var result;
20172         try {
20173             result = reader.readRecords(this.data);
20174         }catch(e){
20175             this.fireEvent("loadexception", this, arg, null, e);
20176             callback.call(scope, null, arg, false);
20177             return;
20178         }
20179         callback.call(scope, result, arg, true);
20180     },
20181     
20182     // private
20183     update : function(params, records){
20184         
20185     }
20186 });/*
20187  * Based on:
20188  * Ext JS Library 1.1.1
20189  * Copyright(c) 2006-2007, Ext JS, LLC.
20190  *
20191  * Originally Released Under LGPL - original licence link has changed is not relivant.
20192  *
20193  * Fork - LGPL
20194  * <script type="text/javascript">
20195  */
20196 /**
20197  * @class Roo.data.HttpProxy
20198  * @extends Roo.data.DataProxy
20199  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20200  * configured to reference a certain URL.<br><br>
20201  * <p>
20202  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20203  * from which the running page was served.<br><br>
20204  * <p>
20205  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20206  * <p>
20207  * Be aware that to enable the browser to parse an XML document, the server must set
20208  * the Content-Type header in the HTTP response to "text/xml".
20209  * @constructor
20210  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20211  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20212  * will be used to make the request.
20213  */
20214 Roo.data.HttpProxy = function(conn){
20215     Roo.data.HttpProxy.superclass.constructor.call(this);
20216     // is conn a conn config or a real conn?
20217     this.conn = conn;
20218     this.useAjax = !conn || !conn.events;
20219   
20220 };
20221
20222 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20223     // thse are take from connection...
20224     
20225     /**
20226      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20227      */
20228     /**
20229      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20230      * extra parameters to each request made by this object. (defaults to undefined)
20231      */
20232     /**
20233      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20234      *  to each request made by this object. (defaults to undefined)
20235      */
20236     /**
20237      * @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)
20238      */
20239     /**
20240      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20241      */
20242      /**
20243      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20244      * @type Boolean
20245      */
20246   
20247
20248     /**
20249      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20250      * @type Boolean
20251      */
20252     /**
20253      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20254      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20255      * a finer-grained basis than the DataProxy events.
20256      */
20257     getConnection : function(){
20258         return this.useAjax ? Roo.Ajax : this.conn;
20259     },
20260
20261     /**
20262      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20263      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20264      * process that block using the passed callback.
20265      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20266      * for the request to the remote server.
20267      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20268      * object into a block of Roo.data.Records.
20269      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20270      * The function must be passed <ul>
20271      * <li>The Record block object</li>
20272      * <li>The "arg" argument from the load function</li>
20273      * <li>A boolean success indicator</li>
20274      * </ul>
20275      * @param {Object} scope The scope in which to call the callback
20276      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20277      */
20278     load : function(params, reader, callback, scope, arg){
20279         if(this.fireEvent("beforeload", this, params) !== false){
20280             var  o = {
20281                 params : params || {},
20282                 request: {
20283                     callback : callback,
20284                     scope : scope,
20285                     arg : arg
20286                 },
20287                 reader: reader,
20288                 callback : this.loadResponse,
20289                 scope: this
20290             };
20291             if(this.useAjax){
20292                 Roo.applyIf(o, this.conn);
20293                 if(this.activeRequest){
20294                     Roo.Ajax.abort(this.activeRequest);
20295                 }
20296                 this.activeRequest = Roo.Ajax.request(o);
20297             }else{
20298                 this.conn.request(o);
20299             }
20300         }else{
20301             callback.call(scope||this, null, arg, false);
20302         }
20303     },
20304
20305     // private
20306     loadResponse : function(o, success, response){
20307         delete this.activeRequest;
20308         if(!success){
20309             this.fireEvent("loadexception", this, o, response);
20310             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20311             return;
20312         }
20313         var result;
20314         try {
20315             result = o.reader.read(response);
20316         }catch(e){
20317             this.fireEvent("loadexception", this, o, response, e);
20318             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20319             return;
20320         }
20321         
20322         this.fireEvent("load", this, o, o.request.arg);
20323         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20324     },
20325
20326     // private
20327     update : function(dataSet){
20328
20329     },
20330
20331     // private
20332     updateResponse : function(dataSet){
20333
20334     }
20335 });/*
20336  * Based on:
20337  * Ext JS Library 1.1.1
20338  * Copyright(c) 2006-2007, Ext JS, LLC.
20339  *
20340  * Originally Released Under LGPL - original licence link has changed is not relivant.
20341  *
20342  * Fork - LGPL
20343  * <script type="text/javascript">
20344  */
20345
20346 /**
20347  * @class Roo.data.ScriptTagProxy
20348  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20349  * other than the originating domain of the running page.<br><br>
20350  * <p>
20351  * <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
20352  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20353  * <p>
20354  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20355  * source code that is used as the source inside a &lt;script> tag.<br><br>
20356  * <p>
20357  * In order for the browser to process the returned data, the server must wrap the data object
20358  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20359  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20360  * depending on whether the callback name was passed:
20361  * <p>
20362  * <pre><code>
20363 boolean scriptTag = false;
20364 String cb = request.getParameter("callback");
20365 if (cb != null) {
20366     scriptTag = true;
20367     response.setContentType("text/javascript");
20368 } else {
20369     response.setContentType("application/x-json");
20370 }
20371 Writer out = response.getWriter();
20372 if (scriptTag) {
20373     out.write(cb + "(");
20374 }
20375 out.print(dataBlock.toJsonString());
20376 if (scriptTag) {
20377     out.write(");");
20378 }
20379 </pre></code>
20380  *
20381  * @constructor
20382  * @param {Object} config A configuration object.
20383  */
20384 Roo.data.ScriptTagProxy = function(config){
20385     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20386     Roo.apply(this, config);
20387     this.head = document.getElementsByTagName("head")[0];
20388 };
20389
20390 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20391
20392 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20393     /**
20394      * @cfg {String} url The URL from which to request the data object.
20395      */
20396     /**
20397      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20398      */
20399     timeout : 30000,
20400     /**
20401      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20402      * the server the name of the callback function set up by the load call to process the returned data object.
20403      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20404      * javascript output which calls this named function passing the data object as its only parameter.
20405      */
20406     callbackParam : "callback",
20407     /**
20408      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20409      * name to the request.
20410      */
20411     nocache : true,
20412
20413     /**
20414      * Load data from the configured URL, read the data object into
20415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20416      * process that block using the passed callback.
20417      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20418      * for the request to the remote server.
20419      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20420      * object into a block of Roo.data.Records.
20421      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20422      * The function must be passed <ul>
20423      * <li>The Record block object</li>
20424      * <li>The "arg" argument from the load function</li>
20425      * <li>A boolean success indicator</li>
20426      * </ul>
20427      * @param {Object} scope The scope in which to call the callback
20428      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20429      */
20430     load : function(params, reader, callback, scope, arg){
20431         if(this.fireEvent("beforeload", this, params) !== false){
20432
20433             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20434
20435             var url = this.url;
20436             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20437             if(this.nocache){
20438                 url += "&_dc=" + (new Date().getTime());
20439             }
20440             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20441             var trans = {
20442                 id : transId,
20443                 cb : "stcCallback"+transId,
20444                 scriptId : "stcScript"+transId,
20445                 params : params,
20446                 arg : arg,
20447                 url : url,
20448                 callback : callback,
20449                 scope : scope,
20450                 reader : reader
20451             };
20452             var conn = this;
20453
20454             window[trans.cb] = function(o){
20455                 conn.handleResponse(o, trans);
20456             };
20457
20458             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20459
20460             if(this.autoAbort !== false){
20461                 this.abort();
20462             }
20463
20464             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20465
20466             var script = document.createElement("script");
20467             script.setAttribute("src", url);
20468             script.setAttribute("type", "text/javascript");
20469             script.setAttribute("id", trans.scriptId);
20470             this.head.appendChild(script);
20471
20472             this.trans = trans;
20473         }else{
20474             callback.call(scope||this, null, arg, false);
20475         }
20476     },
20477
20478     // private
20479     isLoading : function(){
20480         return this.trans ? true : false;
20481     },
20482
20483     /**
20484      * Abort the current server request.
20485      */
20486     abort : function(){
20487         if(this.isLoading()){
20488             this.destroyTrans(this.trans);
20489         }
20490     },
20491
20492     // private
20493     destroyTrans : function(trans, isLoaded){
20494         this.head.removeChild(document.getElementById(trans.scriptId));
20495         clearTimeout(trans.timeoutId);
20496         if(isLoaded){
20497             window[trans.cb] = undefined;
20498             try{
20499                 delete window[trans.cb];
20500             }catch(e){}
20501         }else{
20502             // if hasn't been loaded, wait for load to remove it to prevent script error
20503             window[trans.cb] = function(){
20504                 window[trans.cb] = undefined;
20505                 try{
20506                     delete window[trans.cb];
20507                 }catch(e){}
20508             };
20509         }
20510     },
20511
20512     // private
20513     handleResponse : function(o, trans){
20514         this.trans = false;
20515         this.destroyTrans(trans, true);
20516         var result;
20517         try {
20518             result = trans.reader.readRecords(o);
20519         }catch(e){
20520             this.fireEvent("loadexception", this, o, trans.arg, e);
20521             trans.callback.call(trans.scope||window, null, trans.arg, false);
20522             return;
20523         }
20524         this.fireEvent("load", this, o, trans.arg);
20525         trans.callback.call(trans.scope||window, result, trans.arg, true);
20526     },
20527
20528     // private
20529     handleFailure : function(trans){
20530         this.trans = false;
20531         this.destroyTrans(trans, false);
20532         this.fireEvent("loadexception", this, null, trans.arg);
20533         trans.callback.call(trans.scope||window, null, trans.arg, false);
20534     }
20535 });/*
20536  * Based on:
20537  * Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  *
20540  * Originally Released Under LGPL - original licence link has changed is not relivant.
20541  *
20542  * Fork - LGPL
20543  * <script type="text/javascript">
20544  */
20545
20546 /**
20547  * @class Roo.data.JsonReader
20548  * @extends Roo.data.DataReader
20549  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20550  * based on mappings in a provided Roo.data.Record constructor.
20551  * 
20552  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20553  * in the reply previously. 
20554  * 
20555  * <p>
20556  * Example code:
20557  * <pre><code>
20558 var RecordDef = Roo.data.Record.create([
20559     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20560     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20561 ]);
20562 var myReader = new Roo.data.JsonReader({
20563     totalProperty: "results",    // The property which contains the total dataset size (optional)
20564     root: "rows",                // The property which contains an Array of row objects
20565     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20566 }, RecordDef);
20567 </code></pre>
20568  * <p>
20569  * This would consume a JSON file like this:
20570  * <pre><code>
20571 { 'results': 2, 'rows': [
20572     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20573     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20574 }
20575 </code></pre>
20576  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20577  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20578  * paged from the remote server.
20579  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20580  * @cfg {String} root name of the property which contains the Array of row objects.
20581  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20582  * @constructor
20583  * Create a new JsonReader
20584  * @param {Object} meta Metadata configuration options
20585  * @param {Object} recordType Either an Array of field definition objects,
20586  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20587  */
20588 Roo.data.JsonReader = function(meta, recordType){
20589     
20590     meta = meta || {};
20591     // set some defaults:
20592     Roo.applyIf(meta, {
20593         totalProperty: 'total',
20594         successProperty : 'success',
20595         root : 'data',
20596         id : 'id'
20597     });
20598     
20599     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20600 };
20601 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20602     
20603     /**
20604      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20605      * Used by Store query builder to append _requestMeta to params.
20606      * 
20607      */
20608     metaFromRemote : false,
20609     /**
20610      * This method is only used by a DataProxy which has retrieved data from a remote server.
20611      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20612      * @return {Object} data A data block which is used by an Roo.data.Store object as
20613      * a cache of Roo.data.Records.
20614      */
20615     read : function(response){
20616         var json = response.responseText;
20617        
20618         var o = /* eval:var:o */ eval("("+json+")");
20619         if(!o) {
20620             throw {message: "JsonReader.read: Json object not found"};
20621         }
20622         
20623         if(o.metaData){
20624             
20625             delete this.ef;
20626             this.metaFromRemote = true;
20627             this.meta = o.metaData;
20628             this.recordType = Roo.data.Record.create(o.metaData.fields);
20629             this.onMetaChange(this.meta, this.recordType, o);
20630         }
20631         return this.readRecords(o);
20632     },
20633
20634     // private function a store will implement
20635     onMetaChange : function(meta, recordType, o){
20636
20637     },
20638
20639     /**
20640          * @ignore
20641          */
20642     simpleAccess: function(obj, subsc) {
20643         return obj[subsc];
20644     },
20645
20646         /**
20647          * @ignore
20648          */
20649     getJsonAccessor: function(){
20650         var re = /[\[\.]/;
20651         return function(expr) {
20652             try {
20653                 return(re.test(expr))
20654                     ? new Function("obj", "return obj." + expr)
20655                     : function(obj){
20656                         return obj[expr];
20657                     };
20658             } catch(e){}
20659             return Roo.emptyFn;
20660         };
20661     }(),
20662
20663     /**
20664      * Create a data block containing Roo.data.Records from an XML document.
20665      * @param {Object} o An object which contains an Array of row objects in the property specified
20666      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20667      * which contains the total size of the dataset.
20668      * @return {Object} data A data block which is used by an Roo.data.Store object as
20669      * a cache of Roo.data.Records.
20670      */
20671     readRecords : function(o){
20672         /**
20673          * After any data loads, the raw JSON data is available for further custom processing.
20674          * @type Object
20675          */
20676         this.jsonData = o;
20677         var s = this.meta, Record = this.recordType,
20678             f = Record.prototype.fields, fi = f.items, fl = f.length;
20679
20680 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20681         if (!this.ef) {
20682             if(s.totalProperty) {
20683                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20684                 }
20685                 if(s.successProperty) {
20686                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20687                 }
20688                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20689                 if (s.id) {
20690                         var g = this.getJsonAccessor(s.id);
20691                         this.getId = function(rec) {
20692                                 var r = g(rec);
20693                                 return (r === undefined || r === "") ? null : r;
20694                         };
20695                 } else {
20696                         this.getId = function(){return null;};
20697                 }
20698             this.ef = [];
20699             for(var jj = 0; jj < fl; jj++){
20700                 f = fi[jj];
20701                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20702                 this.ef[jj] = this.getJsonAccessor(map);
20703             }
20704         }
20705
20706         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20707         if(s.totalProperty){
20708             var vt = parseInt(this.getTotal(o), 10);
20709             if(!isNaN(vt)){
20710                 totalRecords = vt;
20711             }
20712         }
20713         if(s.successProperty){
20714             var vs = this.getSuccess(o);
20715             if(vs === false || vs === 'false'){
20716                 success = false;
20717             }
20718         }
20719         var records = [];
20720             for(var i = 0; i < c; i++){
20721                     var n = root[i];
20722                 var values = {};
20723                 var id = this.getId(n);
20724                 for(var j = 0; j < fl; j++){
20725                     f = fi[j];
20726                 var v = this.ef[j](n);
20727                 if (!f.convert) {
20728                     Roo.log('missing convert for ' + f.name);
20729                     Roo.log(f);
20730                     continue;
20731                 }
20732                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20733                 }
20734                 var record = new Record(values, id);
20735                 record.json = n;
20736                 records[i] = record;
20737             }
20738             return {
20739                 success : success,
20740                 records : records,
20741                 totalRecords : totalRecords
20742             };
20743     }
20744 });/*
20745  * Based on:
20746  * Ext JS Library 1.1.1
20747  * Copyright(c) 2006-2007, Ext JS, LLC.
20748  *
20749  * Originally Released Under LGPL - original licence link has changed is not relivant.
20750  *
20751  * Fork - LGPL
20752  * <script type="text/javascript">
20753  */
20754
20755 /**
20756  * @class Roo.data.XmlReader
20757  * @extends Roo.data.DataReader
20758  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20759  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20760  * <p>
20761  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20762  * header in the HTTP response must be set to "text/xml".</em>
20763  * <p>
20764  * Example code:
20765  * <pre><code>
20766 var RecordDef = Roo.data.Record.create([
20767    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20768    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20769 ]);
20770 var myReader = new Roo.data.XmlReader({
20771    totalRecords: "results", // The element which contains the total dataset size (optional)
20772    record: "row",           // The repeated element which contains row information
20773    id: "id"                 // The element within the row that provides an ID for the record (optional)
20774 }, RecordDef);
20775 </code></pre>
20776  * <p>
20777  * This would consume an XML file like this:
20778  * <pre><code>
20779 &lt;?xml?>
20780 &lt;dataset>
20781  &lt;results>2&lt;/results>
20782  &lt;row>
20783    &lt;id>1&lt;/id>
20784    &lt;name>Bill&lt;/name>
20785    &lt;occupation>Gardener&lt;/occupation>
20786  &lt;/row>
20787  &lt;row>
20788    &lt;id>2&lt;/id>
20789    &lt;name>Ben&lt;/name>
20790    &lt;occupation>Horticulturalist&lt;/occupation>
20791  &lt;/row>
20792 &lt;/dataset>
20793 </code></pre>
20794  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20795  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20796  * paged from the remote server.
20797  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20798  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20799  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20800  * a record identifier value.
20801  * @constructor
20802  * Create a new XmlReader
20803  * @param {Object} meta Metadata configuration options
20804  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20805  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20806  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20807  */
20808 Roo.data.XmlReader = function(meta, recordType){
20809     meta = meta || {};
20810     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20811 };
20812 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20813     /**
20814      * This method is only used by a DataProxy which has retrieved data from a remote server.
20815          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20816          * to contain a method called 'responseXML' that returns an XML document object.
20817      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20818      * a cache of Roo.data.Records.
20819      */
20820     read : function(response){
20821         var doc = response.responseXML;
20822         if(!doc) {
20823             throw {message: "XmlReader.read: XML Document not available"};
20824         }
20825         return this.readRecords(doc);
20826     },
20827
20828     /**
20829      * Create a data block containing Roo.data.Records from an XML document.
20830          * @param {Object} doc A parsed XML document.
20831      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20832      * a cache of Roo.data.Records.
20833      */
20834     readRecords : function(doc){
20835         /**
20836          * After any data loads/reads, the raw XML Document is available for further custom processing.
20837          * @type XMLDocument
20838          */
20839         this.xmlData = doc;
20840         var root = doc.documentElement || doc;
20841         var q = Roo.DomQuery;
20842         var recordType = this.recordType, fields = recordType.prototype.fields;
20843         var sid = this.meta.id;
20844         var totalRecords = 0, success = true;
20845         if(this.meta.totalRecords){
20846             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20847         }
20848         
20849         if(this.meta.success){
20850             var sv = q.selectValue(this.meta.success, root, true);
20851             success = sv !== false && sv !== 'false';
20852         }
20853         var records = [];
20854         var ns = q.select(this.meta.record, root);
20855         for(var i = 0, len = ns.length; i < len; i++) {
20856                 var n = ns[i];
20857                 var values = {};
20858                 var id = sid ? q.selectValue(sid, n) : undefined;
20859                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20860                     var f = fields.items[j];
20861                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20862                     v = f.convert(v);
20863                     values[f.name] = v;
20864                 }
20865                 var record = new recordType(values, id);
20866                 record.node = n;
20867                 records[records.length] = record;
20868             }
20869
20870             return {
20871                 success : success,
20872                 records : records,
20873                 totalRecords : totalRecords || records.length
20874             };
20875     }
20876 });/*
20877  * Based on:
20878  * Ext JS Library 1.1.1
20879  * Copyright(c) 2006-2007, Ext JS, LLC.
20880  *
20881  * Originally Released Under LGPL - original licence link has changed is not relivant.
20882  *
20883  * Fork - LGPL
20884  * <script type="text/javascript">
20885  */
20886
20887 /**
20888  * @class Roo.data.ArrayReader
20889  * @extends Roo.data.DataReader
20890  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20891  * Each element of that Array represents a row of data fields. The
20892  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20893  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20894  * <p>
20895  * Example code:.
20896  * <pre><code>
20897 var RecordDef = Roo.data.Record.create([
20898     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20899     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20900 ]);
20901 var myReader = new Roo.data.ArrayReader({
20902     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20903 }, RecordDef);
20904 </code></pre>
20905  * <p>
20906  * This would consume an Array like this:
20907  * <pre><code>
20908 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20909   </code></pre>
20910  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20911  * @constructor
20912  * Create a new JsonReader
20913  * @param {Object} meta Metadata configuration options.
20914  * @param {Object} recordType Either an Array of field definition objects
20915  * as specified to {@link Roo.data.Record#create},
20916  * or an {@link Roo.data.Record} object
20917  * created using {@link Roo.data.Record#create}.
20918  */
20919 Roo.data.ArrayReader = function(meta, recordType){
20920     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20921 };
20922
20923 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20924     /**
20925      * Create a data block containing Roo.data.Records from an XML document.
20926      * @param {Object} o An Array of row objects which represents the dataset.
20927      * @return {Object} data A data block which is used by an Roo.data.Store object as
20928      * a cache of Roo.data.Records.
20929      */
20930     readRecords : function(o){
20931         var sid = this.meta ? this.meta.id : null;
20932         var recordType = this.recordType, fields = recordType.prototype.fields;
20933         var records = [];
20934         var root = o;
20935             for(var i = 0; i < root.length; i++){
20936                     var n = root[i];
20937                 var values = {};
20938                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20939                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20940                 var f = fields.items[j];
20941                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20942                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20943                 v = f.convert(v);
20944                 values[f.name] = v;
20945             }
20946                 var record = new recordType(values, id);
20947                 record.json = n;
20948                 records[records.length] = record;
20949             }
20950             return {
20951                 records : records,
20952                 totalRecords : records.length
20953             };
20954     }
20955 });/*
20956  * Based on:
20957  * Ext JS Library 1.1.1
20958  * Copyright(c) 2006-2007, Ext JS, LLC.
20959  *
20960  * Originally Released Under LGPL - original licence link has changed is not relivant.
20961  *
20962  * Fork - LGPL
20963  * <script type="text/javascript">
20964  */
20965
20966
20967 /**
20968  * @class Roo.data.Tree
20969  * @extends Roo.util.Observable
20970  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20971  * in the tree have most standard DOM functionality.
20972  * @constructor
20973  * @param {Node} root (optional) The root node
20974  */
20975 Roo.data.Tree = function(root){
20976    this.nodeHash = {};
20977    /**
20978     * The root node for this tree
20979     * @type Node
20980     */
20981    this.root = null;
20982    if(root){
20983        this.setRootNode(root);
20984    }
20985    this.addEvents({
20986        /**
20987         * @event append
20988         * Fires when a new child node is appended to a node in this tree.
20989         * @param {Tree} tree The owner tree
20990         * @param {Node} parent The parent node
20991         * @param {Node} node The newly appended node
20992         * @param {Number} index The index of the newly appended node
20993         */
20994        "append" : true,
20995        /**
20996         * @event remove
20997         * Fires when a child node is removed from a node in this tree.
20998         * @param {Tree} tree The owner tree
20999         * @param {Node} parent The parent node
21000         * @param {Node} node The child node removed
21001         */
21002        "remove" : true,
21003        /**
21004         * @event move
21005         * Fires when a node is moved to a new location in the tree
21006         * @param {Tree} tree The owner tree
21007         * @param {Node} node The node moved
21008         * @param {Node} oldParent The old parent of this node
21009         * @param {Node} newParent The new parent of this node
21010         * @param {Number} index The index it was moved to
21011         */
21012        "move" : true,
21013        /**
21014         * @event insert
21015         * Fires when a new child node is inserted in a node in this tree.
21016         * @param {Tree} tree The owner tree
21017         * @param {Node} parent The parent node
21018         * @param {Node} node The child node inserted
21019         * @param {Node} refNode The child node the node was inserted before
21020         */
21021        "insert" : true,
21022        /**
21023         * @event beforeappend
21024         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21025         * @param {Tree} tree The owner tree
21026         * @param {Node} parent The parent node
21027         * @param {Node} node The child node to be appended
21028         */
21029        "beforeappend" : true,
21030        /**
21031         * @event beforeremove
21032         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21033         * @param {Tree} tree The owner tree
21034         * @param {Node} parent The parent node
21035         * @param {Node} node The child node to be removed
21036         */
21037        "beforeremove" : true,
21038        /**
21039         * @event beforemove
21040         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21041         * @param {Tree} tree The owner tree
21042         * @param {Node} node The node being moved
21043         * @param {Node} oldParent The parent of the node
21044         * @param {Node} newParent The new parent the node is moving to
21045         * @param {Number} index The index it is being moved to
21046         */
21047        "beforemove" : true,
21048        /**
21049         * @event beforeinsert
21050         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node to be inserted
21054         * @param {Node} refNode The child node the node is being inserted before
21055         */
21056        "beforeinsert" : true
21057    });
21058
21059     Roo.data.Tree.superclass.constructor.call(this);
21060 };
21061
21062 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21063     pathSeparator: "/",
21064
21065     proxyNodeEvent : function(){
21066         return this.fireEvent.apply(this, arguments);
21067     },
21068
21069     /**
21070      * Returns the root node for this tree.
21071      * @return {Node}
21072      */
21073     getRootNode : function(){
21074         return this.root;
21075     },
21076
21077     /**
21078      * Sets the root node for this tree.
21079      * @param {Node} node
21080      * @return {Node}
21081      */
21082     setRootNode : function(node){
21083         this.root = node;
21084         node.ownerTree = this;
21085         node.isRoot = true;
21086         this.registerNode(node);
21087         return node;
21088     },
21089
21090     /**
21091      * Gets a node in this tree by its id.
21092      * @param {String} id
21093      * @return {Node}
21094      */
21095     getNodeById : function(id){
21096         return this.nodeHash[id];
21097     },
21098
21099     registerNode : function(node){
21100         this.nodeHash[node.id] = node;
21101     },
21102
21103     unregisterNode : function(node){
21104         delete this.nodeHash[node.id];
21105     },
21106
21107     toString : function(){
21108         return "[Tree"+(this.id?" "+this.id:"")+"]";
21109     }
21110 });
21111
21112 /**
21113  * @class Roo.data.Node
21114  * @extends Roo.util.Observable
21115  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21116  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21117  * @constructor
21118  * @param {Object} attributes The attributes/config for the node
21119  */
21120 Roo.data.Node = function(attributes){
21121     /**
21122      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21123      * @type {Object}
21124      */
21125     this.attributes = attributes || {};
21126     this.leaf = this.attributes.leaf;
21127     /**
21128      * The node id. @type String
21129      */
21130     this.id = this.attributes.id;
21131     if(!this.id){
21132         this.id = Roo.id(null, "ynode-");
21133         this.attributes.id = this.id;
21134     }
21135     /**
21136      * All child nodes of this node. @type Array
21137      */
21138     this.childNodes = [];
21139     if(!this.childNodes.indexOf){ // indexOf is a must
21140         this.childNodes.indexOf = function(o){
21141             for(var i = 0, len = this.length; i < len; i++){
21142                 if(this[i] == o) {
21143                     return i;
21144                 }
21145             }
21146             return -1;
21147         };
21148     }
21149     /**
21150      * The parent node for this node. @type Node
21151      */
21152     this.parentNode = null;
21153     /**
21154      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21155      */
21156     this.firstChild = null;
21157     /**
21158      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21159      */
21160     this.lastChild = null;
21161     /**
21162      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21163      */
21164     this.previousSibling = null;
21165     /**
21166      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21167      */
21168     this.nextSibling = null;
21169
21170     this.addEvents({
21171        /**
21172         * @event append
21173         * Fires when a new child node is appended
21174         * @param {Tree} tree The owner tree
21175         * @param {Node} this This node
21176         * @param {Node} node The newly appended node
21177         * @param {Number} index The index of the newly appended node
21178         */
21179        "append" : true,
21180        /**
21181         * @event remove
21182         * Fires when a child node is removed
21183         * @param {Tree} tree The owner tree
21184         * @param {Node} this This node
21185         * @param {Node} node The removed node
21186         */
21187        "remove" : true,
21188        /**
21189         * @event move
21190         * Fires when this node is moved to a new location in the tree
21191         * @param {Tree} tree The owner tree
21192         * @param {Node} this This node
21193         * @param {Node} oldParent The old parent of this node
21194         * @param {Node} newParent The new parent of this node
21195         * @param {Number} index The index it was moved to
21196         */
21197        "move" : true,
21198        /**
21199         * @event insert
21200         * Fires when a new child node is inserted.
21201         * @param {Tree} tree The owner tree
21202         * @param {Node} this This node
21203         * @param {Node} node The child node inserted
21204         * @param {Node} refNode The child node the node was inserted before
21205         */
21206        "insert" : true,
21207        /**
21208         * @event beforeappend
21209         * Fires before a new child is appended, return false to cancel the append.
21210         * @param {Tree} tree The owner tree
21211         * @param {Node} this This node
21212         * @param {Node} node The child node to be appended
21213         */
21214        "beforeappend" : true,
21215        /**
21216         * @event beforeremove
21217         * Fires before a child is removed, return false to cancel the remove.
21218         * @param {Tree} tree The owner tree
21219         * @param {Node} this This node
21220         * @param {Node} node The child node to be removed
21221         */
21222        "beforeremove" : true,
21223        /**
21224         * @event beforemove
21225         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21226         * @param {Tree} tree The owner tree
21227         * @param {Node} this This node
21228         * @param {Node} oldParent The parent of this node
21229         * @param {Node} newParent The new parent this node is moving to
21230         * @param {Number} index The index it is being moved to
21231         */
21232        "beforemove" : true,
21233        /**
21234         * @event beforeinsert
21235         * Fires before a new child is inserted, return false to cancel the insert.
21236         * @param {Tree} tree The owner tree
21237         * @param {Node} this This node
21238         * @param {Node} node The child node to be inserted
21239         * @param {Node} refNode The child node the node is being inserted before
21240         */
21241        "beforeinsert" : true
21242    });
21243     this.listeners = this.attributes.listeners;
21244     Roo.data.Node.superclass.constructor.call(this);
21245 };
21246
21247 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21248     fireEvent : function(evtName){
21249         // first do standard event for this node
21250         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21251             return false;
21252         }
21253         // then bubble it up to the tree if the event wasn't cancelled
21254         var ot = this.getOwnerTree();
21255         if(ot){
21256             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21257                 return false;
21258             }
21259         }
21260         return true;
21261     },
21262
21263     /**
21264      * Returns true if this node is a leaf
21265      * @return {Boolean}
21266      */
21267     isLeaf : function(){
21268         return this.leaf === true;
21269     },
21270
21271     // private
21272     setFirstChild : function(node){
21273         this.firstChild = node;
21274     },
21275
21276     //private
21277     setLastChild : function(node){
21278         this.lastChild = node;
21279     },
21280
21281
21282     /**
21283      * Returns true if this node is the last child of its parent
21284      * @return {Boolean}
21285      */
21286     isLast : function(){
21287        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21288     },
21289
21290     /**
21291      * Returns true if this node is the first child of its parent
21292      * @return {Boolean}
21293      */
21294     isFirst : function(){
21295        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21296     },
21297
21298     hasChildNodes : function(){
21299         return !this.isLeaf() && this.childNodes.length > 0;
21300     },
21301
21302     /**
21303      * Insert node(s) as the last child node of this node.
21304      * @param {Node/Array} node The node or Array of nodes to append
21305      * @return {Node} The appended node if single append, or null if an array was passed
21306      */
21307     appendChild : function(node){
21308         var multi = false;
21309         if(node instanceof Array){
21310             multi = node;
21311         }else if(arguments.length > 1){
21312             multi = arguments;
21313         }
21314         // if passed an array or multiple args do them one by one
21315         if(multi){
21316             for(var i = 0, len = multi.length; i < len; i++) {
21317                 this.appendChild(multi[i]);
21318             }
21319         }else{
21320             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21321                 return false;
21322             }
21323             var index = this.childNodes.length;
21324             var oldParent = node.parentNode;
21325             // it's a move, make sure we move it cleanly
21326             if(oldParent){
21327                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21328                     return false;
21329                 }
21330                 oldParent.removeChild(node);
21331             }
21332             index = this.childNodes.length;
21333             if(index == 0){
21334                 this.setFirstChild(node);
21335             }
21336             this.childNodes.push(node);
21337             node.parentNode = this;
21338             var ps = this.childNodes[index-1];
21339             if(ps){
21340                 node.previousSibling = ps;
21341                 ps.nextSibling = node;
21342             }else{
21343                 node.previousSibling = null;
21344             }
21345             node.nextSibling = null;
21346             this.setLastChild(node);
21347             node.setOwnerTree(this.getOwnerTree());
21348             this.fireEvent("append", this.ownerTree, this, node, index);
21349             if(oldParent){
21350                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21351             }
21352             return node;
21353         }
21354     },
21355
21356     /**
21357      * Removes a child node from this node.
21358      * @param {Node} node The node to remove
21359      * @return {Node} The removed node
21360      */
21361     removeChild : function(node){
21362         var index = this.childNodes.indexOf(node);
21363         if(index == -1){
21364             return false;
21365         }
21366         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21367             return false;
21368         }
21369
21370         // remove it from childNodes collection
21371         this.childNodes.splice(index, 1);
21372
21373         // update siblings
21374         if(node.previousSibling){
21375             node.previousSibling.nextSibling = node.nextSibling;
21376         }
21377         if(node.nextSibling){
21378             node.nextSibling.previousSibling = node.previousSibling;
21379         }
21380
21381         // update child refs
21382         if(this.firstChild == node){
21383             this.setFirstChild(node.nextSibling);
21384         }
21385         if(this.lastChild == node){
21386             this.setLastChild(node.previousSibling);
21387         }
21388
21389         node.setOwnerTree(null);
21390         // clear any references from the node
21391         node.parentNode = null;
21392         node.previousSibling = null;
21393         node.nextSibling = null;
21394         this.fireEvent("remove", this.ownerTree, this, node);
21395         return node;
21396     },
21397
21398     /**
21399      * Inserts the first node before the second node in this nodes childNodes collection.
21400      * @param {Node} node The node to insert
21401      * @param {Node} refNode The node to insert before (if null the node is appended)
21402      * @return {Node} The inserted node
21403      */
21404     insertBefore : function(node, refNode){
21405         if(!refNode){ // like standard Dom, refNode can be null for append
21406             return this.appendChild(node);
21407         }
21408         // nothing to do
21409         if(node == refNode){
21410             return false;
21411         }
21412
21413         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21414             return false;
21415         }
21416         var index = this.childNodes.indexOf(refNode);
21417         var oldParent = node.parentNode;
21418         var refIndex = index;
21419
21420         // when moving internally, indexes will change after remove
21421         if(oldParent == this && this.childNodes.indexOf(node) < index){
21422             refIndex--;
21423         }
21424
21425         // it's a move, make sure we move it cleanly
21426         if(oldParent){
21427             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21428                 return false;
21429             }
21430             oldParent.removeChild(node);
21431         }
21432         if(refIndex == 0){
21433             this.setFirstChild(node);
21434         }
21435         this.childNodes.splice(refIndex, 0, node);
21436         node.parentNode = this;
21437         var ps = this.childNodes[refIndex-1];
21438         if(ps){
21439             node.previousSibling = ps;
21440             ps.nextSibling = node;
21441         }else{
21442             node.previousSibling = null;
21443         }
21444         node.nextSibling = refNode;
21445         refNode.previousSibling = node;
21446         node.setOwnerTree(this.getOwnerTree());
21447         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21448         if(oldParent){
21449             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21450         }
21451         return node;
21452     },
21453
21454     /**
21455      * Returns the child node at the specified index.
21456      * @param {Number} index
21457      * @return {Node}
21458      */
21459     item : function(index){
21460         return this.childNodes[index];
21461     },
21462
21463     /**
21464      * Replaces one child node in this node with another.
21465      * @param {Node} newChild The replacement node
21466      * @param {Node} oldChild The node to replace
21467      * @return {Node} The replaced node
21468      */
21469     replaceChild : function(newChild, oldChild){
21470         this.insertBefore(newChild, oldChild);
21471         this.removeChild(oldChild);
21472         return oldChild;
21473     },
21474
21475     /**
21476      * Returns the index of a child node
21477      * @param {Node} node
21478      * @return {Number} The index of the node or -1 if it was not found
21479      */
21480     indexOf : function(child){
21481         return this.childNodes.indexOf(child);
21482     },
21483
21484     /**
21485      * Returns the tree this node is in.
21486      * @return {Tree}
21487      */
21488     getOwnerTree : function(){
21489         // if it doesn't have one, look for one
21490         if(!this.ownerTree){
21491             var p = this;
21492             while(p){
21493                 if(p.ownerTree){
21494                     this.ownerTree = p.ownerTree;
21495                     break;
21496                 }
21497                 p = p.parentNode;
21498             }
21499         }
21500         return this.ownerTree;
21501     },
21502
21503     /**
21504      * Returns depth of this node (the root node has a depth of 0)
21505      * @return {Number}
21506      */
21507     getDepth : function(){
21508         var depth = 0;
21509         var p = this;
21510         while(p.parentNode){
21511             ++depth;
21512             p = p.parentNode;
21513         }
21514         return depth;
21515     },
21516
21517     // private
21518     setOwnerTree : function(tree){
21519         // if it's move, we need to update everyone
21520         if(tree != this.ownerTree){
21521             if(this.ownerTree){
21522                 this.ownerTree.unregisterNode(this);
21523             }
21524             this.ownerTree = tree;
21525             var cs = this.childNodes;
21526             for(var i = 0, len = cs.length; i < len; i++) {
21527                 cs[i].setOwnerTree(tree);
21528             }
21529             if(tree){
21530                 tree.registerNode(this);
21531             }
21532         }
21533     },
21534
21535     /**
21536      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21537      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21538      * @return {String} The path
21539      */
21540     getPath : function(attr){
21541         attr = attr || "id";
21542         var p = this.parentNode;
21543         var b = [this.attributes[attr]];
21544         while(p){
21545             b.unshift(p.attributes[attr]);
21546             p = p.parentNode;
21547         }
21548         var sep = this.getOwnerTree().pathSeparator;
21549         return sep + b.join(sep);
21550     },
21551
21552     /**
21553      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21554      * function call will be the scope provided or the current node. The arguments to the function
21555      * will be the args provided or the current node. If the function returns false at any point,
21556      * the bubble is stopped.
21557      * @param {Function} fn The function to call
21558      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21559      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21560      */
21561     bubble : function(fn, scope, args){
21562         var p = this;
21563         while(p){
21564             if(fn.call(scope || p, args || p) === false){
21565                 break;
21566             }
21567             p = p.parentNode;
21568         }
21569     },
21570
21571     /**
21572      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21573      * function call will be the scope provided or the current node. The arguments to the function
21574      * will be the args provided or the current node. If the function returns false at any point,
21575      * the cascade is stopped on that branch.
21576      * @param {Function} fn The function to call
21577      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21578      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21579      */
21580     cascade : function(fn, scope, args){
21581         if(fn.call(scope || this, args || this) !== false){
21582             var cs = this.childNodes;
21583             for(var i = 0, len = cs.length; i < len; i++) {
21584                 cs[i].cascade(fn, scope, args);
21585             }
21586         }
21587     },
21588
21589     /**
21590      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21591      * function call will be the scope provided or the current node. The arguments to the function
21592      * will be the args provided or the current node. If the function returns false at any point,
21593      * the iteration stops.
21594      * @param {Function} fn The function to call
21595      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21596      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21597      */
21598     eachChild : function(fn, scope, args){
21599         var cs = this.childNodes;
21600         for(var i = 0, len = cs.length; i < len; i++) {
21601                 if(fn.call(scope || this, args || cs[i]) === false){
21602                     break;
21603                 }
21604         }
21605     },
21606
21607     /**
21608      * Finds the first child that has the attribute with the specified value.
21609      * @param {String} attribute The attribute name
21610      * @param {Mixed} value The value to search for
21611      * @return {Node} The found child or null if none was found
21612      */
21613     findChild : function(attribute, value){
21614         var cs = this.childNodes;
21615         for(var i = 0, len = cs.length; i < len; i++) {
21616                 if(cs[i].attributes[attribute] == value){
21617                     return cs[i];
21618                 }
21619         }
21620         return null;
21621     },
21622
21623     /**
21624      * Finds the first child by a custom function. The child matches if the function passed
21625      * returns true.
21626      * @param {Function} fn
21627      * @param {Object} scope (optional)
21628      * @return {Node} The found child or null if none was found
21629      */
21630     findChildBy : function(fn, scope){
21631         var cs = this.childNodes;
21632         for(var i = 0, len = cs.length; i < len; i++) {
21633                 if(fn.call(scope||cs[i], cs[i]) === true){
21634                     return cs[i];
21635                 }
21636         }
21637         return null;
21638     },
21639
21640     /**
21641      * Sorts this nodes children using the supplied sort function
21642      * @param {Function} fn
21643      * @param {Object} scope (optional)
21644      */
21645     sort : function(fn, scope){
21646         var cs = this.childNodes;
21647         var len = cs.length;
21648         if(len > 0){
21649             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21650             cs.sort(sortFn);
21651             for(var i = 0; i < len; i++){
21652                 var n = cs[i];
21653                 n.previousSibling = cs[i-1];
21654                 n.nextSibling = cs[i+1];
21655                 if(i == 0){
21656                     this.setFirstChild(n);
21657                 }
21658                 if(i == len-1){
21659                     this.setLastChild(n);
21660                 }
21661             }
21662         }
21663     },
21664
21665     /**
21666      * Returns true if this node is an ancestor (at any point) of the passed node.
21667      * @param {Node} node
21668      * @return {Boolean}
21669      */
21670     contains : function(node){
21671         return node.isAncestor(this);
21672     },
21673
21674     /**
21675      * Returns true if the passed node is an ancestor (at any point) of this node.
21676      * @param {Node} node
21677      * @return {Boolean}
21678      */
21679     isAncestor : function(node){
21680         var p = this.parentNode;
21681         while(p){
21682             if(p == node){
21683                 return true;
21684             }
21685             p = p.parentNode;
21686         }
21687         return false;
21688     },
21689
21690     toString : function(){
21691         return "[Node"+(this.id?" "+this.id:"")+"]";
21692     }
21693 });/*
21694  * Based on:
21695  * Ext JS Library 1.1.1
21696  * Copyright(c) 2006-2007, Ext JS, LLC.
21697  *
21698  * Originally Released Under LGPL - original licence link has changed is not relivant.
21699  *
21700  * Fork - LGPL
21701  * <script type="text/javascript">
21702  */
21703  
21704
21705 /**
21706  * @class Roo.ComponentMgr
21707  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21708  * @singleton
21709  */
21710 Roo.ComponentMgr = function(){
21711     var all = new Roo.util.MixedCollection();
21712
21713     return {
21714         /**
21715          * Registers a component.
21716          * @param {Roo.Component} c The component
21717          */
21718         register : function(c){
21719             all.add(c);
21720         },
21721
21722         /**
21723          * Unregisters a component.
21724          * @param {Roo.Component} c The component
21725          */
21726         unregister : function(c){
21727             all.remove(c);
21728         },
21729
21730         /**
21731          * Returns a component by id
21732          * @param {String} id The component id
21733          */
21734         get : function(id){
21735             return all.get(id);
21736         },
21737
21738         /**
21739          * Registers a function that will be called when a specified component is added to ComponentMgr
21740          * @param {String} id The component id
21741          * @param {Funtction} fn The callback function
21742          * @param {Object} scope The scope of the callback
21743          */
21744         onAvailable : function(id, fn, scope){
21745             all.on("add", function(index, o){
21746                 if(o.id == id){
21747                     fn.call(scope || o, o);
21748                     all.un("add", fn, scope);
21749                 }
21750             });
21751         }
21752     };
21753 }();/*
21754  * Based on:
21755  * Ext JS Library 1.1.1
21756  * Copyright(c) 2006-2007, Ext JS, LLC.
21757  *
21758  * Originally Released Under LGPL - original licence link has changed is not relivant.
21759  *
21760  * Fork - LGPL
21761  * <script type="text/javascript">
21762  */
21763  
21764 /**
21765  * @class Roo.Component
21766  * @extends Roo.util.Observable
21767  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21768  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21769  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21770  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21771  * All visual components (widgets) that require rendering into a layout should subclass Component.
21772  * @constructor
21773  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21774  * 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
21775  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21776  */
21777 Roo.Component = function(config){
21778     config = config || {};
21779     if(config.tagName || config.dom || typeof config == "string"){ // element object
21780         config = {el: config, id: config.id || config};
21781     }
21782     this.initialConfig = config;
21783
21784     Roo.apply(this, config);
21785     this.addEvents({
21786         /**
21787          * @event disable
21788          * Fires after the component is disabled.
21789              * @param {Roo.Component} this
21790              */
21791         disable : true,
21792         /**
21793          * @event enable
21794          * Fires after the component is enabled.
21795              * @param {Roo.Component} this
21796              */
21797         enable : true,
21798         /**
21799          * @event beforeshow
21800          * Fires before the component is shown.  Return false to stop the show.
21801              * @param {Roo.Component} this
21802              */
21803         beforeshow : true,
21804         /**
21805          * @event show
21806          * Fires after the component is shown.
21807              * @param {Roo.Component} this
21808              */
21809         show : true,
21810         /**
21811          * @event beforehide
21812          * Fires before the component is hidden. Return false to stop the hide.
21813              * @param {Roo.Component} this
21814              */
21815         beforehide : true,
21816         /**
21817          * @event hide
21818          * Fires after the component is hidden.
21819              * @param {Roo.Component} this
21820              */
21821         hide : true,
21822         /**
21823          * @event beforerender
21824          * Fires before the component is rendered. Return false to stop the render.
21825              * @param {Roo.Component} this
21826              */
21827         beforerender : true,
21828         /**
21829          * @event render
21830          * Fires after the component is rendered.
21831              * @param {Roo.Component} this
21832              */
21833         render : true,
21834         /**
21835          * @event beforedestroy
21836          * Fires before the component is destroyed. Return false to stop the destroy.
21837              * @param {Roo.Component} this
21838              */
21839         beforedestroy : true,
21840         /**
21841          * @event destroy
21842          * Fires after the component is destroyed.
21843              * @param {Roo.Component} this
21844              */
21845         destroy : true
21846     });
21847     if(!this.id){
21848         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21849     }
21850     Roo.ComponentMgr.register(this);
21851     Roo.Component.superclass.constructor.call(this);
21852     this.initComponent();
21853     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21854         this.render(this.renderTo);
21855         delete this.renderTo;
21856     }
21857 };
21858
21859 /** @private */
21860 Roo.Component.AUTO_ID = 1000;
21861
21862 Roo.extend(Roo.Component, Roo.util.Observable, {
21863     /**
21864      * @scope Roo.Component.prototype
21865      * @type {Boolean}
21866      * true if this component is hidden. Read-only.
21867      */
21868     hidden : false,
21869     /**
21870      * @type {Boolean}
21871      * true if this component is disabled. Read-only.
21872      */
21873     disabled : false,
21874     /**
21875      * @type {Boolean}
21876      * true if this component has been rendered. Read-only.
21877      */
21878     rendered : false,
21879     
21880     /** @cfg {String} disableClass
21881      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21882      */
21883     disabledClass : "x-item-disabled",
21884         /** @cfg {Boolean} allowDomMove
21885          * Whether the component can move the Dom node when rendering (defaults to true).
21886          */
21887     allowDomMove : true,
21888     /** @cfg {String} hideMode
21889      * How this component should hidden. Supported values are
21890      * "visibility" (css visibility), "offsets" (negative offset position) and
21891      * "display" (css display) - defaults to "display".
21892      */
21893     hideMode: 'display',
21894
21895     /** @private */
21896     ctype : "Roo.Component",
21897
21898     /**
21899      * @cfg {String} actionMode 
21900      * which property holds the element that used for  hide() / show() / disable() / enable()
21901      * default is 'el' 
21902      */
21903     actionMode : "el",
21904
21905     /** @private */
21906     getActionEl : function(){
21907         return this[this.actionMode];
21908     },
21909
21910     initComponent : Roo.emptyFn,
21911     /**
21912      * If this is a lazy rendering component, render it to its container element.
21913      * @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.
21914      */
21915     render : function(container, position){
21916         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21917             if(!container && this.el){
21918                 this.el = Roo.get(this.el);
21919                 container = this.el.dom.parentNode;
21920                 this.allowDomMove = false;
21921             }
21922             this.container = Roo.get(container);
21923             this.rendered = true;
21924             if(position !== undefined){
21925                 if(typeof position == 'number'){
21926                     position = this.container.dom.childNodes[position];
21927                 }else{
21928                     position = Roo.getDom(position);
21929                 }
21930             }
21931             this.onRender(this.container, position || null);
21932             if(this.cls){
21933                 this.el.addClass(this.cls);
21934                 delete this.cls;
21935             }
21936             if(this.style){
21937                 this.el.applyStyles(this.style);
21938                 delete this.style;
21939             }
21940             this.fireEvent("render", this);
21941             this.afterRender(this.container);
21942             if(this.hidden){
21943                 this.hide();
21944             }
21945             if(this.disabled){
21946                 this.disable();
21947             }
21948         }
21949         return this;
21950     },
21951
21952     /** @private */
21953     // default function is not really useful
21954     onRender : function(ct, position){
21955         if(this.el){
21956             this.el = Roo.get(this.el);
21957             if(this.allowDomMove !== false){
21958                 ct.dom.insertBefore(this.el.dom, position);
21959             }
21960         }
21961     },
21962
21963     /** @private */
21964     getAutoCreate : function(){
21965         var cfg = typeof this.autoCreate == "object" ?
21966                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21967         if(this.id && !cfg.id){
21968             cfg.id = this.id;
21969         }
21970         return cfg;
21971     },
21972
21973     /** @private */
21974     afterRender : Roo.emptyFn,
21975
21976     /**
21977      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21978      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21979      */
21980     destroy : function(){
21981         if(this.fireEvent("beforedestroy", this) !== false){
21982             this.purgeListeners();
21983             this.beforeDestroy();
21984             if(this.rendered){
21985                 this.el.removeAllListeners();
21986                 this.el.remove();
21987                 if(this.actionMode == "container"){
21988                     this.container.remove();
21989                 }
21990             }
21991             this.onDestroy();
21992             Roo.ComponentMgr.unregister(this);
21993             this.fireEvent("destroy", this);
21994         }
21995     },
21996
21997         /** @private */
21998     beforeDestroy : function(){
21999
22000     },
22001
22002         /** @private */
22003         onDestroy : function(){
22004
22005     },
22006
22007     /**
22008      * Returns the underlying {@link Roo.Element}.
22009      * @return {Roo.Element} The element
22010      */
22011     getEl : function(){
22012         return this.el;
22013     },
22014
22015     /**
22016      * Returns the id of this component.
22017      * @return {String}
22018      */
22019     getId : function(){
22020         return this.id;
22021     },
22022
22023     /**
22024      * Try to focus this component.
22025      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22026      * @return {Roo.Component} this
22027      */
22028     focus : function(selectText){
22029         if(this.rendered){
22030             this.el.focus();
22031             if(selectText === true){
22032                 this.el.dom.select();
22033             }
22034         }
22035         return this;
22036     },
22037
22038     /** @private */
22039     blur : function(){
22040         if(this.rendered){
22041             this.el.blur();
22042         }
22043         return this;
22044     },
22045
22046     /**
22047      * Disable this component.
22048      * @return {Roo.Component} this
22049      */
22050     disable : function(){
22051         if(this.rendered){
22052             this.onDisable();
22053         }
22054         this.disabled = true;
22055         this.fireEvent("disable", this);
22056         return this;
22057     },
22058
22059         // private
22060     onDisable : function(){
22061         this.getActionEl().addClass(this.disabledClass);
22062         this.el.dom.disabled = true;
22063     },
22064
22065     /**
22066      * Enable this component.
22067      * @return {Roo.Component} this
22068      */
22069     enable : function(){
22070         if(this.rendered){
22071             this.onEnable();
22072         }
22073         this.disabled = false;
22074         this.fireEvent("enable", this);
22075         return this;
22076     },
22077
22078         // private
22079     onEnable : function(){
22080         this.getActionEl().removeClass(this.disabledClass);
22081         this.el.dom.disabled = false;
22082     },
22083
22084     /**
22085      * Convenience function for setting disabled/enabled by boolean.
22086      * @param {Boolean} disabled
22087      */
22088     setDisabled : function(disabled){
22089         this[disabled ? "disable" : "enable"]();
22090     },
22091
22092     /**
22093      * Show this component.
22094      * @return {Roo.Component} this
22095      */
22096     show: function(){
22097         if(this.fireEvent("beforeshow", this) !== false){
22098             this.hidden = false;
22099             if(this.rendered){
22100                 this.onShow();
22101             }
22102             this.fireEvent("show", this);
22103         }
22104         return this;
22105     },
22106
22107     // private
22108     onShow : function(){
22109         var ae = this.getActionEl();
22110         if(this.hideMode == 'visibility'){
22111             ae.dom.style.visibility = "visible";
22112         }else if(this.hideMode == 'offsets'){
22113             ae.removeClass('x-hidden');
22114         }else{
22115             ae.dom.style.display = "";
22116         }
22117     },
22118
22119     /**
22120      * Hide this component.
22121      * @return {Roo.Component} this
22122      */
22123     hide: function(){
22124         if(this.fireEvent("beforehide", this) !== false){
22125             this.hidden = true;
22126             if(this.rendered){
22127                 this.onHide();
22128             }
22129             this.fireEvent("hide", this);
22130         }
22131         return this;
22132     },
22133
22134     // private
22135     onHide : function(){
22136         var ae = this.getActionEl();
22137         if(this.hideMode == 'visibility'){
22138             ae.dom.style.visibility = "hidden";
22139         }else if(this.hideMode == 'offsets'){
22140             ae.addClass('x-hidden');
22141         }else{
22142             ae.dom.style.display = "none";
22143         }
22144     },
22145
22146     /**
22147      * Convenience function to hide or show this component by boolean.
22148      * @param {Boolean} visible True to show, false to hide
22149      * @return {Roo.Component} this
22150      */
22151     setVisible: function(visible){
22152         if(visible) {
22153             this.show();
22154         }else{
22155             this.hide();
22156         }
22157         return this;
22158     },
22159
22160     /**
22161      * Returns true if this component is visible.
22162      */
22163     isVisible : function(){
22164         return this.getActionEl().isVisible();
22165     },
22166
22167     cloneConfig : function(overrides){
22168         overrides = overrides || {};
22169         var id = overrides.id || Roo.id();
22170         var cfg = Roo.applyIf(overrides, this.initialConfig);
22171         cfg.id = id; // prevent dup id
22172         return new this.constructor(cfg);
22173     }
22174 });/*
22175  * Based on:
22176  * Ext JS Library 1.1.1
22177  * Copyright(c) 2006-2007, Ext JS, LLC.
22178  *
22179  * Originally Released Under LGPL - original licence link has changed is not relivant.
22180  *
22181  * Fork - LGPL
22182  * <script type="text/javascript">
22183  */
22184  (function(){ 
22185 /**
22186  * @class Roo.Layer
22187  * @extends Roo.Element
22188  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22189  * automatic maintaining of shadow/shim positions.
22190  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22191  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22192  * you can pass a string with a CSS class name. False turns off the shadow.
22193  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22194  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22195  * @cfg {String} cls CSS class to add to the element
22196  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22197  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22198  * @constructor
22199  * @param {Object} config An object with config options.
22200  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22201  */
22202
22203 Roo.Layer = function(config, existingEl){
22204     config = config || {};
22205     var dh = Roo.DomHelper;
22206     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22207     if(existingEl){
22208         this.dom = Roo.getDom(existingEl);
22209     }
22210     if(!this.dom){
22211         var o = config.dh || {tag: "div", cls: "x-layer"};
22212         this.dom = dh.append(pel, o);
22213     }
22214     if(config.cls){
22215         this.addClass(config.cls);
22216     }
22217     this.constrain = config.constrain !== false;
22218     this.visibilityMode = Roo.Element.VISIBILITY;
22219     if(config.id){
22220         this.id = this.dom.id = config.id;
22221     }else{
22222         this.id = Roo.id(this.dom);
22223     }
22224     this.zindex = config.zindex || this.getZIndex();
22225     this.position("absolute", this.zindex);
22226     if(config.shadow){
22227         this.shadowOffset = config.shadowOffset || 4;
22228         this.shadow = new Roo.Shadow({
22229             offset : this.shadowOffset,
22230             mode : config.shadow
22231         });
22232     }else{
22233         this.shadowOffset = 0;
22234     }
22235     this.useShim = config.shim !== false && Roo.useShims;
22236     this.useDisplay = config.useDisplay;
22237     this.hide();
22238 };
22239
22240 var supr = Roo.Element.prototype;
22241
22242 // shims are shared among layer to keep from having 100 iframes
22243 var shims = [];
22244
22245 Roo.extend(Roo.Layer, Roo.Element, {
22246
22247     getZIndex : function(){
22248         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22249     },
22250
22251     getShim : function(){
22252         if(!this.useShim){
22253             return null;
22254         }
22255         if(this.shim){
22256             return this.shim;
22257         }
22258         var shim = shims.shift();
22259         if(!shim){
22260             shim = this.createShim();
22261             shim.enableDisplayMode('block');
22262             shim.dom.style.display = 'none';
22263             shim.dom.style.visibility = 'visible';
22264         }
22265         var pn = this.dom.parentNode;
22266         if(shim.dom.parentNode != pn){
22267             pn.insertBefore(shim.dom, this.dom);
22268         }
22269         shim.setStyle('z-index', this.getZIndex()-2);
22270         this.shim = shim;
22271         return shim;
22272     },
22273
22274     hideShim : function(){
22275         if(this.shim){
22276             this.shim.setDisplayed(false);
22277             shims.push(this.shim);
22278             delete this.shim;
22279         }
22280     },
22281
22282     disableShadow : function(){
22283         if(this.shadow){
22284             this.shadowDisabled = true;
22285             this.shadow.hide();
22286             this.lastShadowOffset = this.shadowOffset;
22287             this.shadowOffset = 0;
22288         }
22289     },
22290
22291     enableShadow : function(show){
22292         if(this.shadow){
22293             this.shadowDisabled = false;
22294             this.shadowOffset = this.lastShadowOffset;
22295             delete this.lastShadowOffset;
22296             if(show){
22297                 this.sync(true);
22298             }
22299         }
22300     },
22301
22302     // private
22303     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22304     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22305     sync : function(doShow){
22306         var sw = this.shadow;
22307         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22308             var sh = this.getShim();
22309
22310             var w = this.getWidth(),
22311                 h = this.getHeight();
22312
22313             var l = this.getLeft(true),
22314                 t = this.getTop(true);
22315
22316             if(sw && !this.shadowDisabled){
22317                 if(doShow && !sw.isVisible()){
22318                     sw.show(this);
22319                 }else{
22320                     sw.realign(l, t, w, h);
22321                 }
22322                 if(sh){
22323                     if(doShow){
22324                        sh.show();
22325                     }
22326                     // fit the shim behind the shadow, so it is shimmed too
22327                     var a = sw.adjusts, s = sh.dom.style;
22328                     s.left = (Math.min(l, l+a.l))+"px";
22329                     s.top = (Math.min(t, t+a.t))+"px";
22330                     s.width = (w+a.w)+"px";
22331                     s.height = (h+a.h)+"px";
22332                 }
22333             }else if(sh){
22334                 if(doShow){
22335                    sh.show();
22336                 }
22337                 sh.setSize(w, h);
22338                 sh.setLeftTop(l, t);
22339             }
22340             
22341         }
22342     },
22343
22344     // private
22345     destroy : function(){
22346         this.hideShim();
22347         if(this.shadow){
22348             this.shadow.hide();
22349         }
22350         this.removeAllListeners();
22351         var pn = this.dom.parentNode;
22352         if(pn){
22353             pn.removeChild(this.dom);
22354         }
22355         Roo.Element.uncache(this.id);
22356     },
22357
22358     remove : function(){
22359         this.destroy();
22360     },
22361
22362     // private
22363     beginUpdate : function(){
22364         this.updating = true;
22365     },
22366
22367     // private
22368     endUpdate : function(){
22369         this.updating = false;
22370         this.sync(true);
22371     },
22372
22373     // private
22374     hideUnders : function(negOffset){
22375         if(this.shadow){
22376             this.shadow.hide();
22377         }
22378         this.hideShim();
22379     },
22380
22381     // private
22382     constrainXY : function(){
22383         if(this.constrain){
22384             var vw = Roo.lib.Dom.getViewWidth(),
22385                 vh = Roo.lib.Dom.getViewHeight();
22386             var s = Roo.get(document).getScroll();
22387
22388             var xy = this.getXY();
22389             var x = xy[0], y = xy[1];   
22390             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22391             // only move it if it needs it
22392             var moved = false;
22393             // first validate right/bottom
22394             if((x + w) > vw+s.left){
22395                 x = vw - w - this.shadowOffset;
22396                 moved = true;
22397             }
22398             if((y + h) > vh+s.top){
22399                 y = vh - h - this.shadowOffset;
22400                 moved = true;
22401             }
22402             // then make sure top/left isn't negative
22403             if(x < s.left){
22404                 x = s.left;
22405                 moved = true;
22406             }
22407             if(y < s.top){
22408                 y = s.top;
22409                 moved = true;
22410             }
22411             if(moved){
22412                 if(this.avoidY){
22413                     var ay = this.avoidY;
22414                     if(y <= ay && (y+h) >= ay){
22415                         y = ay-h-5;   
22416                     }
22417                 }
22418                 xy = [x, y];
22419                 this.storeXY(xy);
22420                 supr.setXY.call(this, xy);
22421                 this.sync();
22422             }
22423         }
22424     },
22425
22426     isVisible : function(){
22427         return this.visible;    
22428     },
22429
22430     // private
22431     showAction : function(){
22432         this.visible = true; // track visibility to prevent getStyle calls
22433         if(this.useDisplay === true){
22434             this.setDisplayed("");
22435         }else if(this.lastXY){
22436             supr.setXY.call(this, this.lastXY);
22437         }else if(this.lastLT){
22438             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22439         }
22440     },
22441
22442     // private
22443     hideAction : function(){
22444         this.visible = false;
22445         if(this.useDisplay === true){
22446             this.setDisplayed(false);
22447         }else{
22448             this.setLeftTop(-10000,-10000);
22449         }
22450     },
22451
22452     // overridden Element method
22453     setVisible : function(v, a, d, c, e){
22454         if(v){
22455             this.showAction();
22456         }
22457         if(a && v){
22458             var cb = function(){
22459                 this.sync(true);
22460                 if(c){
22461                     c();
22462                 }
22463             }.createDelegate(this);
22464             supr.setVisible.call(this, true, true, d, cb, e);
22465         }else{
22466             if(!v){
22467                 this.hideUnders(true);
22468             }
22469             var cb = c;
22470             if(a){
22471                 cb = function(){
22472                     this.hideAction();
22473                     if(c){
22474                         c();
22475                     }
22476                 }.createDelegate(this);
22477             }
22478             supr.setVisible.call(this, v, a, d, cb, e);
22479             if(v){
22480                 this.sync(true);
22481             }else if(!a){
22482                 this.hideAction();
22483             }
22484         }
22485     },
22486
22487     storeXY : function(xy){
22488         delete this.lastLT;
22489         this.lastXY = xy;
22490     },
22491
22492     storeLeftTop : function(left, top){
22493         delete this.lastXY;
22494         this.lastLT = [left, top];
22495     },
22496
22497     // private
22498     beforeFx : function(){
22499         this.beforeAction();
22500         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22501     },
22502
22503     // private
22504     afterFx : function(){
22505         Roo.Layer.superclass.afterFx.apply(this, arguments);
22506         this.sync(this.isVisible());
22507     },
22508
22509     // private
22510     beforeAction : function(){
22511         if(!this.updating && this.shadow){
22512             this.shadow.hide();
22513         }
22514     },
22515
22516     // overridden Element method
22517     setLeft : function(left){
22518         this.storeLeftTop(left, this.getTop(true));
22519         supr.setLeft.apply(this, arguments);
22520         this.sync();
22521     },
22522
22523     setTop : function(top){
22524         this.storeLeftTop(this.getLeft(true), top);
22525         supr.setTop.apply(this, arguments);
22526         this.sync();
22527     },
22528
22529     setLeftTop : function(left, top){
22530         this.storeLeftTop(left, top);
22531         supr.setLeftTop.apply(this, arguments);
22532         this.sync();
22533     },
22534
22535     setXY : function(xy, a, d, c, e){
22536         this.fixDisplay();
22537         this.beforeAction();
22538         this.storeXY(xy);
22539         var cb = this.createCB(c);
22540         supr.setXY.call(this, xy, a, d, cb, e);
22541         if(!a){
22542             cb();
22543         }
22544     },
22545
22546     // private
22547     createCB : function(c){
22548         var el = this;
22549         return function(){
22550             el.constrainXY();
22551             el.sync(true);
22552             if(c){
22553                 c();
22554             }
22555         };
22556     },
22557
22558     // overridden Element method
22559     setX : function(x, a, d, c, e){
22560         this.setXY([x, this.getY()], a, d, c, e);
22561     },
22562
22563     // overridden Element method
22564     setY : function(y, a, d, c, e){
22565         this.setXY([this.getX(), y], a, d, c, e);
22566     },
22567
22568     // overridden Element method
22569     setSize : function(w, h, a, d, c, e){
22570         this.beforeAction();
22571         var cb = this.createCB(c);
22572         supr.setSize.call(this, w, h, a, d, cb, e);
22573         if(!a){
22574             cb();
22575         }
22576     },
22577
22578     // overridden Element method
22579     setWidth : function(w, a, d, c, e){
22580         this.beforeAction();
22581         var cb = this.createCB(c);
22582         supr.setWidth.call(this, w, a, d, cb, e);
22583         if(!a){
22584             cb();
22585         }
22586     },
22587
22588     // overridden Element method
22589     setHeight : function(h, a, d, c, e){
22590         this.beforeAction();
22591         var cb = this.createCB(c);
22592         supr.setHeight.call(this, h, a, d, cb, e);
22593         if(!a){
22594             cb();
22595         }
22596     },
22597
22598     // overridden Element method
22599     setBounds : function(x, y, w, h, a, d, c, e){
22600         this.beforeAction();
22601         var cb = this.createCB(c);
22602         if(!a){
22603             this.storeXY([x, y]);
22604             supr.setXY.call(this, [x, y]);
22605             supr.setSize.call(this, w, h, a, d, cb, e);
22606             cb();
22607         }else{
22608             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22609         }
22610         return this;
22611     },
22612     
22613     /**
22614      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22615      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22616      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22617      * @param {Number} zindex The new z-index to set
22618      * @return {this} The Layer
22619      */
22620     setZIndex : function(zindex){
22621         this.zindex = zindex;
22622         this.setStyle("z-index", zindex + 2);
22623         if(this.shadow){
22624             this.shadow.setZIndex(zindex + 1);
22625         }
22626         if(this.shim){
22627             this.shim.setStyle("z-index", zindex);
22628         }
22629     }
22630 });
22631 })();/*
22632  * Based on:
22633  * Ext JS Library 1.1.1
22634  * Copyright(c) 2006-2007, Ext JS, LLC.
22635  *
22636  * Originally Released Under LGPL - original licence link has changed is not relivant.
22637  *
22638  * Fork - LGPL
22639  * <script type="text/javascript">
22640  */
22641
22642
22643 /**
22644  * @class Roo.Shadow
22645  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22646  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22647  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22648  * @constructor
22649  * Create a new Shadow
22650  * @param {Object} config The config object
22651  */
22652 Roo.Shadow = function(config){
22653     Roo.apply(this, config);
22654     if(typeof this.mode != "string"){
22655         this.mode = this.defaultMode;
22656     }
22657     var o = this.offset, a = {h: 0};
22658     var rad = Math.floor(this.offset/2);
22659     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22660         case "drop":
22661             a.w = 0;
22662             a.l = a.t = o;
22663             a.t -= 1;
22664             if(Roo.isIE){
22665                 a.l -= this.offset + rad;
22666                 a.t -= this.offset + rad;
22667                 a.w -= rad;
22668                 a.h -= rad;
22669                 a.t += 1;
22670             }
22671         break;
22672         case "sides":
22673             a.w = (o*2);
22674             a.l = -o;
22675             a.t = o-1;
22676             if(Roo.isIE){
22677                 a.l -= (this.offset - rad);
22678                 a.t -= this.offset + rad;
22679                 a.l += 1;
22680                 a.w -= (this.offset - rad)*2;
22681                 a.w -= rad + 1;
22682                 a.h -= 1;
22683             }
22684         break;
22685         case "frame":
22686             a.w = a.h = (o*2);
22687             a.l = a.t = -o;
22688             a.t += 1;
22689             a.h -= 2;
22690             if(Roo.isIE){
22691                 a.l -= (this.offset - rad);
22692                 a.t -= (this.offset - rad);
22693                 a.l += 1;
22694                 a.w -= (this.offset + rad + 1);
22695                 a.h -= (this.offset + rad);
22696                 a.h += 1;
22697             }
22698         break;
22699     };
22700
22701     this.adjusts = a;
22702 };
22703
22704 Roo.Shadow.prototype = {
22705     /**
22706      * @cfg {String} mode
22707      * The shadow display mode.  Supports the following options:<br />
22708      * sides: Shadow displays on both sides and bottom only<br />
22709      * frame: Shadow displays equally on all four sides<br />
22710      * drop: Traditional bottom-right drop shadow (default)
22711      */
22712     /**
22713      * @cfg {String} offset
22714      * The number of pixels to offset the shadow from the element (defaults to 4)
22715      */
22716     offset: 4,
22717
22718     // private
22719     defaultMode: "drop",
22720
22721     /**
22722      * Displays the shadow under the target element
22723      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22724      */
22725     show : function(target){
22726         target = Roo.get(target);
22727         if(!this.el){
22728             this.el = Roo.Shadow.Pool.pull();
22729             if(this.el.dom.nextSibling != target.dom){
22730                 this.el.insertBefore(target);
22731             }
22732         }
22733         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22734         if(Roo.isIE){
22735             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22736         }
22737         this.realign(
22738             target.getLeft(true),
22739             target.getTop(true),
22740             target.getWidth(),
22741             target.getHeight()
22742         );
22743         this.el.dom.style.display = "block";
22744     },
22745
22746     /**
22747      * Returns true if the shadow is visible, else false
22748      */
22749     isVisible : function(){
22750         return this.el ? true : false;  
22751     },
22752
22753     /**
22754      * Direct alignment when values are already available. Show must be called at least once before
22755      * calling this method to ensure it is initialized.
22756      * @param {Number} left The target element left position
22757      * @param {Number} top The target element top position
22758      * @param {Number} width The target element width
22759      * @param {Number} height The target element height
22760      */
22761     realign : function(l, t, w, h){
22762         if(!this.el){
22763             return;
22764         }
22765         var a = this.adjusts, d = this.el.dom, s = d.style;
22766         var iea = 0;
22767         s.left = (l+a.l)+"px";
22768         s.top = (t+a.t)+"px";
22769         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22770  
22771         if(s.width != sws || s.height != shs){
22772             s.width = sws;
22773             s.height = shs;
22774             if(!Roo.isIE){
22775                 var cn = d.childNodes;
22776                 var sww = Math.max(0, (sw-12))+"px";
22777                 cn[0].childNodes[1].style.width = sww;
22778                 cn[1].childNodes[1].style.width = sww;
22779                 cn[2].childNodes[1].style.width = sww;
22780                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22781             }
22782         }
22783     },
22784
22785     /**
22786      * Hides this shadow
22787      */
22788     hide : function(){
22789         if(this.el){
22790             this.el.dom.style.display = "none";
22791             Roo.Shadow.Pool.push(this.el);
22792             delete this.el;
22793         }
22794     },
22795
22796     /**
22797      * Adjust the z-index of this shadow
22798      * @param {Number} zindex The new z-index
22799      */
22800     setZIndex : function(z){
22801         this.zIndex = z;
22802         if(this.el){
22803             this.el.setStyle("z-index", z);
22804         }
22805     }
22806 };
22807
22808 // Private utility class that manages the internal Shadow cache
22809 Roo.Shadow.Pool = function(){
22810     var p = [];
22811     var markup = Roo.isIE ?
22812                  '<div class="x-ie-shadow"></div>' :
22813                  '<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>';
22814     return {
22815         pull : function(){
22816             var sh = p.shift();
22817             if(!sh){
22818                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22819                 sh.autoBoxAdjust = false;
22820             }
22821             return sh;
22822         },
22823
22824         push : function(sh){
22825             p.push(sh);
22826         }
22827     };
22828 }();/*
22829  * Based on:
22830  * Ext JS Library 1.1.1
22831  * Copyright(c) 2006-2007, Ext JS, LLC.
22832  *
22833  * Originally Released Under LGPL - original licence link has changed is not relivant.
22834  *
22835  * Fork - LGPL
22836  * <script type="text/javascript">
22837  */
22838
22839 /**
22840  * @class Roo.BoxComponent
22841  * @extends Roo.Component
22842  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22843  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22844  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22845  * layout containers.
22846  * @constructor
22847  * @param {Roo.Element/String/Object} config The configuration options.
22848  */
22849 Roo.BoxComponent = function(config){
22850     Roo.Component.call(this, config);
22851     this.addEvents({
22852         /**
22853          * @event resize
22854          * Fires after the component is resized.
22855              * @param {Roo.Component} this
22856              * @param {Number} adjWidth The box-adjusted width that was set
22857              * @param {Number} adjHeight The box-adjusted height that was set
22858              * @param {Number} rawWidth The width that was originally specified
22859              * @param {Number} rawHeight The height that was originally specified
22860              */
22861         resize : true,
22862         /**
22863          * @event move
22864          * Fires after the component is moved.
22865              * @param {Roo.Component} this
22866              * @param {Number} x The new x position
22867              * @param {Number} y The new y position
22868              */
22869         move : true
22870     });
22871 };
22872
22873 Roo.extend(Roo.BoxComponent, Roo.Component, {
22874     // private, set in afterRender to signify that the component has been rendered
22875     boxReady : false,
22876     // private, used to defer height settings to subclasses
22877     deferHeight: false,
22878     /** @cfg {Number} width
22879      * width (optional) size of component
22880      */
22881      /** @cfg {Number} height
22882      * height (optional) size of component
22883      */
22884      
22885     /**
22886      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22887      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22888      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22889      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22890      * @return {Roo.BoxComponent} this
22891      */
22892     setSize : function(w, h){
22893         // support for standard size objects
22894         if(typeof w == 'object'){
22895             h = w.height;
22896             w = w.width;
22897         }
22898         // not rendered
22899         if(!this.boxReady){
22900             this.width = w;
22901             this.height = h;
22902             return this;
22903         }
22904
22905         // prevent recalcs when not needed
22906         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22907             return this;
22908         }
22909         this.lastSize = {width: w, height: h};
22910
22911         var adj = this.adjustSize(w, h);
22912         var aw = adj.width, ah = adj.height;
22913         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22914             var rz = this.getResizeEl();
22915             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22916                 rz.setSize(aw, ah);
22917             }else if(!this.deferHeight && ah !== undefined){
22918                 rz.setHeight(ah);
22919             }else if(aw !== undefined){
22920                 rz.setWidth(aw);
22921             }
22922             this.onResize(aw, ah, w, h);
22923             this.fireEvent('resize', this, aw, ah, w, h);
22924         }
22925         return this;
22926     },
22927
22928     /**
22929      * Gets the current size of the component's underlying element.
22930      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22931      */
22932     getSize : function(){
22933         return this.el.getSize();
22934     },
22935
22936     /**
22937      * Gets the current XY position of the component's underlying element.
22938      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22939      * @return {Array} The XY position of the element (e.g., [100, 200])
22940      */
22941     getPosition : function(local){
22942         if(local === true){
22943             return [this.el.getLeft(true), this.el.getTop(true)];
22944         }
22945         return this.xy || this.el.getXY();
22946     },
22947
22948     /**
22949      * Gets the current box measurements of the component's underlying element.
22950      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22951      * @returns {Object} box An object in the format {x, y, width, height}
22952      */
22953     getBox : function(local){
22954         var s = this.el.getSize();
22955         if(local){
22956             s.x = this.el.getLeft(true);
22957             s.y = this.el.getTop(true);
22958         }else{
22959             var xy = this.xy || this.el.getXY();
22960             s.x = xy[0];
22961             s.y = xy[1];
22962         }
22963         return s;
22964     },
22965
22966     /**
22967      * Sets the current box measurements of the component's underlying element.
22968      * @param {Object} box An object in the format {x, y, width, height}
22969      * @returns {Roo.BoxComponent} this
22970      */
22971     updateBox : function(box){
22972         this.setSize(box.width, box.height);
22973         this.setPagePosition(box.x, box.y);
22974         return this;
22975     },
22976
22977     // protected
22978     getResizeEl : function(){
22979         return this.resizeEl || this.el;
22980     },
22981
22982     // protected
22983     getPositionEl : function(){
22984         return this.positionEl || this.el;
22985     },
22986
22987     /**
22988      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22989      * This method fires the move event.
22990      * @param {Number} left The new left
22991      * @param {Number} top The new top
22992      * @returns {Roo.BoxComponent} this
22993      */
22994     setPosition : function(x, y){
22995         this.x = x;
22996         this.y = y;
22997         if(!this.boxReady){
22998             return this;
22999         }
23000         var adj = this.adjustPosition(x, y);
23001         var ax = adj.x, ay = adj.y;
23002
23003         var el = this.getPositionEl();
23004         if(ax !== undefined || ay !== undefined){
23005             if(ax !== undefined && ay !== undefined){
23006                 el.setLeftTop(ax, ay);
23007             }else if(ax !== undefined){
23008                 el.setLeft(ax);
23009             }else if(ay !== undefined){
23010                 el.setTop(ay);
23011             }
23012             this.onPosition(ax, ay);
23013             this.fireEvent('move', this, ax, ay);
23014         }
23015         return this;
23016     },
23017
23018     /**
23019      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23020      * This method fires the move event.
23021      * @param {Number} x The new x position
23022      * @param {Number} y The new y position
23023      * @returns {Roo.BoxComponent} this
23024      */
23025     setPagePosition : function(x, y){
23026         this.pageX = x;
23027         this.pageY = y;
23028         if(!this.boxReady){
23029             return;
23030         }
23031         if(x === undefined || y === undefined){ // cannot translate undefined points
23032             return;
23033         }
23034         var p = this.el.translatePoints(x, y);
23035         this.setPosition(p.left, p.top);
23036         return this;
23037     },
23038
23039     // private
23040     onRender : function(ct, position){
23041         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23042         if(this.resizeEl){
23043             this.resizeEl = Roo.get(this.resizeEl);
23044         }
23045         if(this.positionEl){
23046             this.positionEl = Roo.get(this.positionEl);
23047         }
23048     },
23049
23050     // private
23051     afterRender : function(){
23052         Roo.BoxComponent.superclass.afterRender.call(this);
23053         this.boxReady = true;
23054         this.setSize(this.width, this.height);
23055         if(this.x || this.y){
23056             this.setPosition(this.x, this.y);
23057         }
23058         if(this.pageX || this.pageY){
23059             this.setPagePosition(this.pageX, this.pageY);
23060         }
23061     },
23062
23063     /**
23064      * Force the component's size to recalculate based on the underlying element's current height and width.
23065      * @returns {Roo.BoxComponent} this
23066      */
23067     syncSize : function(){
23068         delete this.lastSize;
23069         this.setSize(this.el.getWidth(), this.el.getHeight());
23070         return this;
23071     },
23072
23073     /**
23074      * Called after the component is resized, this method is empty by default but can be implemented by any
23075      * subclass that needs to perform custom logic after a resize occurs.
23076      * @param {Number} adjWidth The box-adjusted width that was set
23077      * @param {Number} adjHeight The box-adjusted height that was set
23078      * @param {Number} rawWidth The width that was originally specified
23079      * @param {Number} rawHeight The height that was originally specified
23080      */
23081     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23082
23083     },
23084
23085     /**
23086      * Called after the component is moved, this method is empty by default but can be implemented by any
23087      * subclass that needs to perform custom logic after a move occurs.
23088      * @param {Number} x The new x position
23089      * @param {Number} y The new y position
23090      */
23091     onPosition : function(x, y){
23092
23093     },
23094
23095     // private
23096     adjustSize : function(w, h){
23097         if(this.autoWidth){
23098             w = 'auto';
23099         }
23100         if(this.autoHeight){
23101             h = 'auto';
23102         }
23103         return {width : w, height: h};
23104     },
23105
23106     // private
23107     adjustPosition : function(x, y){
23108         return {x : x, y: y};
23109     }
23110 });/*
23111  * Based on:
23112  * Ext JS Library 1.1.1
23113  * Copyright(c) 2006-2007, Ext JS, LLC.
23114  *
23115  * Originally Released Under LGPL - original licence link has changed is not relivant.
23116  *
23117  * Fork - LGPL
23118  * <script type="text/javascript">
23119  */
23120
23121
23122 /**
23123  * @class Roo.SplitBar
23124  * @extends Roo.util.Observable
23125  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23126  * <br><br>
23127  * Usage:
23128  * <pre><code>
23129 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23130                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23131 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23132 split.minSize = 100;
23133 split.maxSize = 600;
23134 split.animate = true;
23135 split.on('moved', splitterMoved);
23136 </code></pre>
23137  * @constructor
23138  * Create a new SplitBar
23139  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23140  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23141  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23142  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23143                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23144                         position of the SplitBar).
23145  */
23146 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23147     
23148     /** @private */
23149     this.el = Roo.get(dragElement, true);
23150     this.el.dom.unselectable = "on";
23151     /** @private */
23152     this.resizingEl = Roo.get(resizingElement, true);
23153
23154     /**
23155      * @private
23156      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23157      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23158      * @type Number
23159      */
23160     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23161     
23162     /**
23163      * The minimum size of the resizing element. (Defaults to 0)
23164      * @type Number
23165      */
23166     this.minSize = 0;
23167     
23168     /**
23169      * The maximum size of the resizing element. (Defaults to 2000)
23170      * @type Number
23171      */
23172     this.maxSize = 2000;
23173     
23174     /**
23175      * Whether to animate the transition to the new size
23176      * @type Boolean
23177      */
23178     this.animate = false;
23179     
23180     /**
23181      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23182      * @type Boolean
23183      */
23184     this.useShim = false;
23185     
23186     /** @private */
23187     this.shim = null;
23188     
23189     if(!existingProxy){
23190         /** @private */
23191         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23192     }else{
23193         this.proxy = Roo.get(existingProxy).dom;
23194     }
23195     /** @private */
23196     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23197     
23198     /** @private */
23199     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23200     
23201     /** @private */
23202     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23203     
23204     /** @private */
23205     this.dragSpecs = {};
23206     
23207     /**
23208      * @private The adapter to use to positon and resize elements
23209      */
23210     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23211     this.adapter.init(this);
23212     
23213     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23214         /** @private */
23215         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23216         this.el.addClass("x-splitbar-h");
23217     }else{
23218         /** @private */
23219         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23220         this.el.addClass("x-splitbar-v");
23221     }
23222     
23223     this.addEvents({
23224         /**
23225          * @event resize
23226          * Fires when the splitter is moved (alias for {@link #event-moved})
23227          * @param {Roo.SplitBar} this
23228          * @param {Number} newSize the new width or height
23229          */
23230         "resize" : true,
23231         /**
23232          * @event moved
23233          * Fires when the splitter is moved
23234          * @param {Roo.SplitBar} this
23235          * @param {Number} newSize the new width or height
23236          */
23237         "moved" : true,
23238         /**
23239          * @event beforeresize
23240          * Fires before the splitter is dragged
23241          * @param {Roo.SplitBar} this
23242          */
23243         "beforeresize" : true,
23244
23245         "beforeapply" : true
23246     });
23247
23248     Roo.util.Observable.call(this);
23249 };
23250
23251 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23252     onStartProxyDrag : function(x, y){
23253         this.fireEvent("beforeresize", this);
23254         if(!this.overlay){
23255             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23256             o.unselectable();
23257             o.enableDisplayMode("block");
23258             // all splitbars share the same overlay
23259             Roo.SplitBar.prototype.overlay = o;
23260         }
23261         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23262         this.overlay.show();
23263         Roo.get(this.proxy).setDisplayed("block");
23264         var size = this.adapter.getElementSize(this);
23265         this.activeMinSize = this.getMinimumSize();;
23266         this.activeMaxSize = this.getMaximumSize();;
23267         var c1 = size - this.activeMinSize;
23268         var c2 = Math.max(this.activeMaxSize - size, 0);
23269         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23270             this.dd.resetConstraints();
23271             this.dd.setXConstraint(
23272                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23273                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23274             );
23275             this.dd.setYConstraint(0, 0);
23276         }else{
23277             this.dd.resetConstraints();
23278             this.dd.setXConstraint(0, 0);
23279             this.dd.setYConstraint(
23280                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23281                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23282             );
23283          }
23284         this.dragSpecs.startSize = size;
23285         this.dragSpecs.startPoint = [x, y];
23286         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23287     },
23288     
23289     /** 
23290      * @private Called after the drag operation by the DDProxy
23291      */
23292     onEndProxyDrag : function(e){
23293         Roo.get(this.proxy).setDisplayed(false);
23294         var endPoint = Roo.lib.Event.getXY(e);
23295         if(this.overlay){
23296             this.overlay.hide();
23297         }
23298         var newSize;
23299         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23300             newSize = this.dragSpecs.startSize + 
23301                 (this.placement == Roo.SplitBar.LEFT ?
23302                     endPoint[0] - this.dragSpecs.startPoint[0] :
23303                     this.dragSpecs.startPoint[0] - endPoint[0]
23304                 );
23305         }else{
23306             newSize = this.dragSpecs.startSize + 
23307                 (this.placement == Roo.SplitBar.TOP ?
23308                     endPoint[1] - this.dragSpecs.startPoint[1] :
23309                     this.dragSpecs.startPoint[1] - endPoint[1]
23310                 );
23311         }
23312         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23313         if(newSize != this.dragSpecs.startSize){
23314             if(this.fireEvent('beforeapply', this, newSize) !== false){
23315                 this.adapter.setElementSize(this, newSize);
23316                 this.fireEvent("moved", this, newSize);
23317                 this.fireEvent("resize", this, newSize);
23318             }
23319         }
23320     },
23321     
23322     /**
23323      * Get the adapter this SplitBar uses
23324      * @return The adapter object
23325      */
23326     getAdapter : function(){
23327         return this.adapter;
23328     },
23329     
23330     /**
23331      * Set the adapter this SplitBar uses
23332      * @param {Object} adapter A SplitBar adapter object
23333      */
23334     setAdapter : function(adapter){
23335         this.adapter = adapter;
23336         this.adapter.init(this);
23337     },
23338     
23339     /**
23340      * Gets the minimum size for the resizing element
23341      * @return {Number} The minimum size
23342      */
23343     getMinimumSize : function(){
23344         return this.minSize;
23345     },
23346     
23347     /**
23348      * Sets the minimum size for the resizing element
23349      * @param {Number} minSize The minimum size
23350      */
23351     setMinimumSize : function(minSize){
23352         this.minSize = minSize;
23353     },
23354     
23355     /**
23356      * Gets the maximum size for the resizing element
23357      * @return {Number} The maximum size
23358      */
23359     getMaximumSize : function(){
23360         return this.maxSize;
23361     },
23362     
23363     /**
23364      * Sets the maximum size for the resizing element
23365      * @param {Number} maxSize The maximum size
23366      */
23367     setMaximumSize : function(maxSize){
23368         this.maxSize = maxSize;
23369     },
23370     
23371     /**
23372      * Sets the initialize size for the resizing element
23373      * @param {Number} size The initial size
23374      */
23375     setCurrentSize : function(size){
23376         var oldAnimate = this.animate;
23377         this.animate = false;
23378         this.adapter.setElementSize(this, size);
23379         this.animate = oldAnimate;
23380     },
23381     
23382     /**
23383      * Destroy this splitbar. 
23384      * @param {Boolean} removeEl True to remove the element
23385      */
23386     destroy : function(removeEl){
23387         if(this.shim){
23388             this.shim.remove();
23389         }
23390         this.dd.unreg();
23391         this.proxy.parentNode.removeChild(this.proxy);
23392         if(removeEl){
23393             this.el.remove();
23394         }
23395     }
23396 });
23397
23398 /**
23399  * @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.
23400  */
23401 Roo.SplitBar.createProxy = function(dir){
23402     var proxy = new Roo.Element(document.createElement("div"));
23403     proxy.unselectable();
23404     var cls = 'x-splitbar-proxy';
23405     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23406     document.body.appendChild(proxy.dom);
23407     return proxy.dom;
23408 };
23409
23410 /** 
23411  * @class Roo.SplitBar.BasicLayoutAdapter
23412  * Default Adapter. It assumes the splitter and resizing element are not positioned
23413  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23414  */
23415 Roo.SplitBar.BasicLayoutAdapter = function(){
23416 };
23417
23418 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23419     // do nothing for now
23420     init : function(s){
23421     
23422     },
23423     /**
23424      * Called before drag operations to get the current size of the resizing element. 
23425      * @param {Roo.SplitBar} s The SplitBar using this adapter
23426      */
23427      getElementSize : function(s){
23428         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23429             return s.resizingEl.getWidth();
23430         }else{
23431             return s.resizingEl.getHeight();
23432         }
23433     },
23434     
23435     /**
23436      * Called after drag operations to set the size of the resizing element.
23437      * @param {Roo.SplitBar} s The SplitBar using this adapter
23438      * @param {Number} newSize The new size to set
23439      * @param {Function} onComplete A function to be invoked when resizing is complete
23440      */
23441     setElementSize : function(s, newSize, onComplete){
23442         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23443             if(!s.animate){
23444                 s.resizingEl.setWidth(newSize);
23445                 if(onComplete){
23446                     onComplete(s, newSize);
23447                 }
23448             }else{
23449                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23450             }
23451         }else{
23452             
23453             if(!s.animate){
23454                 s.resizingEl.setHeight(newSize);
23455                 if(onComplete){
23456                     onComplete(s, newSize);
23457                 }
23458             }else{
23459                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23460             }
23461         }
23462     }
23463 };
23464
23465 /** 
23466  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23467  * @extends Roo.SplitBar.BasicLayoutAdapter
23468  * Adapter that  moves the splitter element to align with the resized sizing element. 
23469  * Used with an absolute positioned SplitBar.
23470  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23471  * document.body, make sure you assign an id to the body element.
23472  */
23473 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23474     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23475     this.container = Roo.get(container);
23476 };
23477
23478 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23479     init : function(s){
23480         this.basic.init(s);
23481     },
23482     
23483     getElementSize : function(s){
23484         return this.basic.getElementSize(s);
23485     },
23486     
23487     setElementSize : function(s, newSize, onComplete){
23488         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23489     },
23490     
23491     moveSplitter : function(s){
23492         var yes = Roo.SplitBar;
23493         switch(s.placement){
23494             case yes.LEFT:
23495                 s.el.setX(s.resizingEl.getRight());
23496                 break;
23497             case yes.RIGHT:
23498                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23499                 break;
23500             case yes.TOP:
23501                 s.el.setY(s.resizingEl.getBottom());
23502                 break;
23503             case yes.BOTTOM:
23504                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23505                 break;
23506         }
23507     }
23508 };
23509
23510 /**
23511  * Orientation constant - Create a vertical SplitBar
23512  * @static
23513  * @type Number
23514  */
23515 Roo.SplitBar.VERTICAL = 1;
23516
23517 /**
23518  * Orientation constant - Create a horizontal SplitBar
23519  * @static
23520  * @type Number
23521  */
23522 Roo.SplitBar.HORIZONTAL = 2;
23523
23524 /**
23525  * Placement constant - The resizing element is to the left of the splitter element
23526  * @static
23527  * @type Number
23528  */
23529 Roo.SplitBar.LEFT = 1;
23530
23531 /**
23532  * Placement constant - The resizing element is to the right of the splitter element
23533  * @static
23534  * @type Number
23535  */
23536 Roo.SplitBar.RIGHT = 2;
23537
23538 /**
23539  * Placement constant - The resizing element is positioned above the splitter element
23540  * @static
23541  * @type Number
23542  */
23543 Roo.SplitBar.TOP = 3;
23544
23545 /**
23546  * Placement constant - The resizing element is positioned under splitter element
23547  * @static
23548  * @type Number
23549  */
23550 Roo.SplitBar.BOTTOM = 4;
23551 /*
23552  * Based on:
23553  * Ext JS Library 1.1.1
23554  * Copyright(c) 2006-2007, Ext JS, LLC.
23555  *
23556  * Originally Released Under LGPL - original licence link has changed is not relivant.
23557  *
23558  * Fork - LGPL
23559  * <script type="text/javascript">
23560  */
23561
23562 /**
23563  * @class Roo.View
23564  * @extends Roo.util.Observable
23565  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23566  * This class also supports single and multi selection modes. <br>
23567  * Create a data model bound view:
23568  <pre><code>
23569  var store = new Roo.data.Store(...);
23570
23571  var view = new Roo.View({
23572     el : "my-element",
23573     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23574  
23575     singleSelect: true,
23576     selectedClass: "ydataview-selected",
23577     store: store
23578  });
23579
23580  // listen for node click?
23581  view.on("click", function(vw, index, node, e){
23582  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23583  });
23584
23585  // load XML data
23586  dataModel.load("foobar.xml");
23587  </code></pre>
23588  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23589  * <br><br>
23590  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23591  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23592  * 
23593  * Note: old style constructor is still suported (container, template, config)
23594  * 
23595  * @constructor
23596  * Create a new View
23597  * @param {Object} config The config object
23598  * 
23599  */
23600 Roo.View = function(config, depreciated_tpl, depreciated_config){
23601     
23602     if (typeof(depreciated_tpl) == 'undefined') {
23603         // new way.. - universal constructor.
23604         Roo.apply(this, config);
23605         this.el  = Roo.get(this.el);
23606     } else {
23607         // old format..
23608         this.el  = Roo.get(config);
23609         this.tpl = depreciated_tpl;
23610         Roo.apply(this, depreciated_config);
23611     }
23612      
23613     
23614     if(typeof(this.tpl) == "string"){
23615         this.tpl = new Roo.Template(this.tpl);
23616     } else {
23617         // support xtype ctors..
23618         this.tpl = new Roo.factory(this.tpl, Roo);
23619     }
23620     
23621     
23622     this.tpl.compile();
23623    
23624
23625      
23626     /** @private */
23627     this.addEvents({
23628         /**
23629          * @event beforeclick
23630          * Fires before a click is processed. Returns false to cancel the default action.
23631          * @param {Roo.View} this
23632          * @param {Number} index The index of the target node
23633          * @param {HTMLElement} node The target node
23634          * @param {Roo.EventObject} e The raw event object
23635          */
23636             "beforeclick" : true,
23637         /**
23638          * @event click
23639          * Fires when a template node is clicked.
23640          * @param {Roo.View} this
23641          * @param {Number} index The index of the target node
23642          * @param {HTMLElement} node The target node
23643          * @param {Roo.EventObject} e The raw event object
23644          */
23645             "click" : true,
23646         /**
23647          * @event dblclick
23648          * Fires when a template node is double clicked.
23649          * @param {Roo.View} this
23650          * @param {Number} index The index of the target node
23651          * @param {HTMLElement} node The target node
23652          * @param {Roo.EventObject} e The raw event object
23653          */
23654             "dblclick" : true,
23655         /**
23656          * @event contextmenu
23657          * Fires when a template node is right clicked.
23658          * @param {Roo.View} this
23659          * @param {Number} index The index of the target node
23660          * @param {HTMLElement} node The target node
23661          * @param {Roo.EventObject} e The raw event object
23662          */
23663             "contextmenu" : true,
23664         /**
23665          * @event selectionchange
23666          * Fires when the selected nodes change.
23667          * @param {Roo.View} this
23668          * @param {Array} selections Array of the selected nodes
23669          */
23670             "selectionchange" : true,
23671     
23672         /**
23673          * @event beforeselect
23674          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23675          * @param {Roo.View} this
23676          * @param {HTMLElement} node The node to be selected
23677          * @param {Array} selections Array of currently selected nodes
23678          */
23679             "beforeselect" : true,
23680         /**
23681          * @event preparedata
23682          * Fires on every row to render, to allow you to change the data.
23683          * @param {Roo.View} this
23684          * @param {Object} data to be rendered (change this)
23685          */
23686           "preparedata" : true
23687         });
23688
23689     this.el.on({
23690         "click": this.onClick,
23691         "dblclick": this.onDblClick,
23692         "contextmenu": this.onContextMenu,
23693         scope:this
23694     });
23695
23696     this.selections = [];
23697     this.nodes = [];
23698     this.cmp = new Roo.CompositeElementLite([]);
23699     if(this.store){
23700         this.store = Roo.factory(this.store, Roo.data);
23701         this.setStore(this.store, true);
23702     }
23703     Roo.View.superclass.constructor.call(this);
23704 };
23705
23706 Roo.extend(Roo.View, Roo.util.Observable, {
23707     
23708      /**
23709      * @cfg {Roo.data.Store} store Data store to load data from.
23710      */
23711     store : false,
23712     
23713     /**
23714      * @cfg {String|Roo.Element} el The container element.
23715      */
23716     el : '',
23717     
23718     /**
23719      * @cfg {String|Roo.Template} tpl The template used by this View 
23720      */
23721     tpl : false,
23722     
23723     /**
23724      * @cfg {String} selectedClass The css class to add to selected nodes
23725      */
23726     selectedClass : "x-view-selected",
23727      /**
23728      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23729      */
23730     emptyText : "",
23731     /**
23732      * @cfg {Boolean} multiSelect Allow multiple selection
23733      */
23734     multiSelect : false,
23735     /**
23736      * @cfg {Boolean} singleSelect Allow single selection
23737      */
23738     singleSelect:  false,
23739     
23740     /**
23741      * @cfg {Boolean} toggleSelect - selecting 
23742      */
23743     toggleSelect : false,
23744     
23745     /**
23746      * Returns the element this view is bound to.
23747      * @return {Roo.Element}
23748      */
23749     getEl : function(){
23750         return this.el;
23751     },
23752
23753     /**
23754      * Refreshes the view.
23755      */
23756     refresh : function(){
23757         var t = this.tpl;
23758         this.clearSelections();
23759         this.el.update("");
23760         var html = [];
23761         var records = this.store.getRange();
23762         if(records.length < 1){
23763             this.el.update(this.emptyText);
23764             return;
23765         }
23766         for(var i = 0, len = records.length; i < len; i++){
23767             var data = this.prepareData(records[i].data, i, records[i]);
23768             this.fireEvent("preparedata", this, data, i, records[i]);
23769             html[html.length] = t.apply(data);
23770         }
23771         this.el.update(html.join(""));
23772         this.nodes = this.el.dom.childNodes;
23773         this.updateIndexes(0);
23774     },
23775
23776     /**
23777      * Function to override to reformat the data that is sent to
23778      * the template for each node.
23779      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23780      * a JSON object for an UpdateManager bound view).
23781      */
23782     prepareData : function(data){
23783         return data;
23784     },
23785
23786     onUpdate : function(ds, record){
23787         this.clearSelections();
23788         var index = this.store.indexOf(record);
23789         var n = this.nodes[index];
23790         this.tpl.insertBefore(n, this.prepareData(record.data));
23791         n.parentNode.removeChild(n);
23792         this.updateIndexes(index, index);
23793     },
23794
23795     onAdd : function(ds, records, index){
23796         this.clearSelections();
23797         if(this.nodes.length == 0){
23798             this.refresh();
23799             return;
23800         }
23801         var n = this.nodes[index];
23802         for(var i = 0, len = records.length; i < len; i++){
23803             var d = this.prepareData(records[i].data);
23804             if(n){
23805                 this.tpl.insertBefore(n, d);
23806             }else{
23807                 this.tpl.append(this.el, d);
23808             }
23809         }
23810         this.updateIndexes(index);
23811     },
23812
23813     onRemove : function(ds, record, index){
23814         this.clearSelections();
23815         this.el.dom.removeChild(this.nodes[index]);
23816         this.updateIndexes(index);
23817     },
23818
23819     /**
23820      * Refresh an individual node.
23821      * @param {Number} index
23822      */
23823     refreshNode : function(index){
23824         this.onUpdate(this.store, this.store.getAt(index));
23825     },
23826
23827     updateIndexes : function(startIndex, endIndex){
23828         var ns = this.nodes;
23829         startIndex = startIndex || 0;
23830         endIndex = endIndex || ns.length - 1;
23831         for(var i = startIndex; i <= endIndex; i++){
23832             ns[i].nodeIndex = i;
23833         }
23834     },
23835
23836     /**
23837      * Changes the data store this view uses and refresh the view.
23838      * @param {Store} store
23839      */
23840     setStore : function(store, initial){
23841         if(!initial && this.store){
23842             this.store.un("datachanged", this.refresh);
23843             this.store.un("add", this.onAdd);
23844             this.store.un("remove", this.onRemove);
23845             this.store.un("update", this.onUpdate);
23846             this.store.un("clear", this.refresh);
23847         }
23848         if(store){
23849           
23850             store.on("datachanged", this.refresh, this);
23851             store.on("add", this.onAdd, this);
23852             store.on("remove", this.onRemove, this);
23853             store.on("update", this.onUpdate, this);
23854             store.on("clear", this.refresh, this);
23855         }
23856         
23857         if(store){
23858             this.refresh();
23859         }
23860     },
23861
23862     /**
23863      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23864      * @param {HTMLElement} node
23865      * @return {HTMLElement} The template node
23866      */
23867     findItemFromChild : function(node){
23868         var el = this.el.dom;
23869         if(!node || node.parentNode == el){
23870                     return node;
23871             }
23872             var p = node.parentNode;
23873             while(p && p != el){
23874             if(p.parentNode == el){
23875                 return p;
23876             }
23877             p = p.parentNode;
23878         }
23879             return null;
23880     },
23881
23882     /** @ignore */
23883     onClick : function(e){
23884         var item = this.findItemFromChild(e.getTarget());
23885         if(item){
23886             var index = this.indexOf(item);
23887             if(this.onItemClick(item, index, e) !== false){
23888                 this.fireEvent("click", this, index, item, e);
23889             }
23890         }else{
23891             this.clearSelections();
23892         }
23893     },
23894
23895     /** @ignore */
23896     onContextMenu : function(e){
23897         var item = this.findItemFromChild(e.getTarget());
23898         if(item){
23899             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23900         }
23901     },
23902
23903     /** @ignore */
23904     onDblClick : function(e){
23905         var item = this.findItemFromChild(e.getTarget());
23906         if(item){
23907             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23908         }
23909     },
23910
23911     onItemClick : function(item, index, e)
23912     {
23913         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23914             return false;
23915         }
23916         if (this.toggleSelect) {
23917             var m = this.isSelected(item) ? 'unselect' : 'select';
23918             Roo.log(m);
23919             var _t = this;
23920             _t[m](item, true, false);
23921             return true;
23922         }
23923         if(this.multiSelect || this.singleSelect){
23924             if(this.multiSelect && e.shiftKey && this.lastSelection){
23925                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23926             }else{
23927                 this.select(item, this.multiSelect && e.ctrlKey);
23928                 this.lastSelection = item;
23929             }
23930             e.preventDefault();
23931         }
23932         return true;
23933     },
23934
23935     /**
23936      * Get the number of selected nodes.
23937      * @return {Number}
23938      */
23939     getSelectionCount : function(){
23940         return this.selections.length;
23941     },
23942
23943     /**
23944      * Get the currently selected nodes.
23945      * @return {Array} An array of HTMLElements
23946      */
23947     getSelectedNodes : function(){
23948         return this.selections;
23949     },
23950
23951     /**
23952      * Get the indexes of the selected nodes.
23953      * @return {Array}
23954      */
23955     getSelectedIndexes : function(){
23956         var indexes = [], s = this.selections;
23957         for(var i = 0, len = s.length; i < len; i++){
23958             indexes.push(s[i].nodeIndex);
23959         }
23960         return indexes;
23961     },
23962
23963     /**
23964      * Clear all selections
23965      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23966      */
23967     clearSelections : function(suppressEvent){
23968         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23969             this.cmp.elements = this.selections;
23970             this.cmp.removeClass(this.selectedClass);
23971             this.selections = [];
23972             if(!suppressEvent){
23973                 this.fireEvent("selectionchange", this, this.selections);
23974             }
23975         }
23976     },
23977
23978     /**
23979      * Returns true if the passed node is selected
23980      * @param {HTMLElement/Number} node The node or node index
23981      * @return {Boolean}
23982      */
23983     isSelected : function(node){
23984         var s = this.selections;
23985         if(s.length < 1){
23986             return false;
23987         }
23988         node = this.getNode(node);
23989         return s.indexOf(node) !== -1;
23990     },
23991
23992     /**
23993      * Selects nodes.
23994      * @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
23995      * @param {Boolean} keepExisting (optional) true to keep existing selections
23996      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23997      */
23998     select : function(nodeInfo, keepExisting, suppressEvent){
23999         if(nodeInfo instanceof Array){
24000             if(!keepExisting){
24001                 this.clearSelections(true);
24002             }
24003             for(var i = 0, len = nodeInfo.length; i < len; i++){
24004                 this.select(nodeInfo[i], true, true);
24005             }
24006             return;
24007         } 
24008         var node = this.getNode(nodeInfo);
24009         if(!node || this.isSelected(node)){
24010             return; // already selected.
24011         }
24012         if(!keepExisting){
24013             this.clearSelections(true);
24014         }
24015         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24016             Roo.fly(node).addClass(this.selectedClass);
24017             this.selections.push(node);
24018             if(!suppressEvent){
24019                 this.fireEvent("selectionchange", this, this.selections);
24020             }
24021         }
24022         
24023         
24024     },
24025       /**
24026      * Unselects nodes.
24027      * @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
24028      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24029      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24030      */
24031     unselect : function(nodeInfo, keepExisting, suppressEvent)
24032     {
24033         if(nodeInfo instanceof Array){
24034             Roo.each(this.selections, function(s) {
24035                 this.unselect(s, nodeInfo);
24036             }, this);
24037             return;
24038         }
24039         var node = this.getNode(nodeInfo);
24040         if(!node || !this.isSelected(node)){
24041             Roo.log("not selected");
24042             return; // not selected.
24043         }
24044         // fireevent???
24045         var ns = [];
24046         Roo.each(this.selections, function(s) {
24047             if (s == node ) {
24048                 Roo.fly(node).removeClass(this.selectedClass);
24049
24050                 return;
24051             }
24052             ns.push(s);
24053         },this);
24054         
24055         this.selections= ns;
24056         this.fireEvent("selectionchange", this, this.selections);
24057     },
24058
24059     /**
24060      * Gets a template node.
24061      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24062      * @return {HTMLElement} The node or null if it wasn't found
24063      */
24064     getNode : function(nodeInfo){
24065         if(typeof nodeInfo == "string"){
24066             return document.getElementById(nodeInfo);
24067         }else if(typeof nodeInfo == "number"){
24068             return this.nodes[nodeInfo];
24069         }
24070         return nodeInfo;
24071     },
24072
24073     /**
24074      * Gets a range template nodes.
24075      * @param {Number} startIndex
24076      * @param {Number} endIndex
24077      * @return {Array} An array of nodes
24078      */
24079     getNodes : function(start, end){
24080         var ns = this.nodes;
24081         start = start || 0;
24082         end = typeof end == "undefined" ? ns.length - 1 : end;
24083         var nodes = [];
24084         if(start <= end){
24085             for(var i = start; i <= end; i++){
24086                 nodes.push(ns[i]);
24087             }
24088         } else{
24089             for(var i = start; i >= end; i--){
24090                 nodes.push(ns[i]);
24091             }
24092         }
24093         return nodes;
24094     },
24095
24096     /**
24097      * Finds the index of the passed node
24098      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24099      * @return {Number} The index of the node or -1
24100      */
24101     indexOf : function(node){
24102         node = this.getNode(node);
24103         if(typeof node.nodeIndex == "number"){
24104             return node.nodeIndex;
24105         }
24106         var ns = this.nodes;
24107         for(var i = 0, len = ns.length; i < len; i++){
24108             if(ns[i] == node){
24109                 return i;
24110             }
24111         }
24112         return -1;
24113     }
24114 });
24115 /*
24116  * Based on:
24117  * Ext JS Library 1.1.1
24118  * Copyright(c) 2006-2007, Ext JS, LLC.
24119  *
24120  * Originally Released Under LGPL - original licence link has changed is not relivant.
24121  *
24122  * Fork - LGPL
24123  * <script type="text/javascript">
24124  */
24125
24126 /**
24127  * @class Roo.JsonView
24128  * @extends Roo.View
24129  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24130 <pre><code>
24131 var view = new Roo.JsonView({
24132     container: "my-element",
24133     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24134     multiSelect: true, 
24135     jsonRoot: "data" 
24136 });
24137
24138 // listen for node click?
24139 view.on("click", function(vw, index, node, e){
24140     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24141 });
24142
24143 // direct load of JSON data
24144 view.load("foobar.php");
24145
24146 // Example from my blog list
24147 var tpl = new Roo.Template(
24148     '&lt;div class="entry"&gt;' +
24149     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24150     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24151     "&lt;/div&gt;&lt;hr /&gt;"
24152 );
24153
24154 var moreView = new Roo.JsonView({
24155     container :  "entry-list", 
24156     template : tpl,
24157     jsonRoot: "posts"
24158 });
24159 moreView.on("beforerender", this.sortEntries, this);
24160 moreView.load({
24161     url: "/blog/get-posts.php",
24162     params: "allposts=true",
24163     text: "Loading Blog Entries..."
24164 });
24165 </code></pre>
24166
24167 * Note: old code is supported with arguments : (container, template, config)
24168
24169
24170  * @constructor
24171  * Create a new JsonView
24172  * 
24173  * @param {Object} config The config object
24174  * 
24175  */
24176 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24177     
24178     
24179     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24180
24181     var um = this.el.getUpdateManager();
24182     um.setRenderer(this);
24183     um.on("update", this.onLoad, this);
24184     um.on("failure", this.onLoadException, this);
24185
24186     /**
24187      * @event beforerender
24188      * Fires before rendering of the downloaded JSON data.
24189      * @param {Roo.JsonView} this
24190      * @param {Object} data The JSON data loaded
24191      */
24192     /**
24193      * @event load
24194      * Fires when data is loaded.
24195      * @param {Roo.JsonView} this
24196      * @param {Object} data The JSON data loaded
24197      * @param {Object} response The raw Connect response object
24198      */
24199     /**
24200      * @event loadexception
24201      * Fires when loading fails.
24202      * @param {Roo.JsonView} this
24203      * @param {Object} response The raw Connect response object
24204      */
24205     this.addEvents({
24206         'beforerender' : true,
24207         'load' : true,
24208         'loadexception' : true
24209     });
24210 };
24211 Roo.extend(Roo.JsonView, Roo.View, {
24212     /**
24213      * @type {String} The root property in the loaded JSON object that contains the data
24214      */
24215     jsonRoot : "",
24216
24217     /**
24218      * Refreshes the view.
24219      */
24220     refresh : function(){
24221         this.clearSelections();
24222         this.el.update("");
24223         var html = [];
24224         var o = this.jsonData;
24225         if(o && o.length > 0){
24226             for(var i = 0, len = o.length; i < len; i++){
24227                 var data = this.prepareData(o[i], i, o);
24228                 html[html.length] = this.tpl.apply(data);
24229             }
24230         }else{
24231             html.push(this.emptyText);
24232         }
24233         this.el.update(html.join(""));
24234         this.nodes = this.el.dom.childNodes;
24235         this.updateIndexes(0);
24236     },
24237
24238     /**
24239      * 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.
24240      * @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:
24241      <pre><code>
24242      view.load({
24243          url: "your-url.php",
24244          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24245          callback: yourFunction,
24246          scope: yourObject, //(optional scope)
24247          discardUrl: false,
24248          nocache: false,
24249          text: "Loading...",
24250          timeout: 30,
24251          scripts: false
24252      });
24253      </code></pre>
24254      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24255      * 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.
24256      * @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}
24257      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24258      * @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.
24259      */
24260     load : function(){
24261         var um = this.el.getUpdateManager();
24262         um.update.apply(um, arguments);
24263     },
24264
24265     render : function(el, response){
24266         this.clearSelections();
24267         this.el.update("");
24268         var o;
24269         try{
24270             o = Roo.util.JSON.decode(response.responseText);
24271             if(this.jsonRoot){
24272                 
24273                 o = o[this.jsonRoot];
24274             }
24275         } catch(e){
24276         }
24277         /**
24278          * The current JSON data or null
24279          */
24280         this.jsonData = o;
24281         this.beforeRender();
24282         this.refresh();
24283     },
24284
24285 /**
24286  * Get the number of records in the current JSON dataset
24287  * @return {Number}
24288  */
24289     getCount : function(){
24290         return this.jsonData ? this.jsonData.length : 0;
24291     },
24292
24293 /**
24294  * Returns the JSON object for the specified node(s)
24295  * @param {HTMLElement/Array} node The node or an array of nodes
24296  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24297  * you get the JSON object for the node
24298  */
24299     getNodeData : function(node){
24300         if(node instanceof Array){
24301             var data = [];
24302             for(var i = 0, len = node.length; i < len; i++){
24303                 data.push(this.getNodeData(node[i]));
24304             }
24305             return data;
24306         }
24307         return this.jsonData[this.indexOf(node)] || null;
24308     },
24309
24310     beforeRender : function(){
24311         this.snapshot = this.jsonData;
24312         if(this.sortInfo){
24313             this.sort.apply(this, this.sortInfo);
24314         }
24315         this.fireEvent("beforerender", this, this.jsonData);
24316     },
24317
24318     onLoad : function(el, o){
24319         this.fireEvent("load", this, this.jsonData, o);
24320     },
24321
24322     onLoadException : function(el, o){
24323         this.fireEvent("loadexception", this, o);
24324     },
24325
24326 /**
24327  * Filter the data by a specific property.
24328  * @param {String} property A property on your JSON objects
24329  * @param {String/RegExp} value Either string that the property values
24330  * should start with, or a RegExp to test against the property
24331  */
24332     filter : function(property, value){
24333         if(this.jsonData){
24334             var data = [];
24335             var ss = this.snapshot;
24336             if(typeof value == "string"){
24337                 var vlen = value.length;
24338                 if(vlen == 0){
24339                     this.clearFilter();
24340                     return;
24341                 }
24342                 value = value.toLowerCase();
24343                 for(var i = 0, len = ss.length; i < len; i++){
24344                     var o = ss[i];
24345                     if(o[property].substr(0, vlen).toLowerCase() == value){
24346                         data.push(o);
24347                     }
24348                 }
24349             } else if(value.exec){ // regex?
24350                 for(var i = 0, len = ss.length; i < len; i++){
24351                     var o = ss[i];
24352                     if(value.test(o[property])){
24353                         data.push(o);
24354                     }
24355                 }
24356             } else{
24357                 return;
24358             }
24359             this.jsonData = data;
24360             this.refresh();
24361         }
24362     },
24363
24364 /**
24365  * Filter by a function. The passed function will be called with each
24366  * object in the current dataset. If the function returns true the value is kept,
24367  * otherwise it is filtered.
24368  * @param {Function} fn
24369  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24370  */
24371     filterBy : function(fn, scope){
24372         if(this.jsonData){
24373             var data = [];
24374             var ss = this.snapshot;
24375             for(var i = 0, len = ss.length; i < len; i++){
24376                 var o = ss[i];
24377                 if(fn.call(scope || this, o)){
24378                     data.push(o);
24379                 }
24380             }
24381             this.jsonData = data;
24382             this.refresh();
24383         }
24384     },
24385
24386 /**
24387  * Clears the current filter.
24388  */
24389     clearFilter : function(){
24390         if(this.snapshot && this.jsonData != this.snapshot){
24391             this.jsonData = this.snapshot;
24392             this.refresh();
24393         }
24394     },
24395
24396
24397 /**
24398  * Sorts the data for this view and refreshes it.
24399  * @param {String} property A property on your JSON objects to sort on
24400  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24401  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24402  */
24403     sort : function(property, dir, sortType){
24404         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24405         if(this.jsonData){
24406             var p = property;
24407             var dsc = dir && dir.toLowerCase() == "desc";
24408             var f = function(o1, o2){
24409                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24410                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24411                 ;
24412                 if(v1 < v2){
24413                     return dsc ? +1 : -1;
24414                 } else if(v1 > v2){
24415                     return dsc ? -1 : +1;
24416                 } else{
24417                     return 0;
24418                 }
24419             };
24420             this.jsonData.sort(f);
24421             this.refresh();
24422             if(this.jsonData != this.snapshot){
24423                 this.snapshot.sort(f);
24424             }
24425         }
24426     }
24427 });/*
24428  * Based on:
24429  * Ext JS Library 1.1.1
24430  * Copyright(c) 2006-2007, Ext JS, LLC.
24431  *
24432  * Originally Released Under LGPL - original licence link has changed is not relivant.
24433  *
24434  * Fork - LGPL
24435  * <script type="text/javascript">
24436  */
24437  
24438
24439 /**
24440  * @class Roo.ColorPalette
24441  * @extends Roo.Component
24442  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24443  * Here's an example of typical usage:
24444  * <pre><code>
24445 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24446 cp.render('my-div');
24447
24448 cp.on('select', function(palette, selColor){
24449     // do something with selColor
24450 });
24451 </code></pre>
24452  * @constructor
24453  * Create a new ColorPalette
24454  * @param {Object} config The config object
24455  */
24456 Roo.ColorPalette = function(config){
24457     Roo.ColorPalette.superclass.constructor.call(this, config);
24458     this.addEvents({
24459         /**
24460              * @event select
24461              * Fires when a color is selected
24462              * @param {ColorPalette} this
24463              * @param {String} color The 6-digit color hex code (without the # symbol)
24464              */
24465         select: true
24466     });
24467
24468     if(this.handler){
24469         this.on("select", this.handler, this.scope, true);
24470     }
24471 };
24472 Roo.extend(Roo.ColorPalette, Roo.Component, {
24473     /**
24474      * @cfg {String} itemCls
24475      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24476      */
24477     itemCls : "x-color-palette",
24478     /**
24479      * @cfg {String} value
24480      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24481      * the hex codes are case-sensitive.
24482      */
24483     value : null,
24484     clickEvent:'click',
24485     // private
24486     ctype: "Roo.ColorPalette",
24487
24488     /**
24489      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24490      */
24491     allowReselect : false,
24492
24493     /**
24494      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24495      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24496      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24497      * of colors with the width setting until the box is symmetrical.</p>
24498      * <p>You can override individual colors if needed:</p>
24499      * <pre><code>
24500 var cp = new Roo.ColorPalette();
24501 cp.colors[0] = "FF0000";  // change the first box to red
24502 </code></pre>
24503
24504 Or you can provide a custom array of your own for complete control:
24505 <pre><code>
24506 var cp = new Roo.ColorPalette();
24507 cp.colors = ["000000", "993300", "333300"];
24508 </code></pre>
24509      * @type Array
24510      */
24511     colors : [
24512         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24513         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24514         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24515         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24516         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24517     ],
24518
24519     // private
24520     onRender : function(container, position){
24521         var t = new Roo.MasterTemplate(
24522             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24523         );
24524         var c = this.colors;
24525         for(var i = 0, len = c.length; i < len; i++){
24526             t.add([c[i]]);
24527         }
24528         var el = document.createElement("div");
24529         el.className = this.itemCls;
24530         t.overwrite(el);
24531         container.dom.insertBefore(el, position);
24532         this.el = Roo.get(el);
24533         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24534         if(this.clickEvent != 'click'){
24535             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24536         }
24537     },
24538
24539     // private
24540     afterRender : function(){
24541         Roo.ColorPalette.superclass.afterRender.call(this);
24542         if(this.value){
24543             var s = this.value;
24544             this.value = null;
24545             this.select(s);
24546         }
24547     },
24548
24549     // private
24550     handleClick : function(e, t){
24551         e.preventDefault();
24552         if(!this.disabled){
24553             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24554             this.select(c.toUpperCase());
24555         }
24556     },
24557
24558     /**
24559      * Selects the specified color in the palette (fires the select event)
24560      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24561      */
24562     select : function(color){
24563         color = color.replace("#", "");
24564         if(color != this.value || this.allowReselect){
24565             var el = this.el;
24566             if(this.value){
24567                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24568             }
24569             el.child("a.color-"+color).addClass("x-color-palette-sel");
24570             this.value = color;
24571             this.fireEvent("select", this, color);
24572         }
24573     }
24574 });/*
24575  * Based on:
24576  * Ext JS Library 1.1.1
24577  * Copyright(c) 2006-2007, Ext JS, LLC.
24578  *
24579  * Originally Released Under LGPL - original licence link has changed is not relivant.
24580  *
24581  * Fork - LGPL
24582  * <script type="text/javascript">
24583  */
24584  
24585 /**
24586  * @class Roo.DatePicker
24587  * @extends Roo.Component
24588  * Simple date picker class.
24589  * @constructor
24590  * Create a new DatePicker
24591  * @param {Object} config The config object
24592  */
24593 Roo.DatePicker = function(config){
24594     Roo.DatePicker.superclass.constructor.call(this, config);
24595
24596     this.value = config && config.value ?
24597                  config.value.clearTime() : new Date().clearTime();
24598
24599     this.addEvents({
24600         /**
24601              * @event select
24602              * Fires when a date is selected
24603              * @param {DatePicker} this
24604              * @param {Date} date The selected date
24605              */
24606         'select': true,
24607         /**
24608              * @event monthchange
24609              * Fires when the displayed month changes 
24610              * @param {DatePicker} this
24611              * @param {Date} date The selected month
24612              */
24613         'monthchange': true
24614     });
24615
24616     if(this.handler){
24617         this.on("select", this.handler,  this.scope || this);
24618     }
24619     // build the disabledDatesRE
24620     if(!this.disabledDatesRE && this.disabledDates){
24621         var dd = this.disabledDates;
24622         var re = "(?:";
24623         for(var i = 0; i < dd.length; i++){
24624             re += dd[i];
24625             if(i != dd.length-1) re += "|";
24626         }
24627         this.disabledDatesRE = new RegExp(re + ")");
24628     }
24629 };
24630
24631 Roo.extend(Roo.DatePicker, Roo.Component, {
24632     /**
24633      * @cfg {String} todayText
24634      * The text to display on the button that selects the current date (defaults to "Today")
24635      */
24636     todayText : "Today",
24637     /**
24638      * @cfg {String} okText
24639      * The text to display on the ok button
24640      */
24641     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24642     /**
24643      * @cfg {String} cancelText
24644      * The text to display on the cancel button
24645      */
24646     cancelText : "Cancel",
24647     /**
24648      * @cfg {String} todayTip
24649      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24650      */
24651     todayTip : "{0} (Spacebar)",
24652     /**
24653      * @cfg {Date} minDate
24654      * Minimum allowable date (JavaScript date object, defaults to null)
24655      */
24656     minDate : null,
24657     /**
24658      * @cfg {Date} maxDate
24659      * Maximum allowable date (JavaScript date object, defaults to null)
24660      */
24661     maxDate : null,
24662     /**
24663      * @cfg {String} minText
24664      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24665      */
24666     minText : "This date is before the minimum date",
24667     /**
24668      * @cfg {String} maxText
24669      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24670      */
24671     maxText : "This date is after the maximum date",
24672     /**
24673      * @cfg {String} format
24674      * The default date format string which can be overriden for localization support.  The format must be
24675      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24676      */
24677     format : "m/d/y",
24678     /**
24679      * @cfg {Array} disabledDays
24680      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24681      */
24682     disabledDays : null,
24683     /**
24684      * @cfg {String} disabledDaysText
24685      * The tooltip to display when the date falls on a disabled day (defaults to "")
24686      */
24687     disabledDaysText : "",
24688     /**
24689      * @cfg {RegExp} disabledDatesRE
24690      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24691      */
24692     disabledDatesRE : null,
24693     /**
24694      * @cfg {String} disabledDatesText
24695      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24696      */
24697     disabledDatesText : "",
24698     /**
24699      * @cfg {Boolean} constrainToViewport
24700      * True to constrain the date picker to the viewport (defaults to true)
24701      */
24702     constrainToViewport : true,
24703     /**
24704      * @cfg {Array} monthNames
24705      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24706      */
24707     monthNames : Date.monthNames,
24708     /**
24709      * @cfg {Array} dayNames
24710      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24711      */
24712     dayNames : Date.dayNames,
24713     /**
24714      * @cfg {String} nextText
24715      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24716      */
24717     nextText: 'Next Month (Control+Right)',
24718     /**
24719      * @cfg {String} prevText
24720      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24721      */
24722     prevText: 'Previous Month (Control+Left)',
24723     /**
24724      * @cfg {String} monthYearText
24725      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24726      */
24727     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24728     /**
24729      * @cfg {Number} startDay
24730      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24731      */
24732     startDay : 0,
24733     /**
24734      * @cfg {Bool} showClear
24735      * Show a clear button (usefull for date form elements that can be blank.)
24736      */
24737     
24738     showClear: false,
24739     
24740     /**
24741      * Sets the value of the date field
24742      * @param {Date} value The date to set
24743      */
24744     setValue : function(value){
24745         var old = this.value;
24746         this.value = value.clearTime(true);
24747         if(this.el){
24748             this.update(this.value);
24749         }
24750     },
24751
24752     /**
24753      * Gets the current selected value of the date field
24754      * @return {Date} The selected date
24755      */
24756     getValue : function(){
24757         return this.value;
24758     },
24759
24760     // private
24761     focus : function(){
24762         if(this.el){
24763             this.update(this.activeDate);
24764         }
24765     },
24766
24767     // private
24768     onRender : function(container, position){
24769         var m = [
24770              '<table cellspacing="0">',
24771                 '<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>',
24772                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24773         var dn = this.dayNames;
24774         for(var i = 0; i < 7; i++){
24775             var d = this.startDay+i;
24776             if(d > 6){
24777                 d = d-7;
24778             }
24779             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24780         }
24781         m[m.length] = "</tr></thead><tbody><tr>";
24782         for(var i = 0; i < 42; i++) {
24783             if(i % 7 == 0 && i != 0){
24784                 m[m.length] = "</tr><tr>";
24785             }
24786             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24787         }
24788         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24789             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24790
24791         var el = document.createElement("div");
24792         el.className = "x-date-picker";
24793         el.innerHTML = m.join("");
24794
24795         container.dom.insertBefore(el, position);
24796
24797         this.el = Roo.get(el);
24798         this.eventEl = Roo.get(el.firstChild);
24799
24800         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24801             handler: this.showPrevMonth,
24802             scope: this,
24803             preventDefault:true,
24804             stopDefault:true
24805         });
24806
24807         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24808             handler: this.showNextMonth,
24809             scope: this,
24810             preventDefault:true,
24811             stopDefault:true
24812         });
24813
24814         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24815
24816         this.monthPicker = this.el.down('div.x-date-mp');
24817         this.monthPicker.enableDisplayMode('block');
24818         
24819         var kn = new Roo.KeyNav(this.eventEl, {
24820             "left" : function(e){
24821                 e.ctrlKey ?
24822                     this.showPrevMonth() :
24823                     this.update(this.activeDate.add("d", -1));
24824             },
24825
24826             "right" : function(e){
24827                 e.ctrlKey ?
24828                     this.showNextMonth() :
24829                     this.update(this.activeDate.add("d", 1));
24830             },
24831
24832             "up" : function(e){
24833                 e.ctrlKey ?
24834                     this.showNextYear() :
24835                     this.update(this.activeDate.add("d", -7));
24836             },
24837
24838             "down" : function(e){
24839                 e.ctrlKey ?
24840                     this.showPrevYear() :
24841                     this.update(this.activeDate.add("d", 7));
24842             },
24843
24844             "pageUp" : function(e){
24845                 this.showNextMonth();
24846             },
24847
24848             "pageDown" : function(e){
24849                 this.showPrevMonth();
24850             },
24851
24852             "enter" : function(e){
24853                 e.stopPropagation();
24854                 return true;
24855             },
24856
24857             scope : this
24858         });
24859
24860         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24861
24862         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24863
24864         this.el.unselectable();
24865         
24866         this.cells = this.el.select("table.x-date-inner tbody td");
24867         this.textNodes = this.el.query("table.x-date-inner tbody span");
24868
24869         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24870             text: "&#160;",
24871             tooltip: this.monthYearText
24872         });
24873
24874         this.mbtn.on('click', this.showMonthPicker, this);
24875         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24876
24877
24878         var today = (new Date()).dateFormat(this.format);
24879         
24880         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24881         if (this.showClear) {
24882             baseTb.add( new Roo.Toolbar.Fill());
24883         }
24884         baseTb.add({
24885             text: String.format(this.todayText, today),
24886             tooltip: String.format(this.todayTip, today),
24887             handler: this.selectToday,
24888             scope: this
24889         });
24890         
24891         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24892             
24893         //});
24894         if (this.showClear) {
24895             
24896             baseTb.add( new Roo.Toolbar.Fill());
24897             baseTb.add({
24898                 text: '&#160;',
24899                 cls: 'x-btn-icon x-btn-clear',
24900                 handler: function() {
24901                     //this.value = '';
24902                     this.fireEvent("select", this, '');
24903                 },
24904                 scope: this
24905             });
24906         }
24907         
24908         
24909         if(Roo.isIE){
24910             this.el.repaint();
24911         }
24912         this.update(this.value);
24913     },
24914
24915     createMonthPicker : function(){
24916         if(!this.monthPicker.dom.firstChild){
24917             var buf = ['<table border="0" cellspacing="0">'];
24918             for(var i = 0; i < 6; i++){
24919                 buf.push(
24920                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24921                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24922                     i == 0 ?
24923                     '<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>' :
24924                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24925                 );
24926             }
24927             buf.push(
24928                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24929                     this.okText,
24930                     '</button><button type="button" class="x-date-mp-cancel">',
24931                     this.cancelText,
24932                     '</button></td></tr>',
24933                 '</table>'
24934             );
24935             this.monthPicker.update(buf.join(''));
24936             this.monthPicker.on('click', this.onMonthClick, this);
24937             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24938
24939             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24940             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24941
24942             this.mpMonths.each(function(m, a, i){
24943                 i += 1;
24944                 if((i%2) == 0){
24945                     m.dom.xmonth = 5 + Math.round(i * .5);
24946                 }else{
24947                     m.dom.xmonth = Math.round((i-1) * .5);
24948                 }
24949             });
24950         }
24951     },
24952
24953     showMonthPicker : function(){
24954         this.createMonthPicker();
24955         var size = this.el.getSize();
24956         this.monthPicker.setSize(size);
24957         this.monthPicker.child('table').setSize(size);
24958
24959         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24960         this.updateMPMonth(this.mpSelMonth);
24961         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24962         this.updateMPYear(this.mpSelYear);
24963
24964         this.monthPicker.slideIn('t', {duration:.2});
24965     },
24966
24967     updateMPYear : function(y){
24968         this.mpyear = y;
24969         var ys = this.mpYears.elements;
24970         for(var i = 1; i <= 10; i++){
24971             var td = ys[i-1], y2;
24972             if((i%2) == 0){
24973                 y2 = y + Math.round(i * .5);
24974                 td.firstChild.innerHTML = y2;
24975                 td.xyear = y2;
24976             }else{
24977                 y2 = y - (5-Math.round(i * .5));
24978                 td.firstChild.innerHTML = y2;
24979                 td.xyear = y2;
24980             }
24981             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24982         }
24983     },
24984
24985     updateMPMonth : function(sm){
24986         this.mpMonths.each(function(m, a, i){
24987             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24988         });
24989     },
24990
24991     selectMPMonth: function(m){
24992         
24993     },
24994
24995     onMonthClick : function(e, t){
24996         e.stopEvent();
24997         var el = new Roo.Element(t), pn;
24998         if(el.is('button.x-date-mp-cancel')){
24999             this.hideMonthPicker();
25000         }
25001         else if(el.is('button.x-date-mp-ok')){
25002             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25003             this.hideMonthPicker();
25004         }
25005         else if(pn = el.up('td.x-date-mp-month', 2)){
25006             this.mpMonths.removeClass('x-date-mp-sel');
25007             pn.addClass('x-date-mp-sel');
25008             this.mpSelMonth = pn.dom.xmonth;
25009         }
25010         else if(pn = el.up('td.x-date-mp-year', 2)){
25011             this.mpYears.removeClass('x-date-mp-sel');
25012             pn.addClass('x-date-mp-sel');
25013             this.mpSelYear = pn.dom.xyear;
25014         }
25015         else if(el.is('a.x-date-mp-prev')){
25016             this.updateMPYear(this.mpyear-10);
25017         }
25018         else if(el.is('a.x-date-mp-next')){
25019             this.updateMPYear(this.mpyear+10);
25020         }
25021     },
25022
25023     onMonthDblClick : function(e, t){
25024         e.stopEvent();
25025         var el = new Roo.Element(t), pn;
25026         if(pn = el.up('td.x-date-mp-month', 2)){
25027             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25028             this.hideMonthPicker();
25029         }
25030         else if(pn = el.up('td.x-date-mp-year', 2)){
25031             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25032             this.hideMonthPicker();
25033         }
25034     },
25035
25036     hideMonthPicker : function(disableAnim){
25037         if(this.monthPicker){
25038             if(disableAnim === true){
25039                 this.monthPicker.hide();
25040             }else{
25041                 this.monthPicker.slideOut('t', {duration:.2});
25042             }
25043         }
25044     },
25045
25046     // private
25047     showPrevMonth : function(e){
25048         this.update(this.activeDate.add("mo", -1));
25049     },
25050
25051     // private
25052     showNextMonth : function(e){
25053         this.update(this.activeDate.add("mo", 1));
25054     },
25055
25056     // private
25057     showPrevYear : function(){
25058         this.update(this.activeDate.add("y", -1));
25059     },
25060
25061     // private
25062     showNextYear : function(){
25063         this.update(this.activeDate.add("y", 1));
25064     },
25065
25066     // private
25067     handleMouseWheel : function(e){
25068         var delta = e.getWheelDelta();
25069         if(delta > 0){
25070             this.showPrevMonth();
25071             e.stopEvent();
25072         } else if(delta < 0){
25073             this.showNextMonth();
25074             e.stopEvent();
25075         }
25076     },
25077
25078     // private
25079     handleDateClick : function(e, t){
25080         e.stopEvent();
25081         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25082             this.setValue(new Date(t.dateValue));
25083             this.fireEvent("select", this, this.value);
25084         }
25085     },
25086
25087     // private
25088     selectToday : function(){
25089         this.setValue(new Date().clearTime());
25090         this.fireEvent("select", this, this.value);
25091     },
25092
25093     // private
25094     update : function(date)
25095     {
25096         var vd = this.activeDate;
25097         this.activeDate = date;
25098         if(vd && this.el){
25099             var t = date.getTime();
25100             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25101                 this.cells.removeClass("x-date-selected");
25102                 this.cells.each(function(c){
25103                    if(c.dom.firstChild.dateValue == t){
25104                        c.addClass("x-date-selected");
25105                        setTimeout(function(){
25106                             try{c.dom.firstChild.focus();}catch(e){}
25107                        }, 50);
25108                        return false;
25109                    }
25110                 });
25111                 return;
25112             }
25113         }
25114         
25115         var days = date.getDaysInMonth();
25116         var firstOfMonth = date.getFirstDateOfMonth();
25117         var startingPos = firstOfMonth.getDay()-this.startDay;
25118
25119         if(startingPos <= this.startDay){
25120             startingPos += 7;
25121         }
25122
25123         var pm = date.add("mo", -1);
25124         var prevStart = pm.getDaysInMonth()-startingPos;
25125
25126         var cells = this.cells.elements;
25127         var textEls = this.textNodes;
25128         days += startingPos;
25129
25130         // convert everything to numbers so it's fast
25131         var day = 86400000;
25132         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25133         var today = new Date().clearTime().getTime();
25134         var sel = date.clearTime().getTime();
25135         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25136         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25137         var ddMatch = this.disabledDatesRE;
25138         var ddText = this.disabledDatesText;
25139         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25140         var ddaysText = this.disabledDaysText;
25141         var format = this.format;
25142
25143         var setCellClass = function(cal, cell){
25144             cell.title = "";
25145             var t = d.getTime();
25146             cell.firstChild.dateValue = t;
25147             if(t == today){
25148                 cell.className += " x-date-today";
25149                 cell.title = cal.todayText;
25150             }
25151             if(t == sel){
25152                 cell.className += " x-date-selected";
25153                 setTimeout(function(){
25154                     try{cell.firstChild.focus();}catch(e){}
25155                 }, 50);
25156             }
25157             // disabling
25158             if(t < min) {
25159                 cell.className = " x-date-disabled";
25160                 cell.title = cal.minText;
25161                 return;
25162             }
25163             if(t > max) {
25164                 cell.className = " x-date-disabled";
25165                 cell.title = cal.maxText;
25166                 return;
25167             }
25168             if(ddays){
25169                 if(ddays.indexOf(d.getDay()) != -1){
25170                     cell.title = ddaysText;
25171                     cell.className = " x-date-disabled";
25172                 }
25173             }
25174             if(ddMatch && format){
25175                 var fvalue = d.dateFormat(format);
25176                 if(ddMatch.test(fvalue)){
25177                     cell.title = ddText.replace("%0", fvalue);
25178                     cell.className = " x-date-disabled";
25179                 }
25180             }
25181         };
25182
25183         var i = 0;
25184         for(; i < startingPos; i++) {
25185             textEls[i].innerHTML = (++prevStart);
25186             d.setDate(d.getDate()+1);
25187             cells[i].className = "x-date-prevday";
25188             setCellClass(this, cells[i]);
25189         }
25190         for(; i < days; i++){
25191             intDay = i - startingPos + 1;
25192             textEls[i].innerHTML = (intDay);
25193             d.setDate(d.getDate()+1);
25194             cells[i].className = "x-date-active";
25195             setCellClass(this, cells[i]);
25196         }
25197         var extraDays = 0;
25198         for(; i < 42; i++) {
25199              textEls[i].innerHTML = (++extraDays);
25200              d.setDate(d.getDate()+1);
25201              cells[i].className = "x-date-nextday";
25202              setCellClass(this, cells[i]);
25203         }
25204
25205         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25206         this.fireEvent('monthchange', this, date);
25207         
25208         if(!this.internalRender){
25209             var main = this.el.dom.firstChild;
25210             var w = main.offsetWidth;
25211             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25212             Roo.fly(main).setWidth(w);
25213             this.internalRender = true;
25214             // opera does not respect the auto grow header center column
25215             // then, after it gets a width opera refuses to recalculate
25216             // without a second pass
25217             if(Roo.isOpera && !this.secondPass){
25218                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25219                 this.secondPass = true;
25220                 this.update.defer(10, this, [date]);
25221             }
25222         }
25223         
25224         
25225     }
25226 });        /*
25227  * Based on:
25228  * Ext JS Library 1.1.1
25229  * Copyright(c) 2006-2007, Ext JS, LLC.
25230  *
25231  * Originally Released Under LGPL - original licence link has changed is not relivant.
25232  *
25233  * Fork - LGPL
25234  * <script type="text/javascript">
25235  */
25236 /**
25237  * @class Roo.TabPanel
25238  * @extends Roo.util.Observable
25239  * A lightweight tab container.
25240  * <br><br>
25241  * Usage:
25242  * <pre><code>
25243 // basic tabs 1, built from existing content
25244 var tabs = new Roo.TabPanel("tabs1");
25245 tabs.addTab("script", "View Script");
25246 tabs.addTab("markup", "View Markup");
25247 tabs.activate("script");
25248
25249 // more advanced tabs, built from javascript
25250 var jtabs = new Roo.TabPanel("jtabs");
25251 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25252
25253 // set up the UpdateManager
25254 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25255 var updater = tab2.getUpdateManager();
25256 updater.setDefaultUrl("ajax1.htm");
25257 tab2.on('activate', updater.refresh, updater, true);
25258
25259 // Use setUrl for Ajax loading
25260 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25261 tab3.setUrl("ajax2.htm", null, true);
25262
25263 // Disabled tab
25264 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25265 tab4.disable();
25266
25267 jtabs.activate("jtabs-1");
25268  * </code></pre>
25269  * @constructor
25270  * Create a new TabPanel.
25271  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25272  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25273  */
25274 Roo.TabPanel = function(container, config){
25275     /**
25276     * The container element for this TabPanel.
25277     * @type Roo.Element
25278     */
25279     this.el = Roo.get(container, true);
25280     if(config){
25281         if(typeof config == "boolean"){
25282             this.tabPosition = config ? "bottom" : "top";
25283         }else{
25284             Roo.apply(this, config);
25285         }
25286     }
25287     if(this.tabPosition == "bottom"){
25288         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25289         this.el.addClass("x-tabs-bottom");
25290     }
25291     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25292     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25293     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25294     if(Roo.isIE){
25295         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25296     }
25297     if(this.tabPosition != "bottom"){
25298         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25299          * @type Roo.Element
25300          */
25301         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25302         this.el.addClass("x-tabs-top");
25303     }
25304     this.items = [];
25305
25306     this.bodyEl.setStyle("position", "relative");
25307
25308     this.active = null;
25309     this.activateDelegate = this.activate.createDelegate(this);
25310
25311     this.addEvents({
25312         /**
25313          * @event tabchange
25314          * Fires when the active tab changes
25315          * @param {Roo.TabPanel} this
25316          * @param {Roo.TabPanelItem} activePanel The new active tab
25317          */
25318         "tabchange": true,
25319         /**
25320          * @event beforetabchange
25321          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25322          * @param {Roo.TabPanel} this
25323          * @param {Object} e Set cancel to true on this object to cancel the tab change
25324          * @param {Roo.TabPanelItem} tab The tab being changed to
25325          */
25326         "beforetabchange" : true
25327     });
25328
25329     Roo.EventManager.onWindowResize(this.onResize, this);
25330     this.cpad = this.el.getPadding("lr");
25331     this.hiddenCount = 0;
25332
25333
25334     // toolbar on the tabbar support...
25335     if (this.toolbar) {
25336         var tcfg = this.toolbar;
25337         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25338         this.toolbar = new Roo.Toolbar(tcfg);
25339         if (Roo.isSafari) {
25340             var tbl = tcfg.container.child('table', true);
25341             tbl.setAttribute('width', '100%');
25342         }
25343         
25344     }
25345    
25346
25347
25348     Roo.TabPanel.superclass.constructor.call(this);
25349 };
25350
25351 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25352     /*
25353      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25354      */
25355     tabPosition : "top",
25356     /*
25357      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25358      */
25359     currentTabWidth : 0,
25360     /*
25361      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25362      */
25363     minTabWidth : 40,
25364     /*
25365      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25366      */
25367     maxTabWidth : 250,
25368     /*
25369      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25370      */
25371     preferredTabWidth : 175,
25372     /*
25373      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25374      */
25375     resizeTabs : false,
25376     /*
25377      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25378      */
25379     monitorResize : true,
25380     /*
25381      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25382      */
25383     toolbar : false,
25384
25385     /**
25386      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25387      * @param {String} id The id of the div to use <b>or create</b>
25388      * @param {String} text The text for the tab
25389      * @param {String} content (optional) Content to put in the TabPanelItem body
25390      * @param {Boolean} closable (optional) True to create a close icon on the tab
25391      * @return {Roo.TabPanelItem} The created TabPanelItem
25392      */
25393     addTab : function(id, text, content, closable){
25394         var item = new Roo.TabPanelItem(this, id, text, closable);
25395         this.addTabItem(item);
25396         if(content){
25397             item.setContent(content);
25398         }
25399         return item;
25400     },
25401
25402     /**
25403      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25404      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25405      * @return {Roo.TabPanelItem}
25406      */
25407     getTab : function(id){
25408         return this.items[id];
25409     },
25410
25411     /**
25412      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25413      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25414      */
25415     hideTab : function(id){
25416         var t = this.items[id];
25417         if(!t.isHidden()){
25418            t.setHidden(true);
25419            this.hiddenCount++;
25420            this.autoSizeTabs();
25421         }
25422     },
25423
25424     /**
25425      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25426      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25427      */
25428     unhideTab : function(id){
25429         var t = this.items[id];
25430         if(t.isHidden()){
25431            t.setHidden(false);
25432            this.hiddenCount--;
25433            this.autoSizeTabs();
25434         }
25435     },
25436
25437     /**
25438      * Adds an existing {@link Roo.TabPanelItem}.
25439      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25440      */
25441     addTabItem : function(item){
25442         this.items[item.id] = item;
25443         this.items.push(item);
25444         if(this.resizeTabs){
25445            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25446            this.autoSizeTabs();
25447         }else{
25448             item.autoSize();
25449         }
25450     },
25451
25452     /**
25453      * Removes a {@link Roo.TabPanelItem}.
25454      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25455      */
25456     removeTab : function(id){
25457         var items = this.items;
25458         var tab = items[id];
25459         if(!tab) { return; }
25460         var index = items.indexOf(tab);
25461         if(this.active == tab && items.length > 1){
25462             var newTab = this.getNextAvailable(index);
25463             if(newTab) {
25464                 newTab.activate();
25465             }
25466         }
25467         this.stripEl.dom.removeChild(tab.pnode.dom);
25468         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25469             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25470         }
25471         items.splice(index, 1);
25472         delete this.items[tab.id];
25473         tab.fireEvent("close", tab);
25474         tab.purgeListeners();
25475         this.autoSizeTabs();
25476     },
25477
25478     getNextAvailable : function(start){
25479         var items = this.items;
25480         var index = start;
25481         // look for a next tab that will slide over to
25482         // replace the one being removed
25483         while(index < items.length){
25484             var item = items[++index];
25485             if(item && !item.isHidden()){
25486                 return item;
25487             }
25488         }
25489         // if one isn't found select the previous tab (on the left)
25490         index = start;
25491         while(index >= 0){
25492             var item = items[--index];
25493             if(item && !item.isHidden()){
25494                 return item;
25495             }
25496         }
25497         return null;
25498     },
25499
25500     /**
25501      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25502      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25503      */
25504     disableTab : function(id){
25505         var tab = this.items[id];
25506         if(tab && this.active != tab){
25507             tab.disable();
25508         }
25509     },
25510
25511     /**
25512      * Enables a {@link Roo.TabPanelItem} that is disabled.
25513      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25514      */
25515     enableTab : function(id){
25516         var tab = this.items[id];
25517         tab.enable();
25518     },
25519
25520     /**
25521      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25522      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25523      * @return {Roo.TabPanelItem} The TabPanelItem.
25524      */
25525     activate : function(id){
25526         var tab = this.items[id];
25527         if(!tab){
25528             return null;
25529         }
25530         if(tab == this.active || tab.disabled){
25531             return tab;
25532         }
25533         var e = {};
25534         this.fireEvent("beforetabchange", this, e, tab);
25535         if(e.cancel !== true && !tab.disabled){
25536             if(this.active){
25537                 this.active.hide();
25538             }
25539             this.active = this.items[id];
25540             this.active.show();
25541             this.fireEvent("tabchange", this, this.active);
25542         }
25543         return tab;
25544     },
25545
25546     /**
25547      * Gets the active {@link Roo.TabPanelItem}.
25548      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25549      */
25550     getActiveTab : function(){
25551         return this.active;
25552     },
25553
25554     /**
25555      * Updates the tab body element to fit the height of the container element
25556      * for overflow scrolling
25557      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25558      */
25559     syncHeight : function(targetHeight){
25560         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25561         var bm = this.bodyEl.getMargins();
25562         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25563         this.bodyEl.setHeight(newHeight);
25564         return newHeight;
25565     },
25566
25567     onResize : function(){
25568         if(this.monitorResize){
25569             this.autoSizeTabs();
25570         }
25571     },
25572
25573     /**
25574      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25575      */
25576     beginUpdate : function(){
25577         this.updating = true;
25578     },
25579
25580     /**
25581      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25582      */
25583     endUpdate : function(){
25584         this.updating = false;
25585         this.autoSizeTabs();
25586     },
25587
25588     /**
25589      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25590      */
25591     autoSizeTabs : function(){
25592         var count = this.items.length;
25593         var vcount = count - this.hiddenCount;
25594         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25595         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25596         var availWidth = Math.floor(w / vcount);
25597         var b = this.stripBody;
25598         if(b.getWidth() > w){
25599             var tabs = this.items;
25600             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25601             if(availWidth < this.minTabWidth){
25602                 /*if(!this.sleft){    // incomplete scrolling code
25603                     this.createScrollButtons();
25604                 }
25605                 this.showScroll();
25606                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25607             }
25608         }else{
25609             if(this.currentTabWidth < this.preferredTabWidth){
25610                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25611             }
25612         }
25613     },
25614
25615     /**
25616      * Returns the number of tabs in this TabPanel.
25617      * @return {Number}
25618      */
25619      getCount : function(){
25620          return this.items.length;
25621      },
25622
25623     /**
25624      * Resizes all the tabs to the passed width
25625      * @param {Number} The new width
25626      */
25627     setTabWidth : function(width){
25628         this.currentTabWidth = width;
25629         for(var i = 0, len = this.items.length; i < len; i++) {
25630                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25631         }
25632     },
25633
25634     /**
25635      * Destroys this TabPanel
25636      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25637      */
25638     destroy : function(removeEl){
25639         Roo.EventManager.removeResizeListener(this.onResize, this);
25640         for(var i = 0, len = this.items.length; i < len; i++){
25641             this.items[i].purgeListeners();
25642         }
25643         if(removeEl === true){
25644             this.el.update("");
25645             this.el.remove();
25646         }
25647     }
25648 });
25649
25650 /**
25651  * @class Roo.TabPanelItem
25652  * @extends Roo.util.Observable
25653  * Represents an individual item (tab plus body) in a TabPanel.
25654  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25655  * @param {String} id The id of this TabPanelItem
25656  * @param {String} text The text for the tab of this TabPanelItem
25657  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25658  */
25659 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25660     /**
25661      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25662      * @type Roo.TabPanel
25663      */
25664     this.tabPanel = tabPanel;
25665     /**
25666      * The id for this TabPanelItem
25667      * @type String
25668      */
25669     this.id = id;
25670     /** @private */
25671     this.disabled = false;
25672     /** @private */
25673     this.text = text;
25674     /** @private */
25675     this.loaded = false;
25676     this.closable = closable;
25677
25678     /**
25679      * The body element for this TabPanelItem.
25680      * @type Roo.Element
25681      */
25682     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25683     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25684     this.bodyEl.setStyle("display", "block");
25685     this.bodyEl.setStyle("zoom", "1");
25686     this.hideAction();
25687
25688     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25689     /** @private */
25690     this.el = Roo.get(els.el, true);
25691     this.inner = Roo.get(els.inner, true);
25692     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25693     this.pnode = Roo.get(els.el.parentNode, true);
25694     this.el.on("mousedown", this.onTabMouseDown, this);
25695     this.el.on("click", this.onTabClick, this);
25696     /** @private */
25697     if(closable){
25698         var c = Roo.get(els.close, true);
25699         c.dom.title = this.closeText;
25700         c.addClassOnOver("close-over");
25701         c.on("click", this.closeClick, this);
25702      }
25703
25704     this.addEvents({
25705          /**
25706          * @event activate
25707          * Fires when this tab becomes the active tab.
25708          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25709          * @param {Roo.TabPanelItem} this
25710          */
25711         "activate": true,
25712         /**
25713          * @event beforeclose
25714          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25715          * @param {Roo.TabPanelItem} this
25716          * @param {Object} e Set cancel to true on this object to cancel the close.
25717          */
25718         "beforeclose": true,
25719         /**
25720          * @event close
25721          * Fires when this tab is closed.
25722          * @param {Roo.TabPanelItem} this
25723          */
25724          "close": true,
25725         /**
25726          * @event deactivate
25727          * Fires when this tab is no longer the active tab.
25728          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25729          * @param {Roo.TabPanelItem} this
25730          */
25731          "deactivate" : true
25732     });
25733     this.hidden = false;
25734
25735     Roo.TabPanelItem.superclass.constructor.call(this);
25736 };
25737
25738 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25739     purgeListeners : function(){
25740        Roo.util.Observable.prototype.purgeListeners.call(this);
25741        this.el.removeAllListeners();
25742     },
25743     /**
25744      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25745      */
25746     show : function(){
25747         this.pnode.addClass("on");
25748         this.showAction();
25749         if(Roo.isOpera){
25750             this.tabPanel.stripWrap.repaint();
25751         }
25752         this.fireEvent("activate", this.tabPanel, this);
25753     },
25754
25755     /**
25756      * Returns true if this tab is the active tab.
25757      * @return {Boolean}
25758      */
25759     isActive : function(){
25760         return this.tabPanel.getActiveTab() == this;
25761     },
25762
25763     /**
25764      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25765      */
25766     hide : function(){
25767         this.pnode.removeClass("on");
25768         this.hideAction();
25769         this.fireEvent("deactivate", this.tabPanel, this);
25770     },
25771
25772     hideAction : function(){
25773         this.bodyEl.hide();
25774         this.bodyEl.setStyle("position", "absolute");
25775         this.bodyEl.setLeft("-20000px");
25776         this.bodyEl.setTop("-20000px");
25777     },
25778
25779     showAction : function(){
25780         this.bodyEl.setStyle("position", "relative");
25781         this.bodyEl.setTop("");
25782         this.bodyEl.setLeft("");
25783         this.bodyEl.show();
25784     },
25785
25786     /**
25787      * Set the tooltip for the tab.
25788      * @param {String} tooltip The tab's tooltip
25789      */
25790     setTooltip : function(text){
25791         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25792             this.textEl.dom.qtip = text;
25793             this.textEl.dom.removeAttribute('title');
25794         }else{
25795             this.textEl.dom.title = text;
25796         }
25797     },
25798
25799     onTabClick : function(e){
25800         e.preventDefault();
25801         this.tabPanel.activate(this.id);
25802     },
25803
25804     onTabMouseDown : function(e){
25805         e.preventDefault();
25806         this.tabPanel.activate(this.id);
25807     },
25808
25809     getWidth : function(){
25810         return this.inner.getWidth();
25811     },
25812
25813     setWidth : function(width){
25814         var iwidth = width - this.pnode.getPadding("lr");
25815         this.inner.setWidth(iwidth);
25816         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25817         this.pnode.setWidth(width);
25818     },
25819
25820     /**
25821      * Show or hide the tab
25822      * @param {Boolean} hidden True to hide or false to show.
25823      */
25824     setHidden : function(hidden){
25825         this.hidden = hidden;
25826         this.pnode.setStyle("display", hidden ? "none" : "");
25827     },
25828
25829     /**
25830      * Returns true if this tab is "hidden"
25831      * @return {Boolean}
25832      */
25833     isHidden : function(){
25834         return this.hidden;
25835     },
25836
25837     /**
25838      * Returns the text for this tab
25839      * @return {String}
25840      */
25841     getText : function(){
25842         return this.text;
25843     },
25844
25845     autoSize : function(){
25846         //this.el.beginMeasure();
25847         this.textEl.setWidth(1);
25848         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25849         //this.el.endMeasure();
25850     },
25851
25852     /**
25853      * Sets the text for the tab (Note: this also sets the tooltip text)
25854      * @param {String} text The tab's text and tooltip
25855      */
25856     setText : function(text){
25857         this.text = text;
25858         this.textEl.update(text);
25859         this.setTooltip(text);
25860         if(!this.tabPanel.resizeTabs){
25861             this.autoSize();
25862         }
25863     },
25864     /**
25865      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25866      */
25867     activate : function(){
25868         this.tabPanel.activate(this.id);
25869     },
25870
25871     /**
25872      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25873      */
25874     disable : function(){
25875         if(this.tabPanel.active != this){
25876             this.disabled = true;
25877             this.pnode.addClass("disabled");
25878         }
25879     },
25880
25881     /**
25882      * Enables this TabPanelItem if it was previously disabled.
25883      */
25884     enable : function(){
25885         this.disabled = false;
25886         this.pnode.removeClass("disabled");
25887     },
25888
25889     /**
25890      * Sets the content for this TabPanelItem.
25891      * @param {String} content The content
25892      * @param {Boolean} loadScripts true to look for and load scripts
25893      */
25894     setContent : function(content, loadScripts){
25895         this.bodyEl.update(content, loadScripts);
25896     },
25897
25898     /**
25899      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25900      * @return {Roo.UpdateManager} The UpdateManager
25901      */
25902     getUpdateManager : function(){
25903         return this.bodyEl.getUpdateManager();
25904     },
25905
25906     /**
25907      * Set a URL to be used to load the content for this TabPanelItem.
25908      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25909      * @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)
25910      * @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)
25911      * @return {Roo.UpdateManager} The UpdateManager
25912      */
25913     setUrl : function(url, params, loadOnce){
25914         if(this.refreshDelegate){
25915             this.un('activate', this.refreshDelegate);
25916         }
25917         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25918         this.on("activate", this.refreshDelegate);
25919         return this.bodyEl.getUpdateManager();
25920     },
25921
25922     /** @private */
25923     _handleRefresh : function(url, params, loadOnce){
25924         if(!loadOnce || !this.loaded){
25925             var updater = this.bodyEl.getUpdateManager();
25926             updater.update(url, params, this._setLoaded.createDelegate(this));
25927         }
25928     },
25929
25930     /**
25931      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25932      *   Will fail silently if the setUrl method has not been called.
25933      *   This does not activate the panel, just updates its content.
25934      */
25935     refresh : function(){
25936         if(this.refreshDelegate){
25937            this.loaded = false;
25938            this.refreshDelegate();
25939         }
25940     },
25941
25942     /** @private */
25943     _setLoaded : function(){
25944         this.loaded = true;
25945     },
25946
25947     /** @private */
25948     closeClick : function(e){
25949         var o = {};
25950         e.stopEvent();
25951         this.fireEvent("beforeclose", this, o);
25952         if(o.cancel !== true){
25953             this.tabPanel.removeTab(this.id);
25954         }
25955     },
25956     /**
25957      * The text displayed in the tooltip for the close icon.
25958      * @type String
25959      */
25960     closeText : "Close this tab"
25961 });
25962
25963 /** @private */
25964 Roo.TabPanel.prototype.createStrip = function(container){
25965     var strip = document.createElement("div");
25966     strip.className = "x-tabs-wrap";
25967     container.appendChild(strip);
25968     return strip;
25969 };
25970 /** @private */
25971 Roo.TabPanel.prototype.createStripList = function(strip){
25972     // div wrapper for retard IE
25973     // returns the "tr" element.
25974     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25975         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25976         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25977     return strip.firstChild.firstChild.firstChild.firstChild;
25978 };
25979 /** @private */
25980 Roo.TabPanel.prototype.createBody = function(container){
25981     var body = document.createElement("div");
25982     Roo.id(body, "tab-body");
25983     Roo.fly(body).addClass("x-tabs-body");
25984     container.appendChild(body);
25985     return body;
25986 };
25987 /** @private */
25988 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25989     var body = Roo.getDom(id);
25990     if(!body){
25991         body = document.createElement("div");
25992         body.id = id;
25993     }
25994     Roo.fly(body).addClass("x-tabs-item-body");
25995     bodyEl.insertBefore(body, bodyEl.firstChild);
25996     return body;
25997 };
25998 /** @private */
25999 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26000     var td = document.createElement("td");
26001     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26002     //stripEl.appendChild(td);
26003     if(closable){
26004         td.className = "x-tabs-closable";
26005         if(!this.closeTpl){
26006             this.closeTpl = new Roo.Template(
26007                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26008                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26009                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26010             );
26011         }
26012         var el = this.closeTpl.overwrite(td, {"text": text});
26013         var close = el.getElementsByTagName("div")[0];
26014         var inner = el.getElementsByTagName("em")[0];
26015         return {"el": el, "close": close, "inner": inner};
26016     } else {
26017         if(!this.tabTpl){
26018             this.tabTpl = new Roo.Template(
26019                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26020                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26021             );
26022         }
26023         var el = this.tabTpl.overwrite(td, {"text": text});
26024         var inner = el.getElementsByTagName("em")[0];
26025         return {"el": el, "inner": inner};
26026     }
26027 };/*
26028  * Based on:
26029  * Ext JS Library 1.1.1
26030  * Copyright(c) 2006-2007, Ext JS, LLC.
26031  *
26032  * Originally Released Under LGPL - original licence link has changed is not relivant.
26033  *
26034  * Fork - LGPL
26035  * <script type="text/javascript">
26036  */
26037
26038 /**
26039  * @class Roo.Button
26040  * @extends Roo.util.Observable
26041  * Simple Button class
26042  * @cfg {String} text The button text
26043  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26044  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26045  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26046  * @cfg {Object} scope The scope of the handler
26047  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26048  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26049  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26050  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26051  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26052  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26053    applies if enableToggle = true)
26054  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26055  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26056   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26057  * @constructor
26058  * Create a new button
26059  * @param {Object} config The config object
26060  */
26061 Roo.Button = function(renderTo, config)
26062 {
26063     if (!config) {
26064         config = renderTo;
26065         renderTo = config.renderTo || false;
26066     }
26067     
26068     Roo.apply(this, config);
26069     this.addEvents({
26070         /**
26071              * @event click
26072              * Fires when this button is clicked
26073              * @param {Button} this
26074              * @param {EventObject} e The click event
26075              */
26076             "click" : true,
26077         /**
26078              * @event toggle
26079              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26080              * @param {Button} this
26081              * @param {Boolean} pressed
26082              */
26083             "toggle" : true,
26084         /**
26085              * @event mouseover
26086              * Fires when the mouse hovers over the button
26087              * @param {Button} this
26088              * @param {Event} e The event object
26089              */
26090         'mouseover' : true,
26091         /**
26092              * @event mouseout
26093              * Fires when the mouse exits the button
26094              * @param {Button} this
26095              * @param {Event} e The event object
26096              */
26097         'mouseout': true,
26098          /**
26099              * @event render
26100              * Fires when the button is rendered
26101              * @param {Button} this
26102              */
26103         'render': true
26104     });
26105     if(this.menu){
26106         this.menu = Roo.menu.MenuMgr.get(this.menu);
26107     }
26108     // register listeners first!!  - so render can be captured..
26109     Roo.util.Observable.call(this);
26110     if(renderTo){
26111         this.render(renderTo);
26112     }
26113     
26114   
26115 };
26116
26117 Roo.extend(Roo.Button, Roo.util.Observable, {
26118     /**
26119      * 
26120      */
26121     
26122     /**
26123      * Read-only. True if this button is hidden
26124      * @type Boolean
26125      */
26126     hidden : false,
26127     /**
26128      * Read-only. True if this button is disabled
26129      * @type Boolean
26130      */
26131     disabled : false,
26132     /**
26133      * Read-only. True if this button is pressed (only if enableToggle = true)
26134      * @type Boolean
26135      */
26136     pressed : false,
26137
26138     /**
26139      * @cfg {Number} tabIndex 
26140      * The DOM tabIndex for this button (defaults to undefined)
26141      */
26142     tabIndex : undefined,
26143
26144     /**
26145      * @cfg {Boolean} enableToggle
26146      * True to enable pressed/not pressed toggling (defaults to false)
26147      */
26148     enableToggle: false,
26149     /**
26150      * @cfg {Mixed} menu
26151      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26152      */
26153     menu : undefined,
26154     /**
26155      * @cfg {String} menuAlign
26156      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26157      */
26158     menuAlign : "tl-bl?",
26159
26160     /**
26161      * @cfg {String} iconCls
26162      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26163      */
26164     iconCls : undefined,
26165     /**
26166      * @cfg {String} type
26167      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26168      */
26169     type : 'button',
26170
26171     // private
26172     menuClassTarget: 'tr',
26173
26174     /**
26175      * @cfg {String} clickEvent
26176      * The type of event to map to the button's event handler (defaults to 'click')
26177      */
26178     clickEvent : 'click',
26179
26180     /**
26181      * @cfg {Boolean} handleMouseEvents
26182      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26183      */
26184     handleMouseEvents : true,
26185
26186     /**
26187      * @cfg {String} tooltipType
26188      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26189      */
26190     tooltipType : 'qtip',
26191
26192     /**
26193      * @cfg {String} cls
26194      * A CSS class to apply to the button's main element.
26195      */
26196     
26197     /**
26198      * @cfg {Roo.Template} template (Optional)
26199      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26200      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26201      * require code modifications if required elements (e.g. a button) aren't present.
26202      */
26203
26204     // private
26205     render : function(renderTo){
26206         var btn;
26207         if(this.hideParent){
26208             this.parentEl = Roo.get(renderTo);
26209         }
26210         if(!this.dhconfig){
26211             if(!this.template){
26212                 if(!Roo.Button.buttonTemplate){
26213                     // hideous table template
26214                     Roo.Button.buttonTemplate = new Roo.Template(
26215                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26216                         '<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>',
26217                         "</tr></tbody></table>");
26218                 }
26219                 this.template = Roo.Button.buttonTemplate;
26220             }
26221             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26222             var btnEl = btn.child("button:first");
26223             btnEl.on('focus', this.onFocus, this);
26224             btnEl.on('blur', this.onBlur, this);
26225             if(this.cls){
26226                 btn.addClass(this.cls);
26227             }
26228             if(this.icon){
26229                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26230             }
26231             if(this.iconCls){
26232                 btnEl.addClass(this.iconCls);
26233                 if(!this.cls){
26234                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26235                 }
26236             }
26237             if(this.tabIndex !== undefined){
26238                 btnEl.dom.tabIndex = this.tabIndex;
26239             }
26240             if(this.tooltip){
26241                 if(typeof this.tooltip == 'object'){
26242                     Roo.QuickTips.tips(Roo.apply({
26243                           target: btnEl.id
26244                     }, this.tooltip));
26245                 } else {
26246                     btnEl.dom[this.tooltipType] = this.tooltip;
26247                 }
26248             }
26249         }else{
26250             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26251         }
26252         this.el = btn;
26253         if(this.id){
26254             this.el.dom.id = this.el.id = this.id;
26255         }
26256         if(this.menu){
26257             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26258             this.menu.on("show", this.onMenuShow, this);
26259             this.menu.on("hide", this.onMenuHide, this);
26260         }
26261         btn.addClass("x-btn");
26262         if(Roo.isIE && !Roo.isIE7){
26263             this.autoWidth.defer(1, this);
26264         }else{
26265             this.autoWidth();
26266         }
26267         if(this.handleMouseEvents){
26268             btn.on("mouseover", this.onMouseOver, this);
26269             btn.on("mouseout", this.onMouseOut, this);
26270             btn.on("mousedown", this.onMouseDown, this);
26271         }
26272         btn.on(this.clickEvent, this.onClick, this);
26273         //btn.on("mouseup", this.onMouseUp, this);
26274         if(this.hidden){
26275             this.hide();
26276         }
26277         if(this.disabled){
26278             this.disable();
26279         }
26280         Roo.ButtonToggleMgr.register(this);
26281         if(this.pressed){
26282             this.el.addClass("x-btn-pressed");
26283         }
26284         if(this.repeat){
26285             var repeater = new Roo.util.ClickRepeater(btn,
26286                 typeof this.repeat == "object" ? this.repeat : {}
26287             );
26288             repeater.on("click", this.onClick,  this);
26289         }
26290         
26291         this.fireEvent('render', this);
26292         
26293     },
26294     /**
26295      * Returns the button's underlying element
26296      * @return {Roo.Element} The element
26297      */
26298     getEl : function(){
26299         return this.el;  
26300     },
26301     
26302     /**
26303      * Destroys this Button and removes any listeners.
26304      */
26305     destroy : function(){
26306         Roo.ButtonToggleMgr.unregister(this);
26307         this.el.removeAllListeners();
26308         this.purgeListeners();
26309         this.el.remove();
26310     },
26311
26312     // private
26313     autoWidth : function(){
26314         if(this.el){
26315             this.el.setWidth("auto");
26316             if(Roo.isIE7 && Roo.isStrict){
26317                 var ib = this.el.child('button');
26318                 if(ib && ib.getWidth() > 20){
26319                     ib.clip();
26320                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26321                 }
26322             }
26323             if(this.minWidth){
26324                 if(this.hidden){
26325                     this.el.beginMeasure();
26326                 }
26327                 if(this.el.getWidth() < this.minWidth){
26328                     this.el.setWidth(this.minWidth);
26329                 }
26330                 if(this.hidden){
26331                     this.el.endMeasure();
26332                 }
26333             }
26334         }
26335     },
26336
26337     /**
26338      * Assigns this button's click handler
26339      * @param {Function} handler The function to call when the button is clicked
26340      * @param {Object} scope (optional) Scope for the function passed in
26341      */
26342     setHandler : function(handler, scope){
26343         this.handler = handler;
26344         this.scope = scope;  
26345     },
26346     
26347     /**
26348      * Sets this button's text
26349      * @param {String} text The button text
26350      */
26351     setText : function(text){
26352         this.text = text;
26353         if(this.el){
26354             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26355         }
26356         this.autoWidth();
26357     },
26358     
26359     /**
26360      * Gets the text for this button
26361      * @return {String} The button text
26362      */
26363     getText : function(){
26364         return this.text;  
26365     },
26366     
26367     /**
26368      * Show this button
26369      */
26370     show: function(){
26371         this.hidden = false;
26372         if(this.el){
26373             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26374         }
26375     },
26376     
26377     /**
26378      * Hide this button
26379      */
26380     hide: function(){
26381         this.hidden = true;
26382         if(this.el){
26383             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26384         }
26385     },
26386     
26387     /**
26388      * Convenience function for boolean show/hide
26389      * @param {Boolean} visible True to show, false to hide
26390      */
26391     setVisible: function(visible){
26392         if(visible) {
26393             this.show();
26394         }else{
26395             this.hide();
26396         }
26397     },
26398     
26399     /**
26400      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26401      * @param {Boolean} state (optional) Force a particular state
26402      */
26403     toggle : function(state){
26404         state = state === undefined ? !this.pressed : state;
26405         if(state != this.pressed){
26406             if(state){
26407                 this.el.addClass("x-btn-pressed");
26408                 this.pressed = true;
26409                 this.fireEvent("toggle", this, true);
26410             }else{
26411                 this.el.removeClass("x-btn-pressed");
26412                 this.pressed = false;
26413                 this.fireEvent("toggle", this, false);
26414             }
26415             if(this.toggleHandler){
26416                 this.toggleHandler.call(this.scope || this, this, state);
26417             }
26418         }
26419     },
26420     
26421     /**
26422      * Focus the button
26423      */
26424     focus : function(){
26425         this.el.child('button:first').focus();
26426     },
26427     
26428     /**
26429      * Disable this button
26430      */
26431     disable : function(){
26432         if(this.el){
26433             this.el.addClass("x-btn-disabled");
26434         }
26435         this.disabled = true;
26436     },
26437     
26438     /**
26439      * Enable this button
26440      */
26441     enable : function(){
26442         if(this.el){
26443             this.el.removeClass("x-btn-disabled");
26444         }
26445         this.disabled = false;
26446     },
26447
26448     /**
26449      * Convenience function for boolean enable/disable
26450      * @param {Boolean} enabled True to enable, false to disable
26451      */
26452     setDisabled : function(v){
26453         this[v !== true ? "enable" : "disable"]();
26454     },
26455
26456     // private
26457     onClick : function(e){
26458         if(e){
26459             e.preventDefault();
26460         }
26461         if(e.button != 0){
26462             return;
26463         }
26464         if(!this.disabled){
26465             if(this.enableToggle){
26466                 this.toggle();
26467             }
26468             if(this.menu && !this.menu.isVisible()){
26469                 this.menu.show(this.el, this.menuAlign);
26470             }
26471             this.fireEvent("click", this, e);
26472             if(this.handler){
26473                 this.el.removeClass("x-btn-over");
26474                 this.handler.call(this.scope || this, this, e);
26475             }
26476         }
26477     },
26478     // private
26479     onMouseOver : function(e){
26480         if(!this.disabled){
26481             this.el.addClass("x-btn-over");
26482             this.fireEvent('mouseover', this, e);
26483         }
26484     },
26485     // private
26486     onMouseOut : function(e){
26487         if(!e.within(this.el,  true)){
26488             this.el.removeClass("x-btn-over");
26489             this.fireEvent('mouseout', this, e);
26490         }
26491     },
26492     // private
26493     onFocus : function(e){
26494         if(!this.disabled){
26495             this.el.addClass("x-btn-focus");
26496         }
26497     },
26498     // private
26499     onBlur : function(e){
26500         this.el.removeClass("x-btn-focus");
26501     },
26502     // private
26503     onMouseDown : function(e){
26504         if(!this.disabled && e.button == 0){
26505             this.el.addClass("x-btn-click");
26506             Roo.get(document).on('mouseup', this.onMouseUp, this);
26507         }
26508     },
26509     // private
26510     onMouseUp : function(e){
26511         if(e.button == 0){
26512             this.el.removeClass("x-btn-click");
26513             Roo.get(document).un('mouseup', this.onMouseUp, this);
26514         }
26515     },
26516     // private
26517     onMenuShow : function(e){
26518         this.el.addClass("x-btn-menu-active");
26519     },
26520     // private
26521     onMenuHide : function(e){
26522         this.el.removeClass("x-btn-menu-active");
26523     }   
26524 });
26525
26526 // Private utility class used by Button
26527 Roo.ButtonToggleMgr = function(){
26528    var groups = {};
26529    
26530    function toggleGroup(btn, state){
26531        if(state){
26532            var g = groups[btn.toggleGroup];
26533            for(var i = 0, l = g.length; i < l; i++){
26534                if(g[i] != btn){
26535                    g[i].toggle(false);
26536                }
26537            }
26538        }
26539    }
26540    
26541    return {
26542        register : function(btn){
26543            if(!btn.toggleGroup){
26544                return;
26545            }
26546            var g = groups[btn.toggleGroup];
26547            if(!g){
26548                g = groups[btn.toggleGroup] = [];
26549            }
26550            g.push(btn);
26551            btn.on("toggle", toggleGroup);
26552        },
26553        
26554        unregister : function(btn){
26555            if(!btn.toggleGroup){
26556                return;
26557            }
26558            var g = groups[btn.toggleGroup];
26559            if(g){
26560                g.remove(btn);
26561                btn.un("toggle", toggleGroup);
26562            }
26563        }
26564    };
26565 }();/*
26566  * Based on:
26567  * Ext JS Library 1.1.1
26568  * Copyright(c) 2006-2007, Ext JS, LLC.
26569  *
26570  * Originally Released Under LGPL - original licence link has changed is not relivant.
26571  *
26572  * Fork - LGPL
26573  * <script type="text/javascript">
26574  */
26575  
26576 /**
26577  * @class Roo.SplitButton
26578  * @extends Roo.Button
26579  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26580  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26581  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26582  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26583  * @cfg {String} arrowTooltip The title attribute of the arrow
26584  * @constructor
26585  * Create a new menu button
26586  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26587  * @param {Object} config The config object
26588  */
26589 Roo.SplitButton = function(renderTo, config){
26590     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26591     /**
26592      * @event arrowclick
26593      * Fires when this button's arrow is clicked
26594      * @param {SplitButton} this
26595      * @param {EventObject} e The click event
26596      */
26597     this.addEvents({"arrowclick":true});
26598 };
26599
26600 Roo.extend(Roo.SplitButton, Roo.Button, {
26601     render : function(renderTo){
26602         // this is one sweet looking template!
26603         var tpl = new Roo.Template(
26604             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26605             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26606             '<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>',
26607             "</tbody></table></td><td>",
26608             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26609             '<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>',
26610             "</tbody></table></td></tr></table>"
26611         );
26612         var btn = tpl.append(renderTo, [this.text, this.type], true);
26613         var btnEl = btn.child("button");
26614         if(this.cls){
26615             btn.addClass(this.cls);
26616         }
26617         if(this.icon){
26618             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26619         }
26620         if(this.iconCls){
26621             btnEl.addClass(this.iconCls);
26622             if(!this.cls){
26623                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26624             }
26625         }
26626         this.el = btn;
26627         if(this.handleMouseEvents){
26628             btn.on("mouseover", this.onMouseOver, this);
26629             btn.on("mouseout", this.onMouseOut, this);
26630             btn.on("mousedown", this.onMouseDown, this);
26631             btn.on("mouseup", this.onMouseUp, this);
26632         }
26633         btn.on(this.clickEvent, this.onClick, this);
26634         if(this.tooltip){
26635             if(typeof this.tooltip == 'object'){
26636                 Roo.QuickTips.tips(Roo.apply({
26637                       target: btnEl.id
26638                 }, this.tooltip));
26639             } else {
26640                 btnEl.dom[this.tooltipType] = this.tooltip;
26641             }
26642         }
26643         if(this.arrowTooltip){
26644             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26645         }
26646         if(this.hidden){
26647             this.hide();
26648         }
26649         if(this.disabled){
26650             this.disable();
26651         }
26652         if(this.pressed){
26653             this.el.addClass("x-btn-pressed");
26654         }
26655         if(Roo.isIE && !Roo.isIE7){
26656             this.autoWidth.defer(1, this);
26657         }else{
26658             this.autoWidth();
26659         }
26660         if(this.menu){
26661             this.menu.on("show", this.onMenuShow, this);
26662             this.menu.on("hide", this.onMenuHide, this);
26663         }
26664         this.fireEvent('render', this);
26665     },
26666
26667     // private
26668     autoWidth : function(){
26669         if(this.el){
26670             var tbl = this.el.child("table:first");
26671             var tbl2 = this.el.child("table:last");
26672             this.el.setWidth("auto");
26673             tbl.setWidth("auto");
26674             if(Roo.isIE7 && Roo.isStrict){
26675                 var ib = this.el.child('button:first');
26676                 if(ib && ib.getWidth() > 20){
26677                     ib.clip();
26678                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26679                 }
26680             }
26681             if(this.minWidth){
26682                 if(this.hidden){
26683                     this.el.beginMeasure();
26684                 }
26685                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26686                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26687                 }
26688                 if(this.hidden){
26689                     this.el.endMeasure();
26690                 }
26691             }
26692             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26693         } 
26694     },
26695     /**
26696      * Sets this button's click handler
26697      * @param {Function} handler The function to call when the button is clicked
26698      * @param {Object} scope (optional) Scope for the function passed above
26699      */
26700     setHandler : function(handler, scope){
26701         this.handler = handler;
26702         this.scope = scope;  
26703     },
26704     
26705     /**
26706      * Sets this button's arrow click handler
26707      * @param {Function} handler The function to call when the arrow is clicked
26708      * @param {Object} scope (optional) Scope for the function passed above
26709      */
26710     setArrowHandler : function(handler, scope){
26711         this.arrowHandler = handler;
26712         this.scope = scope;  
26713     },
26714     
26715     /**
26716      * Focus the button
26717      */
26718     focus : function(){
26719         if(this.el){
26720             this.el.child("button:first").focus();
26721         }
26722     },
26723
26724     // private
26725     onClick : function(e){
26726         e.preventDefault();
26727         if(!this.disabled){
26728             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26729                 if(this.menu && !this.menu.isVisible()){
26730                     this.menu.show(this.el, this.menuAlign);
26731                 }
26732                 this.fireEvent("arrowclick", this, e);
26733                 if(this.arrowHandler){
26734                     this.arrowHandler.call(this.scope || this, this, e);
26735                 }
26736             }else{
26737                 this.fireEvent("click", this, e);
26738                 if(this.handler){
26739                     this.handler.call(this.scope || this, this, e);
26740                 }
26741             }
26742         }
26743     },
26744     // private
26745     onMouseDown : function(e){
26746         if(!this.disabled){
26747             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26748         }
26749     },
26750     // private
26751     onMouseUp : function(e){
26752         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26753     }   
26754 });
26755
26756
26757 // backwards compat
26758 Roo.MenuButton = Roo.SplitButton;/*
26759  * Based on:
26760  * Ext JS Library 1.1.1
26761  * Copyright(c) 2006-2007, Ext JS, LLC.
26762  *
26763  * Originally Released Under LGPL - original licence link has changed is not relivant.
26764  *
26765  * Fork - LGPL
26766  * <script type="text/javascript">
26767  */
26768
26769 /**
26770  * @class Roo.Toolbar
26771  * Basic Toolbar class.
26772  * @constructor
26773  * Creates a new Toolbar
26774  * @param {Object} container The config object
26775  */ 
26776 Roo.Toolbar = function(container, buttons, config)
26777 {
26778     /// old consturctor format still supported..
26779     if(container instanceof Array){ // omit the container for later rendering
26780         buttons = container;
26781         config = buttons;
26782         container = null;
26783     }
26784     if (typeof(container) == 'object' && container.xtype) {
26785         config = container;
26786         container = config.container;
26787         buttons = config.buttons || []; // not really - use items!!
26788     }
26789     var xitems = [];
26790     if (config && config.items) {
26791         xitems = config.items;
26792         delete config.items;
26793     }
26794     Roo.apply(this, config);
26795     this.buttons = buttons;
26796     
26797     if(container){
26798         this.render(container);
26799     }
26800     this.xitems = xitems;
26801     Roo.each(xitems, function(b) {
26802         this.add(b);
26803     }, this);
26804     
26805 };
26806
26807 Roo.Toolbar.prototype = {
26808     /**
26809      * @cfg {Array} items
26810      * array of button configs or elements to add (will be converted to a MixedCollection)
26811      */
26812     
26813     /**
26814      * @cfg {String/HTMLElement/Element} container
26815      * The id or element that will contain the toolbar
26816      */
26817     // private
26818     render : function(ct){
26819         this.el = Roo.get(ct);
26820         if(this.cls){
26821             this.el.addClass(this.cls);
26822         }
26823         // using a table allows for vertical alignment
26824         // 100% width is needed by Safari...
26825         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26826         this.tr = this.el.child("tr", true);
26827         var autoId = 0;
26828         this.items = new Roo.util.MixedCollection(false, function(o){
26829             return o.id || ("item" + (++autoId));
26830         });
26831         if(this.buttons){
26832             this.add.apply(this, this.buttons);
26833             delete this.buttons;
26834         }
26835     },
26836
26837     /**
26838      * Adds element(s) to the toolbar -- this function takes a variable number of 
26839      * arguments of mixed type and adds them to the toolbar.
26840      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26841      * <ul>
26842      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26843      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26844      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26845      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26846      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26847      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26848      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26849      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26850      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26851      * </ul>
26852      * @param {Mixed} arg2
26853      * @param {Mixed} etc.
26854      */
26855     add : function(){
26856         var a = arguments, l = a.length;
26857         for(var i = 0; i < l; i++){
26858             this._add(a[i]);
26859         }
26860     },
26861     // private..
26862     _add : function(el) {
26863         
26864         if (el.xtype) {
26865             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26866         }
26867         
26868         if (el.applyTo){ // some kind of form field
26869             return this.addField(el);
26870         } 
26871         if (el.render){ // some kind of Toolbar.Item
26872             return this.addItem(el);
26873         }
26874         if (typeof el == "string"){ // string
26875             if(el == "separator" || el == "-"){
26876                 return this.addSeparator();
26877             }
26878             if (el == " "){
26879                 return this.addSpacer();
26880             }
26881             if(el == "->"){
26882                 return this.addFill();
26883             }
26884             return this.addText(el);
26885             
26886         }
26887         if(el.tagName){ // element
26888             return this.addElement(el);
26889         }
26890         if(typeof el == "object"){ // must be button config?
26891             return this.addButton(el);
26892         }
26893         // and now what?!?!
26894         return false;
26895         
26896     },
26897     
26898     /**
26899      * Add an Xtype element
26900      * @param {Object} xtype Xtype Object
26901      * @return {Object} created Object
26902      */
26903     addxtype : function(e){
26904         return this.add(e);  
26905     },
26906     
26907     /**
26908      * Returns the Element for this toolbar.
26909      * @return {Roo.Element}
26910      */
26911     getEl : function(){
26912         return this.el;  
26913     },
26914     
26915     /**
26916      * Adds a separator
26917      * @return {Roo.Toolbar.Item} The separator item
26918      */
26919     addSeparator : function(){
26920         return this.addItem(new Roo.Toolbar.Separator());
26921     },
26922
26923     /**
26924      * Adds a spacer element
26925      * @return {Roo.Toolbar.Spacer} The spacer item
26926      */
26927     addSpacer : function(){
26928         return this.addItem(new Roo.Toolbar.Spacer());
26929     },
26930
26931     /**
26932      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26933      * @return {Roo.Toolbar.Fill} The fill item
26934      */
26935     addFill : function(){
26936         return this.addItem(new Roo.Toolbar.Fill());
26937     },
26938
26939     /**
26940      * Adds any standard HTML element to the toolbar
26941      * @param {String/HTMLElement/Element} el The element or id of the element to add
26942      * @return {Roo.Toolbar.Item} The element's item
26943      */
26944     addElement : function(el){
26945         return this.addItem(new Roo.Toolbar.Item(el));
26946     },
26947     /**
26948      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26949      * @type Roo.util.MixedCollection  
26950      */
26951     items : false,
26952      
26953     /**
26954      * Adds any Toolbar.Item or subclass
26955      * @param {Roo.Toolbar.Item} item
26956      * @return {Roo.Toolbar.Item} The item
26957      */
26958     addItem : function(item){
26959         var td = this.nextBlock();
26960         item.render(td);
26961         this.items.add(item);
26962         return item;
26963     },
26964     
26965     /**
26966      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26967      * @param {Object/Array} config A button config or array of configs
26968      * @return {Roo.Toolbar.Button/Array}
26969      */
26970     addButton : function(config){
26971         if(config instanceof Array){
26972             var buttons = [];
26973             for(var i = 0, len = config.length; i < len; i++) {
26974                 buttons.push(this.addButton(config[i]));
26975             }
26976             return buttons;
26977         }
26978         var b = config;
26979         if(!(config instanceof Roo.Toolbar.Button)){
26980             b = config.split ?
26981                 new Roo.Toolbar.SplitButton(config) :
26982                 new Roo.Toolbar.Button(config);
26983         }
26984         var td = this.nextBlock();
26985         b.render(td);
26986         this.items.add(b);
26987         return b;
26988     },
26989     
26990     /**
26991      * Adds text to the toolbar
26992      * @param {String} text The text to add
26993      * @return {Roo.Toolbar.Item} The element's item
26994      */
26995     addText : function(text){
26996         return this.addItem(new Roo.Toolbar.TextItem(text));
26997     },
26998     
26999     /**
27000      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27001      * @param {Number} index The index where the item is to be inserted
27002      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27003      * @return {Roo.Toolbar.Button/Item}
27004      */
27005     insertButton : function(index, item){
27006         if(item instanceof Array){
27007             var buttons = [];
27008             for(var i = 0, len = item.length; i < len; i++) {
27009                buttons.push(this.insertButton(index + i, item[i]));
27010             }
27011             return buttons;
27012         }
27013         if (!(item instanceof Roo.Toolbar.Button)){
27014            item = new Roo.Toolbar.Button(item);
27015         }
27016         var td = document.createElement("td");
27017         this.tr.insertBefore(td, this.tr.childNodes[index]);
27018         item.render(td);
27019         this.items.insert(index, item);
27020         return item;
27021     },
27022     
27023     /**
27024      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27025      * @param {Object} config
27026      * @return {Roo.Toolbar.Item} The element's item
27027      */
27028     addDom : function(config, returnEl){
27029         var td = this.nextBlock();
27030         Roo.DomHelper.overwrite(td, config);
27031         var ti = new Roo.Toolbar.Item(td.firstChild);
27032         ti.render(td);
27033         this.items.add(ti);
27034         return ti;
27035     },
27036
27037     /**
27038      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27039      * @type Roo.util.MixedCollection  
27040      */
27041     fields : false,
27042     
27043     /**
27044      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27045      * Note: the field should not have been rendered yet. For a field that has already been
27046      * rendered, use {@link #addElement}.
27047      * @param {Roo.form.Field} field
27048      * @return {Roo.ToolbarItem}
27049      */
27050      
27051       
27052     addField : function(field) {
27053         if (!this.fields) {
27054             var autoId = 0;
27055             this.fields = new Roo.util.MixedCollection(false, function(o){
27056                 return o.id || ("item" + (++autoId));
27057             });
27058
27059         }
27060         
27061         var td = this.nextBlock();
27062         field.render(td);
27063         var ti = new Roo.Toolbar.Item(td.firstChild);
27064         ti.render(td);
27065         this.items.add(ti);
27066         this.fields.add(field);
27067         return ti;
27068     },
27069     /**
27070      * Hide the toolbar
27071      * @method hide
27072      */
27073      
27074       
27075     hide : function()
27076     {
27077         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27078         this.el.child('div').hide();
27079     },
27080     /**
27081      * Show the toolbar
27082      * @method show
27083      */
27084     show : function()
27085     {
27086         this.el.child('div').show();
27087     },
27088       
27089     // private
27090     nextBlock : function(){
27091         var td = document.createElement("td");
27092         this.tr.appendChild(td);
27093         return td;
27094     },
27095
27096     // private
27097     destroy : function(){
27098         if(this.items){ // rendered?
27099             Roo.destroy.apply(Roo, this.items.items);
27100         }
27101         if(this.fields){ // rendered?
27102             Roo.destroy.apply(Roo, this.fields.items);
27103         }
27104         Roo.Element.uncache(this.el, this.tr);
27105     }
27106 };
27107
27108 /**
27109  * @class Roo.Toolbar.Item
27110  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27111  * @constructor
27112  * Creates a new Item
27113  * @param {HTMLElement} el 
27114  */
27115 Roo.Toolbar.Item = function(el){
27116     this.el = Roo.getDom(el);
27117     this.id = Roo.id(this.el);
27118     this.hidden = false;
27119 };
27120
27121 Roo.Toolbar.Item.prototype = {
27122     
27123     /**
27124      * Get this item's HTML Element
27125      * @return {HTMLElement}
27126      */
27127     getEl : function(){
27128        return this.el;  
27129     },
27130
27131     // private
27132     render : function(td){
27133         this.td = td;
27134         td.appendChild(this.el);
27135     },
27136     
27137     /**
27138      * Removes and destroys this item.
27139      */
27140     destroy : function(){
27141         this.td.parentNode.removeChild(this.td);
27142     },
27143     
27144     /**
27145      * Shows this item.
27146      */
27147     show: function(){
27148         this.hidden = false;
27149         this.td.style.display = "";
27150     },
27151     
27152     /**
27153      * Hides this item.
27154      */
27155     hide: function(){
27156         this.hidden = true;
27157         this.td.style.display = "none";
27158     },
27159     
27160     /**
27161      * Convenience function for boolean show/hide.
27162      * @param {Boolean} visible true to show/false to hide
27163      */
27164     setVisible: function(visible){
27165         if(visible) {
27166             this.show();
27167         }else{
27168             this.hide();
27169         }
27170     },
27171     
27172     /**
27173      * Try to focus this item.
27174      */
27175     focus : function(){
27176         Roo.fly(this.el).focus();
27177     },
27178     
27179     /**
27180      * Disables this item.
27181      */
27182     disable : function(){
27183         Roo.fly(this.td).addClass("x-item-disabled");
27184         this.disabled = true;
27185         this.el.disabled = true;
27186     },
27187     
27188     /**
27189      * Enables this item.
27190      */
27191     enable : function(){
27192         Roo.fly(this.td).removeClass("x-item-disabled");
27193         this.disabled = false;
27194         this.el.disabled = false;
27195     }
27196 };
27197
27198
27199 /**
27200  * @class Roo.Toolbar.Separator
27201  * @extends Roo.Toolbar.Item
27202  * A simple toolbar separator class
27203  * @constructor
27204  * Creates a new Separator
27205  */
27206 Roo.Toolbar.Separator = function(){
27207     var s = document.createElement("span");
27208     s.className = "ytb-sep";
27209     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27210 };
27211 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27212     enable:Roo.emptyFn,
27213     disable:Roo.emptyFn,
27214     focus:Roo.emptyFn
27215 });
27216
27217 /**
27218  * @class Roo.Toolbar.Spacer
27219  * @extends Roo.Toolbar.Item
27220  * A simple element that adds extra horizontal space to a toolbar.
27221  * @constructor
27222  * Creates a new Spacer
27223  */
27224 Roo.Toolbar.Spacer = function(){
27225     var s = document.createElement("div");
27226     s.className = "ytb-spacer";
27227     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27228 };
27229 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27230     enable:Roo.emptyFn,
27231     disable:Roo.emptyFn,
27232     focus:Roo.emptyFn
27233 });
27234
27235 /**
27236  * @class Roo.Toolbar.Fill
27237  * @extends Roo.Toolbar.Spacer
27238  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27239  * @constructor
27240  * Creates a new Spacer
27241  */
27242 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27243     // private
27244     render : function(td){
27245         td.style.width = '100%';
27246         Roo.Toolbar.Fill.superclass.render.call(this, td);
27247     }
27248 });
27249
27250 /**
27251  * @class Roo.Toolbar.TextItem
27252  * @extends Roo.Toolbar.Item
27253  * A simple class that renders text directly into a toolbar.
27254  * @constructor
27255  * Creates a new TextItem
27256  * @param {String} text
27257  */
27258 Roo.Toolbar.TextItem = function(text){
27259     if (typeof(text) == 'object') {
27260         text = text.text;
27261     }
27262     var s = document.createElement("span");
27263     s.className = "ytb-text";
27264     s.innerHTML = text;
27265     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27266 };
27267 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27268     enable:Roo.emptyFn,
27269     disable:Roo.emptyFn,
27270     focus:Roo.emptyFn
27271 });
27272
27273 /**
27274  * @class Roo.Toolbar.Button
27275  * @extends Roo.Button
27276  * A button that renders into a toolbar.
27277  * @constructor
27278  * Creates a new Button
27279  * @param {Object} config A standard {@link Roo.Button} config object
27280  */
27281 Roo.Toolbar.Button = function(config){
27282     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27283 };
27284 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27285     render : function(td){
27286         this.td = td;
27287         Roo.Toolbar.Button.superclass.render.call(this, td);
27288     },
27289     
27290     /**
27291      * Removes and destroys this button
27292      */
27293     destroy : function(){
27294         Roo.Toolbar.Button.superclass.destroy.call(this);
27295         this.td.parentNode.removeChild(this.td);
27296     },
27297     
27298     /**
27299      * Shows this button
27300      */
27301     show: function(){
27302         this.hidden = false;
27303         this.td.style.display = "";
27304     },
27305     
27306     /**
27307      * Hides this button
27308      */
27309     hide: function(){
27310         this.hidden = true;
27311         this.td.style.display = "none";
27312     },
27313
27314     /**
27315      * Disables this item
27316      */
27317     disable : function(){
27318         Roo.fly(this.td).addClass("x-item-disabled");
27319         this.disabled = true;
27320     },
27321
27322     /**
27323      * Enables this item
27324      */
27325     enable : function(){
27326         Roo.fly(this.td).removeClass("x-item-disabled");
27327         this.disabled = false;
27328     }
27329 });
27330 // backwards compat
27331 Roo.ToolbarButton = Roo.Toolbar.Button;
27332
27333 /**
27334  * @class Roo.Toolbar.SplitButton
27335  * @extends Roo.SplitButton
27336  * A menu button that renders into a toolbar.
27337  * @constructor
27338  * Creates a new SplitButton
27339  * @param {Object} config A standard {@link Roo.SplitButton} config object
27340  */
27341 Roo.Toolbar.SplitButton = function(config){
27342     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27343 };
27344 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27345     render : function(td){
27346         this.td = td;
27347         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27348     },
27349     
27350     /**
27351      * Removes and destroys this button
27352      */
27353     destroy : function(){
27354         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27355         this.td.parentNode.removeChild(this.td);
27356     },
27357     
27358     /**
27359      * Shows this button
27360      */
27361     show: function(){
27362         this.hidden = false;
27363         this.td.style.display = "";
27364     },
27365     
27366     /**
27367      * Hides this button
27368      */
27369     hide: function(){
27370         this.hidden = true;
27371         this.td.style.display = "none";
27372     }
27373 });
27374
27375 // backwards compat
27376 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27377  * Based on:
27378  * Ext JS Library 1.1.1
27379  * Copyright(c) 2006-2007, Ext JS, LLC.
27380  *
27381  * Originally Released Under LGPL - original licence link has changed is not relivant.
27382  *
27383  * Fork - LGPL
27384  * <script type="text/javascript">
27385  */
27386  
27387 /**
27388  * @class Roo.PagingToolbar
27389  * @extends Roo.Toolbar
27390  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27391  * @constructor
27392  * Create a new PagingToolbar
27393  * @param {Object} config The config object
27394  */
27395 Roo.PagingToolbar = function(el, ds, config)
27396 {
27397     // old args format still supported... - xtype is prefered..
27398     if (typeof(el) == 'object' && el.xtype) {
27399         // created from xtype...
27400         config = el;
27401         ds = el.dataSource;
27402         el = config.container;
27403     }
27404     var items = [];
27405     if (config.items) {
27406         items = config.items;
27407         config.items = [];
27408     }
27409     
27410     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27411     this.ds = ds;
27412     this.cursor = 0;
27413     this.renderButtons(this.el);
27414     this.bind(ds);
27415     
27416     // supprot items array.
27417    
27418     Roo.each(items, function(e) {
27419         this.add(Roo.factory(e));
27420     },this);
27421     
27422 };
27423
27424 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27425     /**
27426      * @cfg {Roo.data.Store} dataSource
27427      * The underlying data store providing the paged data
27428      */
27429     /**
27430      * @cfg {String/HTMLElement/Element} container
27431      * container The id or element that will contain the toolbar
27432      */
27433     /**
27434      * @cfg {Boolean} displayInfo
27435      * True to display the displayMsg (defaults to false)
27436      */
27437     /**
27438      * @cfg {Number} pageSize
27439      * The number of records to display per page (defaults to 20)
27440      */
27441     pageSize: 20,
27442     /**
27443      * @cfg {String} displayMsg
27444      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27445      */
27446     displayMsg : 'Displaying {0} - {1} of {2}',
27447     /**
27448      * @cfg {String} emptyMsg
27449      * The message to display when no records are found (defaults to "No data to display")
27450      */
27451     emptyMsg : 'No data to display',
27452     /**
27453      * Customizable piece of the default paging text (defaults to "Page")
27454      * @type String
27455      */
27456     beforePageText : "Page",
27457     /**
27458      * Customizable piece of the default paging text (defaults to "of %0")
27459      * @type String
27460      */
27461     afterPageText : "of {0}",
27462     /**
27463      * Customizable piece of the default paging text (defaults to "First Page")
27464      * @type String
27465      */
27466     firstText : "First Page",
27467     /**
27468      * Customizable piece of the default paging text (defaults to "Previous Page")
27469      * @type String
27470      */
27471     prevText : "Previous Page",
27472     /**
27473      * Customizable piece of the default paging text (defaults to "Next Page")
27474      * @type String
27475      */
27476     nextText : "Next Page",
27477     /**
27478      * Customizable piece of the default paging text (defaults to "Last Page")
27479      * @type String
27480      */
27481     lastText : "Last Page",
27482     /**
27483      * Customizable piece of the default paging text (defaults to "Refresh")
27484      * @type String
27485      */
27486     refreshText : "Refresh",
27487
27488     // private
27489     renderButtons : function(el){
27490         Roo.PagingToolbar.superclass.render.call(this, el);
27491         this.first = this.addButton({
27492             tooltip: this.firstText,
27493             cls: "x-btn-icon x-grid-page-first",
27494             disabled: true,
27495             handler: this.onClick.createDelegate(this, ["first"])
27496         });
27497         this.prev = this.addButton({
27498             tooltip: this.prevText,
27499             cls: "x-btn-icon x-grid-page-prev",
27500             disabled: true,
27501             handler: this.onClick.createDelegate(this, ["prev"])
27502         });
27503         //this.addSeparator();
27504         this.add(this.beforePageText);
27505         this.field = Roo.get(this.addDom({
27506            tag: "input",
27507            type: "text",
27508            size: "3",
27509            value: "1",
27510            cls: "x-grid-page-number"
27511         }).el);
27512         this.field.on("keydown", this.onPagingKeydown, this);
27513         this.field.on("focus", function(){this.dom.select();});
27514         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27515         this.field.setHeight(18);
27516         //this.addSeparator();
27517         this.next = this.addButton({
27518             tooltip: this.nextText,
27519             cls: "x-btn-icon x-grid-page-next",
27520             disabled: true,
27521             handler: this.onClick.createDelegate(this, ["next"])
27522         });
27523         this.last = this.addButton({
27524             tooltip: this.lastText,
27525             cls: "x-btn-icon x-grid-page-last",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["last"])
27528         });
27529         //this.addSeparator();
27530         this.loading = this.addButton({
27531             tooltip: this.refreshText,
27532             cls: "x-btn-icon x-grid-loading",
27533             handler: this.onClick.createDelegate(this, ["refresh"])
27534         });
27535
27536         if(this.displayInfo){
27537             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27538         }
27539     },
27540
27541     // private
27542     updateInfo : function(){
27543         if(this.displayEl){
27544             var count = this.ds.getCount();
27545             var msg = count == 0 ?
27546                 this.emptyMsg :
27547                 String.format(
27548                     this.displayMsg,
27549                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27550                 );
27551             this.displayEl.update(msg);
27552         }
27553     },
27554
27555     // private
27556     onLoad : function(ds, r, o){
27557        this.cursor = o.params ? o.params.start : 0;
27558        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27559
27560        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27561        this.field.dom.value = ap;
27562        this.first.setDisabled(ap == 1);
27563        this.prev.setDisabled(ap == 1);
27564        this.next.setDisabled(ap == ps);
27565        this.last.setDisabled(ap == ps);
27566        this.loading.enable();
27567        this.updateInfo();
27568     },
27569
27570     // private
27571     getPageData : function(){
27572         var total = this.ds.getTotalCount();
27573         return {
27574             total : total,
27575             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27576             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27577         };
27578     },
27579
27580     // private
27581     onLoadError : function(){
27582         this.loading.enable();
27583     },
27584
27585     // private
27586     onPagingKeydown : function(e){
27587         var k = e.getKey();
27588         var d = this.getPageData();
27589         if(k == e.RETURN){
27590             var v = this.field.dom.value, pageNum;
27591             if(!v || isNaN(pageNum = parseInt(v, 10))){
27592                 this.field.dom.value = d.activePage;
27593                 return;
27594             }
27595             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27596             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27597             e.stopEvent();
27598         }
27599         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))
27600         {
27601           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27602           this.field.dom.value = pageNum;
27603           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27604           e.stopEvent();
27605         }
27606         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27607         {
27608           var v = this.field.dom.value, pageNum; 
27609           var increment = (e.shiftKey) ? 10 : 1;
27610           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27611             increment *= -1;
27612           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27613             this.field.dom.value = d.activePage;
27614             return;
27615           }
27616           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27617           {
27618             this.field.dom.value = parseInt(v, 10) + increment;
27619             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27620             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27621           }
27622           e.stopEvent();
27623         }
27624     },
27625
27626     // private
27627     beforeLoad : function(){
27628         if(this.loading){
27629             this.loading.disable();
27630         }
27631     },
27632
27633     // private
27634     onClick : function(which){
27635         var ds = this.ds;
27636         switch(which){
27637             case "first":
27638                 ds.load({params:{start: 0, limit: this.pageSize}});
27639             break;
27640             case "prev":
27641                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27642             break;
27643             case "next":
27644                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27645             break;
27646             case "last":
27647                 var total = ds.getTotalCount();
27648                 var extra = total % this.pageSize;
27649                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27650                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27651             break;
27652             case "refresh":
27653                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27654             break;
27655         }
27656     },
27657
27658     /**
27659      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27660      * @param {Roo.data.Store} store The data store to unbind
27661      */
27662     unbind : function(ds){
27663         ds.un("beforeload", this.beforeLoad, this);
27664         ds.un("load", this.onLoad, this);
27665         ds.un("loadexception", this.onLoadError, this);
27666         ds.un("remove", this.updateInfo, this);
27667         ds.un("add", this.updateInfo, this);
27668         this.ds = undefined;
27669     },
27670
27671     /**
27672      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27673      * @param {Roo.data.Store} store The data store to bind
27674      */
27675     bind : function(ds){
27676         ds.on("beforeload", this.beforeLoad, this);
27677         ds.on("load", this.onLoad, this);
27678         ds.on("loadexception", this.onLoadError, this);
27679         ds.on("remove", this.updateInfo, this);
27680         ds.on("add", this.updateInfo, this);
27681         this.ds = ds;
27682     }
27683 });/*
27684  * Based on:
27685  * Ext JS Library 1.1.1
27686  * Copyright(c) 2006-2007, Ext JS, LLC.
27687  *
27688  * Originally Released Under LGPL - original licence link has changed is not relivant.
27689  *
27690  * Fork - LGPL
27691  * <script type="text/javascript">
27692  */
27693
27694 /**
27695  * @class Roo.Resizable
27696  * @extends Roo.util.Observable
27697  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27698  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27699  * 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
27700  * the element will be wrapped for you automatically.</p>
27701  * <p>Here is the list of valid resize handles:</p>
27702  * <pre>
27703 Value   Description
27704 ------  -------------------
27705  'n'     north
27706  's'     south
27707  'e'     east
27708  'w'     west
27709  'nw'    northwest
27710  'sw'    southwest
27711  'se'    southeast
27712  'ne'    northeast
27713  'hd'    horizontal drag
27714  'all'   all
27715 </pre>
27716  * <p>Here's an example showing the creation of a typical Resizable:</p>
27717  * <pre><code>
27718 var resizer = new Roo.Resizable("element-id", {
27719     handles: 'all',
27720     minWidth: 200,
27721     minHeight: 100,
27722     maxWidth: 500,
27723     maxHeight: 400,
27724     pinned: true
27725 });
27726 resizer.on("resize", myHandler);
27727 </code></pre>
27728  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27729  * resizer.east.setDisplayed(false);</p>
27730  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27731  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27732  * resize operation's new size (defaults to [0, 0])
27733  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27734  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27735  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27736  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27737  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27738  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27739  * @cfg {Number} width The width of the element in pixels (defaults to null)
27740  * @cfg {Number} height The height of the element in pixels (defaults to null)
27741  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27742  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27743  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27744  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27745  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27746  * in favor of the handles config option (defaults to false)
27747  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27748  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27749  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27750  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27751  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27752  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27753  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27754  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27755  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27756  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27757  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27758  * @constructor
27759  * Create a new resizable component
27760  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27761  * @param {Object} config configuration options
27762   */
27763 Roo.Resizable = function(el, config)
27764 {
27765     this.el = Roo.get(el);
27766
27767     if(config && config.wrap){
27768         config.resizeChild = this.el;
27769         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27770         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27771         this.el.setStyle("overflow", "hidden");
27772         this.el.setPositioning(config.resizeChild.getPositioning());
27773         config.resizeChild.clearPositioning();
27774         if(!config.width || !config.height){
27775             var csize = config.resizeChild.getSize();
27776             this.el.setSize(csize.width, csize.height);
27777         }
27778         if(config.pinned && !config.adjustments){
27779             config.adjustments = "auto";
27780         }
27781     }
27782
27783     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27784     this.proxy.unselectable();
27785     this.proxy.enableDisplayMode('block');
27786
27787     Roo.apply(this, config);
27788
27789     if(this.pinned){
27790         this.disableTrackOver = true;
27791         this.el.addClass("x-resizable-pinned");
27792     }
27793     // if the element isn't positioned, make it relative
27794     var position = this.el.getStyle("position");
27795     if(position != "absolute" && position != "fixed"){
27796         this.el.setStyle("position", "relative");
27797     }
27798     if(!this.handles){ // no handles passed, must be legacy style
27799         this.handles = 's,e,se';
27800         if(this.multiDirectional){
27801             this.handles += ',n,w';
27802         }
27803     }
27804     if(this.handles == "all"){
27805         this.handles = "n s e w ne nw se sw";
27806     }
27807     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27808     var ps = Roo.Resizable.positions;
27809     for(var i = 0, len = hs.length; i < len; i++){
27810         if(hs[i] && ps[hs[i]]){
27811             var pos = ps[hs[i]];
27812             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27813         }
27814     }
27815     // legacy
27816     this.corner = this.southeast;
27817     
27818     // updateBox = the box can move..
27819     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27820         this.updateBox = true;
27821     }
27822
27823     this.activeHandle = null;
27824
27825     if(this.resizeChild){
27826         if(typeof this.resizeChild == "boolean"){
27827             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27828         }else{
27829             this.resizeChild = Roo.get(this.resizeChild, true);
27830         }
27831     }
27832     
27833     if(this.adjustments == "auto"){
27834         var rc = this.resizeChild;
27835         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27836         if(rc && (hw || hn)){
27837             rc.position("relative");
27838             rc.setLeft(hw ? hw.el.getWidth() : 0);
27839             rc.setTop(hn ? hn.el.getHeight() : 0);
27840         }
27841         this.adjustments = [
27842             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27843             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27844         ];
27845     }
27846
27847     if(this.draggable){
27848         this.dd = this.dynamic ?
27849             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27850         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27851     }
27852
27853     // public events
27854     this.addEvents({
27855         /**
27856          * @event beforeresize
27857          * Fired before resize is allowed. Set enabled to false to cancel resize.
27858          * @param {Roo.Resizable} this
27859          * @param {Roo.EventObject} e The mousedown event
27860          */
27861         "beforeresize" : true,
27862         /**
27863          * @event resize
27864          * Fired after a resize.
27865          * @param {Roo.Resizable} this
27866          * @param {Number} width The new width
27867          * @param {Number} height The new height
27868          * @param {Roo.EventObject} e The mouseup event
27869          */
27870         "resize" : true
27871     });
27872
27873     if(this.width !== null && this.height !== null){
27874         this.resizeTo(this.width, this.height);
27875     }else{
27876         this.updateChildSize();
27877     }
27878     if(Roo.isIE){
27879         this.el.dom.style.zoom = 1;
27880     }
27881     Roo.Resizable.superclass.constructor.call(this);
27882 };
27883
27884 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27885         resizeChild : false,
27886         adjustments : [0, 0],
27887         minWidth : 5,
27888         minHeight : 5,
27889         maxWidth : 10000,
27890         maxHeight : 10000,
27891         enabled : true,
27892         animate : false,
27893         duration : .35,
27894         dynamic : false,
27895         handles : false,
27896         multiDirectional : false,
27897         disableTrackOver : false,
27898         easing : 'easeOutStrong',
27899         widthIncrement : 0,
27900         heightIncrement : 0,
27901         pinned : false,
27902         width : null,
27903         height : null,
27904         preserveRatio : false,
27905         transparent: false,
27906         minX: 0,
27907         minY: 0,
27908         draggable: false,
27909
27910         /**
27911          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27912          */
27913         constrainTo: undefined,
27914         /**
27915          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27916          */
27917         resizeRegion: undefined,
27918
27919
27920     /**
27921      * Perform a manual resize
27922      * @param {Number} width
27923      * @param {Number} height
27924      */
27925     resizeTo : function(width, height){
27926         this.el.setSize(width, height);
27927         this.updateChildSize();
27928         this.fireEvent("resize", this, width, height, null);
27929     },
27930
27931     // private
27932     startSizing : function(e, handle){
27933         this.fireEvent("beforeresize", this, e);
27934         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27935
27936             if(!this.overlay){
27937                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27938                 this.overlay.unselectable();
27939                 this.overlay.enableDisplayMode("block");
27940                 this.overlay.on("mousemove", this.onMouseMove, this);
27941                 this.overlay.on("mouseup", this.onMouseUp, this);
27942             }
27943             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27944
27945             this.resizing = true;
27946             this.startBox = this.el.getBox();
27947             this.startPoint = e.getXY();
27948             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27949                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27950
27951             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27952             this.overlay.show();
27953
27954             if(this.constrainTo) {
27955                 var ct = Roo.get(this.constrainTo);
27956                 this.resizeRegion = ct.getRegion().adjust(
27957                     ct.getFrameWidth('t'),
27958                     ct.getFrameWidth('l'),
27959                     -ct.getFrameWidth('b'),
27960                     -ct.getFrameWidth('r')
27961                 );
27962             }
27963
27964             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27965             this.proxy.show();
27966             this.proxy.setBox(this.startBox);
27967             if(!this.dynamic){
27968                 this.proxy.setStyle('visibility', 'visible');
27969             }
27970         }
27971     },
27972
27973     // private
27974     onMouseDown : function(handle, e){
27975         if(this.enabled){
27976             e.stopEvent();
27977             this.activeHandle = handle;
27978             this.startSizing(e, handle);
27979         }
27980     },
27981
27982     // private
27983     onMouseUp : function(e){
27984         var size = this.resizeElement();
27985         this.resizing = false;
27986         this.handleOut();
27987         this.overlay.hide();
27988         this.proxy.hide();
27989         this.fireEvent("resize", this, size.width, size.height, e);
27990     },
27991
27992     // private
27993     updateChildSize : function(){
27994         if(this.resizeChild){
27995             var el = this.el;
27996             var child = this.resizeChild;
27997             var adj = this.adjustments;
27998             if(el.dom.offsetWidth){
27999                 var b = el.getSize(true);
28000                 child.setSize(b.width+adj[0], b.height+adj[1]);
28001             }
28002             // Second call here for IE
28003             // The first call enables instant resizing and
28004             // the second call corrects scroll bars if they
28005             // exist
28006             if(Roo.isIE){
28007                 setTimeout(function(){
28008                     if(el.dom.offsetWidth){
28009                         var b = el.getSize(true);
28010                         child.setSize(b.width+adj[0], b.height+adj[1]);
28011                     }
28012                 }, 10);
28013             }
28014         }
28015     },
28016
28017     // private
28018     snap : function(value, inc, min){
28019         if(!inc || !value) return value;
28020         var newValue = value;
28021         var m = value % inc;
28022         if(m > 0){
28023             if(m > (inc/2)){
28024                 newValue = value + (inc-m);
28025             }else{
28026                 newValue = value - m;
28027             }
28028         }
28029         return Math.max(min, newValue);
28030     },
28031
28032     // private
28033     resizeElement : function(){
28034         var box = this.proxy.getBox();
28035         if(this.updateBox){
28036             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28037         }else{
28038             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28039         }
28040         this.updateChildSize();
28041         if(!this.dynamic){
28042             this.proxy.hide();
28043         }
28044         return box;
28045     },
28046
28047     // private
28048     constrain : function(v, diff, m, mx){
28049         if(v - diff < m){
28050             diff = v - m;
28051         }else if(v - diff > mx){
28052             diff = mx - v;
28053         }
28054         return diff;
28055     },
28056
28057     // private
28058     onMouseMove : function(e){
28059         if(this.enabled){
28060             try{// try catch so if something goes wrong the user doesn't get hung
28061
28062             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28063                 return;
28064             }
28065
28066             //var curXY = this.startPoint;
28067             var curSize = this.curSize || this.startBox;
28068             var x = this.startBox.x, y = this.startBox.y;
28069             var ox = x, oy = y;
28070             var w = curSize.width, h = curSize.height;
28071             var ow = w, oh = h;
28072             var mw = this.minWidth, mh = this.minHeight;
28073             var mxw = this.maxWidth, mxh = this.maxHeight;
28074             var wi = this.widthIncrement;
28075             var hi = this.heightIncrement;
28076
28077             var eventXY = e.getXY();
28078             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28079             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28080
28081             var pos = this.activeHandle.position;
28082
28083             switch(pos){
28084                 case "east":
28085                     w += diffX;
28086                     w = Math.min(Math.max(mw, w), mxw);
28087                     break;
28088              
28089                 case "south":
28090                     h += diffY;
28091                     h = Math.min(Math.max(mh, h), mxh);
28092                     break;
28093                 case "southeast":
28094                     w += diffX;
28095                     h += diffY;
28096                     w = Math.min(Math.max(mw, w), mxw);
28097                     h = Math.min(Math.max(mh, h), mxh);
28098                     break;
28099                 case "north":
28100                     diffY = this.constrain(h, diffY, mh, mxh);
28101                     y += diffY;
28102                     h -= diffY;
28103                     break;
28104                 case "hdrag":
28105                     
28106                     if (wi) {
28107                         var adiffX = Math.abs(diffX);
28108                         var sub = (adiffX % wi); // how much 
28109                         if (sub > (wi/2)) { // far enough to snap
28110                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28111                         } else {
28112                             // remove difference.. 
28113                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28114                         }
28115                     }
28116                     x += diffX;
28117                     x = Math.max(this.minX, x);
28118                     break;
28119                 case "west":
28120                     diffX = this.constrain(w, diffX, mw, mxw);
28121                     x += diffX;
28122                     w -= diffX;
28123                     break;
28124                 case "northeast":
28125                     w += diffX;
28126                     w = Math.min(Math.max(mw, w), mxw);
28127                     diffY = this.constrain(h, diffY, mh, mxh);
28128                     y += diffY;
28129                     h -= diffY;
28130                     break;
28131                 case "northwest":
28132                     diffX = this.constrain(w, diffX, mw, mxw);
28133                     diffY = this.constrain(h, diffY, mh, mxh);
28134                     y += diffY;
28135                     h -= diffY;
28136                     x += diffX;
28137                     w -= diffX;
28138                     break;
28139                case "southwest":
28140                     diffX = this.constrain(w, diffX, mw, mxw);
28141                     h += diffY;
28142                     h = Math.min(Math.max(mh, h), mxh);
28143                     x += diffX;
28144                     w -= diffX;
28145                     break;
28146             }
28147
28148             var sw = this.snap(w, wi, mw);
28149             var sh = this.snap(h, hi, mh);
28150             if(sw != w || sh != h){
28151                 switch(pos){
28152                     case "northeast":
28153                         y -= sh - h;
28154                     break;
28155                     case "north":
28156                         y -= sh - h;
28157                         break;
28158                     case "southwest":
28159                         x -= sw - w;
28160                     break;
28161                     case "west":
28162                         x -= sw - w;
28163                         break;
28164                     case "northwest":
28165                         x -= sw - w;
28166                         y -= sh - h;
28167                     break;
28168                 }
28169                 w = sw;
28170                 h = sh;
28171             }
28172
28173             if(this.preserveRatio){
28174                 switch(pos){
28175                     case "southeast":
28176                     case "east":
28177                         h = oh * (w/ow);
28178                         h = Math.min(Math.max(mh, h), mxh);
28179                         w = ow * (h/oh);
28180                        break;
28181                     case "south":
28182                         w = ow * (h/oh);
28183                         w = Math.min(Math.max(mw, w), mxw);
28184                         h = oh * (w/ow);
28185                         break;
28186                     case "northeast":
28187                         w = ow * (h/oh);
28188                         w = Math.min(Math.max(mw, w), mxw);
28189                         h = oh * (w/ow);
28190                     break;
28191                     case "north":
28192                         var tw = w;
28193                         w = ow * (h/oh);
28194                         w = Math.min(Math.max(mw, w), mxw);
28195                         h = oh * (w/ow);
28196                         x += (tw - w) / 2;
28197                         break;
28198                     case "southwest":
28199                         h = oh * (w/ow);
28200                         h = Math.min(Math.max(mh, h), mxh);
28201                         var tw = w;
28202                         w = ow * (h/oh);
28203                         x += tw - w;
28204                         break;
28205                     case "west":
28206                         var th = h;
28207                         h = oh * (w/ow);
28208                         h = Math.min(Math.max(mh, h), mxh);
28209                         y += (th - h) / 2;
28210                         var tw = w;
28211                         w = ow * (h/oh);
28212                         x += tw - w;
28213                        break;
28214                     case "northwest":
28215                         var tw = w;
28216                         var th = h;
28217                         h = oh * (w/ow);
28218                         h = Math.min(Math.max(mh, h), mxh);
28219                         w = ow * (h/oh);
28220                         y += th - h;
28221                         x += tw - w;
28222                        break;
28223
28224                 }
28225             }
28226             if (pos == 'hdrag') {
28227                 w = ow;
28228             }
28229             this.proxy.setBounds(x, y, w, h);
28230             if(this.dynamic){
28231                 this.resizeElement();
28232             }
28233             }catch(e){}
28234         }
28235     },
28236
28237     // private
28238     handleOver : function(){
28239         if(this.enabled){
28240             this.el.addClass("x-resizable-over");
28241         }
28242     },
28243
28244     // private
28245     handleOut : function(){
28246         if(!this.resizing){
28247             this.el.removeClass("x-resizable-over");
28248         }
28249     },
28250
28251     /**
28252      * Returns the element this component is bound to.
28253      * @return {Roo.Element}
28254      */
28255     getEl : function(){
28256         return this.el;
28257     },
28258
28259     /**
28260      * Returns the resizeChild element (or null).
28261      * @return {Roo.Element}
28262      */
28263     getResizeChild : function(){
28264         return this.resizeChild;
28265     },
28266
28267     /**
28268      * Destroys this resizable. If the element was wrapped and
28269      * removeEl is not true then the element remains.
28270      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28271      */
28272     destroy : function(removeEl){
28273         this.proxy.remove();
28274         if(this.overlay){
28275             this.overlay.removeAllListeners();
28276             this.overlay.remove();
28277         }
28278         var ps = Roo.Resizable.positions;
28279         for(var k in ps){
28280             if(typeof ps[k] != "function" && this[ps[k]]){
28281                 var h = this[ps[k]];
28282                 h.el.removeAllListeners();
28283                 h.el.remove();
28284             }
28285         }
28286         if(removeEl){
28287             this.el.update("");
28288             this.el.remove();
28289         }
28290     }
28291 });
28292
28293 // private
28294 // hash to map config positions to true positions
28295 Roo.Resizable.positions = {
28296     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28297     hd: "hdrag"
28298 };
28299
28300 // private
28301 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28302     if(!this.tpl){
28303         // only initialize the template if resizable is used
28304         var tpl = Roo.DomHelper.createTemplate(
28305             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28306         );
28307         tpl.compile();
28308         Roo.Resizable.Handle.prototype.tpl = tpl;
28309     }
28310     this.position = pos;
28311     this.rz = rz;
28312     // show north drag fro topdra
28313     var handlepos = pos == 'hdrag' ? 'north' : pos;
28314     
28315     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28316     if (pos == 'hdrag') {
28317         this.el.setStyle('cursor', 'pointer');
28318     }
28319     this.el.unselectable();
28320     if(transparent){
28321         this.el.setOpacity(0);
28322     }
28323     this.el.on("mousedown", this.onMouseDown, this);
28324     if(!disableTrackOver){
28325         this.el.on("mouseover", this.onMouseOver, this);
28326         this.el.on("mouseout", this.onMouseOut, this);
28327     }
28328 };
28329
28330 // private
28331 Roo.Resizable.Handle.prototype = {
28332     afterResize : function(rz){
28333         // do nothing
28334     },
28335     // private
28336     onMouseDown : function(e){
28337         this.rz.onMouseDown(this, e);
28338     },
28339     // private
28340     onMouseOver : function(e){
28341         this.rz.handleOver(this, e);
28342     },
28343     // private
28344     onMouseOut : function(e){
28345         this.rz.handleOut(this, e);
28346     }
28347 };/*
28348  * Based on:
28349  * Ext JS Library 1.1.1
28350  * Copyright(c) 2006-2007, Ext JS, LLC.
28351  *
28352  * Originally Released Under LGPL - original licence link has changed is not relivant.
28353  *
28354  * Fork - LGPL
28355  * <script type="text/javascript">
28356  */
28357
28358 /**
28359  * @class Roo.Editor
28360  * @extends Roo.Component
28361  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28362  * @constructor
28363  * Create a new Editor
28364  * @param {Roo.form.Field} field The Field object (or descendant)
28365  * @param {Object} config The config object
28366  */
28367 Roo.Editor = function(field, config){
28368     Roo.Editor.superclass.constructor.call(this, config);
28369     this.field = field;
28370     this.addEvents({
28371         /**
28372              * @event beforestartedit
28373              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28374              * false from the handler of this event.
28375              * @param {Editor} this
28376              * @param {Roo.Element} boundEl The underlying element bound to this editor
28377              * @param {Mixed} value The field value being set
28378              */
28379         "beforestartedit" : true,
28380         /**
28381              * @event startedit
28382              * Fires when this editor is displayed
28383              * @param {Roo.Element} boundEl The underlying element bound to this editor
28384              * @param {Mixed} value The starting field value
28385              */
28386         "startedit" : true,
28387         /**
28388              * @event beforecomplete
28389              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28390              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28391              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28392              * event will not fire since no edit actually occurred.
28393              * @param {Editor} this
28394              * @param {Mixed} value The current field value
28395              * @param {Mixed} startValue The original field value
28396              */
28397         "beforecomplete" : true,
28398         /**
28399              * @event complete
28400              * Fires after editing is complete and any changed value has been written to the underlying field.
28401              * @param {Editor} this
28402              * @param {Mixed} value The current field value
28403              * @param {Mixed} startValue The original field value
28404              */
28405         "complete" : true,
28406         /**
28407          * @event specialkey
28408          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28409          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28410          * @param {Roo.form.Field} this
28411          * @param {Roo.EventObject} e The event object
28412          */
28413         "specialkey" : true
28414     });
28415 };
28416
28417 Roo.extend(Roo.Editor, Roo.Component, {
28418     /**
28419      * @cfg {Boolean/String} autosize
28420      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28421      * or "height" to adopt the height only (defaults to false)
28422      */
28423     /**
28424      * @cfg {Boolean} revertInvalid
28425      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28426      * validation fails (defaults to true)
28427      */
28428     /**
28429      * @cfg {Boolean} ignoreNoChange
28430      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28431      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28432      * will never be ignored.
28433      */
28434     /**
28435      * @cfg {Boolean} hideEl
28436      * False to keep the bound element visible while the editor is displayed (defaults to true)
28437      */
28438     /**
28439      * @cfg {Mixed} value
28440      * The data value of the underlying field (defaults to "")
28441      */
28442     value : "",
28443     /**
28444      * @cfg {String} alignment
28445      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28446      */
28447     alignment: "c-c?",
28448     /**
28449      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28450      * for bottom-right shadow (defaults to "frame")
28451      */
28452     shadow : "frame",
28453     /**
28454      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28455      */
28456     constrain : false,
28457     /**
28458      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28459      */
28460     completeOnEnter : false,
28461     /**
28462      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28463      */
28464     cancelOnEsc : false,
28465     /**
28466      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28467      */
28468     updateEl : false,
28469
28470     // private
28471     onRender : function(ct, position){
28472         this.el = new Roo.Layer({
28473             shadow: this.shadow,
28474             cls: "x-editor",
28475             parentEl : ct,
28476             shim : this.shim,
28477             shadowOffset:4,
28478             id: this.id,
28479             constrain: this.constrain
28480         });
28481         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28482         if(this.field.msgTarget != 'title'){
28483             this.field.msgTarget = 'qtip';
28484         }
28485         this.field.render(this.el);
28486         if(Roo.isGecko){
28487             this.field.el.dom.setAttribute('autocomplete', 'off');
28488         }
28489         this.field.on("specialkey", this.onSpecialKey, this);
28490         if(this.swallowKeys){
28491             this.field.el.swallowEvent(['keydown','keypress']);
28492         }
28493         this.field.show();
28494         this.field.on("blur", this.onBlur, this);
28495         if(this.field.grow){
28496             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28497         }
28498     },
28499
28500     onSpecialKey : function(field, e)
28501     {
28502         //Roo.log('editor onSpecialKey');
28503         if(this.completeOnEnter && e.getKey() == e.ENTER){
28504             e.stopEvent();
28505             this.completeEdit();
28506             return;
28507         }
28508         // do not fire special key otherwise it might hide close the editor...
28509         if(e.getKey() == e.ENTER){    
28510             return;
28511         }
28512         if(this.cancelOnEsc && e.getKey() == e.ESC){
28513             this.cancelEdit();
28514             return;
28515         } 
28516         this.fireEvent('specialkey', field, e);
28517     
28518     },
28519
28520     /**
28521      * Starts the editing process and shows the editor.
28522      * @param {String/HTMLElement/Element} el The element to edit
28523      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28524       * to the innerHTML of el.
28525      */
28526     startEdit : function(el, value){
28527         if(this.editing){
28528             this.completeEdit();
28529         }
28530         this.boundEl = Roo.get(el);
28531         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28532         if(!this.rendered){
28533             this.render(this.parentEl || document.body);
28534         }
28535         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28536             return;
28537         }
28538         this.startValue = v;
28539         this.field.setValue(v);
28540         if(this.autoSize){
28541             var sz = this.boundEl.getSize();
28542             switch(this.autoSize){
28543                 case "width":
28544                 this.setSize(sz.width,  "");
28545                 break;
28546                 case "height":
28547                 this.setSize("",  sz.height);
28548                 break;
28549                 default:
28550                 this.setSize(sz.width,  sz.height);
28551             }
28552         }
28553         this.el.alignTo(this.boundEl, this.alignment);
28554         this.editing = true;
28555         if(Roo.QuickTips){
28556             Roo.QuickTips.disable();
28557         }
28558         this.show();
28559     },
28560
28561     /**
28562      * Sets the height and width of this editor.
28563      * @param {Number} width The new width
28564      * @param {Number} height The new height
28565      */
28566     setSize : function(w, h){
28567         this.field.setSize(w, h);
28568         if(this.el){
28569             this.el.sync();
28570         }
28571     },
28572
28573     /**
28574      * Realigns the editor to the bound field based on the current alignment config value.
28575      */
28576     realign : function(){
28577         this.el.alignTo(this.boundEl, this.alignment);
28578     },
28579
28580     /**
28581      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28582      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28583      */
28584     completeEdit : function(remainVisible){
28585         if(!this.editing){
28586             return;
28587         }
28588         var v = this.getValue();
28589         if(this.revertInvalid !== false && !this.field.isValid()){
28590             v = this.startValue;
28591             this.cancelEdit(true);
28592         }
28593         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28594             this.editing = false;
28595             this.hide();
28596             return;
28597         }
28598         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28599             this.editing = false;
28600             if(this.updateEl && this.boundEl){
28601                 this.boundEl.update(v);
28602             }
28603             if(remainVisible !== true){
28604                 this.hide();
28605             }
28606             this.fireEvent("complete", this, v, this.startValue);
28607         }
28608     },
28609
28610     // private
28611     onShow : function(){
28612         this.el.show();
28613         if(this.hideEl !== false){
28614             this.boundEl.hide();
28615         }
28616         this.field.show();
28617         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28618             this.fixIEFocus = true;
28619             this.deferredFocus.defer(50, this);
28620         }else{
28621             this.field.focus();
28622         }
28623         this.fireEvent("startedit", this.boundEl, this.startValue);
28624     },
28625
28626     deferredFocus : function(){
28627         if(this.editing){
28628             this.field.focus();
28629         }
28630     },
28631
28632     /**
28633      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28634      * reverted to the original starting value.
28635      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28636      * cancel (defaults to false)
28637      */
28638     cancelEdit : function(remainVisible){
28639         if(this.editing){
28640             this.setValue(this.startValue);
28641             if(remainVisible !== true){
28642                 this.hide();
28643             }
28644         }
28645     },
28646
28647     // private
28648     onBlur : function(){
28649         if(this.allowBlur !== true && this.editing){
28650             this.completeEdit();
28651         }
28652     },
28653
28654     // private
28655     onHide : function(){
28656         if(this.editing){
28657             this.completeEdit();
28658             return;
28659         }
28660         this.field.blur();
28661         if(this.field.collapse){
28662             this.field.collapse();
28663         }
28664         this.el.hide();
28665         if(this.hideEl !== false){
28666             this.boundEl.show();
28667         }
28668         if(Roo.QuickTips){
28669             Roo.QuickTips.enable();
28670         }
28671     },
28672
28673     /**
28674      * Sets the data value of the editor
28675      * @param {Mixed} value Any valid value supported by the underlying field
28676      */
28677     setValue : function(v){
28678         this.field.setValue(v);
28679     },
28680
28681     /**
28682      * Gets the data value of the editor
28683      * @return {Mixed} The data value
28684      */
28685     getValue : function(){
28686         return this.field.getValue();
28687     }
28688 });/*
28689  * Based on:
28690  * Ext JS Library 1.1.1
28691  * Copyright(c) 2006-2007, Ext JS, LLC.
28692  *
28693  * Originally Released Under LGPL - original licence link has changed is not relivant.
28694  *
28695  * Fork - LGPL
28696  * <script type="text/javascript">
28697  */
28698  
28699 /**
28700  * @class Roo.BasicDialog
28701  * @extends Roo.util.Observable
28702  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28703  * <pre><code>
28704 var dlg = new Roo.BasicDialog("my-dlg", {
28705     height: 200,
28706     width: 300,
28707     minHeight: 100,
28708     minWidth: 150,
28709     modal: true,
28710     proxyDrag: true,
28711     shadow: true
28712 });
28713 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28714 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28715 dlg.addButton('Cancel', dlg.hide, dlg);
28716 dlg.show();
28717 </code></pre>
28718   <b>A Dialog should always be a direct child of the body element.</b>
28719  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28720  * @cfg {String} title Default text to display in the title bar (defaults to null)
28721  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28722  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28723  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28724  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28725  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28726  * (defaults to null with no animation)
28727  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28728  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28729  * property for valid values (defaults to 'all')
28730  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28731  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28732  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28733  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28734  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28735  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28736  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28737  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28738  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28739  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28740  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28741  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28742  * draggable = true (defaults to false)
28743  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28744  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28745  * shadow (defaults to false)
28746  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28747  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28748  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28749  * @cfg {Array} buttons Array of buttons
28750  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28751  * @constructor
28752  * Create a new BasicDialog.
28753  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28754  * @param {Object} config Configuration options
28755  */
28756 Roo.BasicDialog = function(el, config){
28757     this.el = Roo.get(el);
28758     var dh = Roo.DomHelper;
28759     if(!this.el && config && config.autoCreate){
28760         if(typeof config.autoCreate == "object"){
28761             if(!config.autoCreate.id){
28762                 config.autoCreate.id = el;
28763             }
28764             this.el = dh.append(document.body,
28765                         config.autoCreate, true);
28766         }else{
28767             this.el = dh.append(document.body,
28768                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28769         }
28770     }
28771     el = this.el;
28772     el.setDisplayed(true);
28773     el.hide = this.hideAction;
28774     this.id = el.id;
28775     el.addClass("x-dlg");
28776
28777     Roo.apply(this, config);
28778
28779     this.proxy = el.createProxy("x-dlg-proxy");
28780     this.proxy.hide = this.hideAction;
28781     this.proxy.setOpacity(.5);
28782     this.proxy.hide();
28783
28784     if(config.width){
28785         el.setWidth(config.width);
28786     }
28787     if(config.height){
28788         el.setHeight(config.height);
28789     }
28790     this.size = el.getSize();
28791     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28792         this.xy = [config.x,config.y];
28793     }else{
28794         this.xy = el.getCenterXY(true);
28795     }
28796     /** The header element @type Roo.Element */
28797     this.header = el.child("> .x-dlg-hd");
28798     /** The body element @type Roo.Element */
28799     this.body = el.child("> .x-dlg-bd");
28800     /** The footer element @type Roo.Element */
28801     this.footer = el.child("> .x-dlg-ft");
28802
28803     if(!this.header){
28804         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28805     }
28806     if(!this.body){
28807         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28808     }
28809
28810     this.header.unselectable();
28811     if(this.title){
28812         this.header.update(this.title);
28813     }
28814     // this element allows the dialog to be focused for keyboard event
28815     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28816     this.focusEl.swallowEvent("click", true);
28817
28818     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28819
28820     // wrap the body and footer for special rendering
28821     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28822     if(this.footer){
28823         this.bwrap.dom.appendChild(this.footer.dom);
28824     }
28825
28826     this.bg = this.el.createChild({
28827         tag: "div", cls:"x-dlg-bg",
28828         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28829     });
28830     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28831
28832
28833     if(this.autoScroll !== false && !this.autoTabs){
28834         this.body.setStyle("overflow", "auto");
28835     }
28836
28837     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28838
28839     if(this.closable !== false){
28840         this.el.addClass("x-dlg-closable");
28841         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28842         this.close.on("click", this.closeClick, this);
28843         this.close.addClassOnOver("x-dlg-close-over");
28844     }
28845     if(this.collapsible !== false){
28846         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28847         this.collapseBtn.on("click", this.collapseClick, this);
28848         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28849         this.header.on("dblclick", this.collapseClick, this);
28850     }
28851     if(this.resizable !== false){
28852         this.el.addClass("x-dlg-resizable");
28853         this.resizer = new Roo.Resizable(el, {
28854             minWidth: this.minWidth || 80,
28855             minHeight:this.minHeight || 80,
28856             handles: this.resizeHandles || "all",
28857             pinned: true
28858         });
28859         this.resizer.on("beforeresize", this.beforeResize, this);
28860         this.resizer.on("resize", this.onResize, this);
28861     }
28862     if(this.draggable !== false){
28863         el.addClass("x-dlg-draggable");
28864         if (!this.proxyDrag) {
28865             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28866         }
28867         else {
28868             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28869         }
28870         dd.setHandleElId(this.header.id);
28871         dd.endDrag = this.endMove.createDelegate(this);
28872         dd.startDrag = this.startMove.createDelegate(this);
28873         dd.onDrag = this.onDrag.createDelegate(this);
28874         dd.scroll = false;
28875         this.dd = dd;
28876     }
28877     if(this.modal){
28878         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28879         this.mask.enableDisplayMode("block");
28880         this.mask.hide();
28881         this.el.addClass("x-dlg-modal");
28882     }
28883     if(this.shadow){
28884         this.shadow = new Roo.Shadow({
28885             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28886             offset : this.shadowOffset
28887         });
28888     }else{
28889         this.shadowOffset = 0;
28890     }
28891     if(Roo.useShims && this.shim !== false){
28892         this.shim = this.el.createShim();
28893         this.shim.hide = this.hideAction;
28894         this.shim.hide();
28895     }else{
28896         this.shim = false;
28897     }
28898     if(this.autoTabs){
28899         this.initTabs();
28900     }
28901     if (this.buttons) { 
28902         var bts= this.buttons;
28903         this.buttons = [];
28904         Roo.each(bts, function(b) {
28905             this.addButton(b);
28906         }, this);
28907     }
28908     
28909     
28910     this.addEvents({
28911         /**
28912          * @event keydown
28913          * Fires when a key is pressed
28914          * @param {Roo.BasicDialog} this
28915          * @param {Roo.EventObject} e
28916          */
28917         "keydown" : true,
28918         /**
28919          * @event move
28920          * Fires when this dialog is moved by the user.
28921          * @param {Roo.BasicDialog} this
28922          * @param {Number} x The new page X
28923          * @param {Number} y The new page Y
28924          */
28925         "move" : true,
28926         /**
28927          * @event resize
28928          * Fires when this dialog is resized by the user.
28929          * @param {Roo.BasicDialog} this
28930          * @param {Number} width The new width
28931          * @param {Number} height The new height
28932          */
28933         "resize" : true,
28934         /**
28935          * @event beforehide
28936          * Fires before this dialog is hidden.
28937          * @param {Roo.BasicDialog} this
28938          */
28939         "beforehide" : true,
28940         /**
28941          * @event hide
28942          * Fires when this dialog is hidden.
28943          * @param {Roo.BasicDialog} this
28944          */
28945         "hide" : true,
28946         /**
28947          * @event beforeshow
28948          * Fires before this dialog is shown.
28949          * @param {Roo.BasicDialog} this
28950          */
28951         "beforeshow" : true,
28952         /**
28953          * @event show
28954          * Fires when this dialog is shown.
28955          * @param {Roo.BasicDialog} this
28956          */
28957         "show" : true
28958     });
28959     el.on("keydown", this.onKeyDown, this);
28960     el.on("mousedown", this.toFront, this);
28961     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28962     this.el.hide();
28963     Roo.DialogManager.register(this);
28964     Roo.BasicDialog.superclass.constructor.call(this);
28965 };
28966
28967 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28968     shadowOffset: Roo.isIE ? 6 : 5,
28969     minHeight: 80,
28970     minWidth: 200,
28971     minButtonWidth: 75,
28972     defaultButton: null,
28973     buttonAlign: "right",
28974     tabTag: 'div',
28975     firstShow: true,
28976
28977     /**
28978      * Sets the dialog title text
28979      * @param {String} text The title text to display
28980      * @return {Roo.BasicDialog} this
28981      */
28982     setTitle : function(text){
28983         this.header.update(text);
28984         return this;
28985     },
28986
28987     // private
28988     closeClick : function(){
28989         this.hide();
28990     },
28991
28992     // private
28993     collapseClick : function(){
28994         this[this.collapsed ? "expand" : "collapse"]();
28995     },
28996
28997     /**
28998      * Collapses the dialog to its minimized state (only the title bar is visible).
28999      * Equivalent to the user clicking the collapse dialog button.
29000      */
29001     collapse : function(){
29002         if(!this.collapsed){
29003             this.collapsed = true;
29004             this.el.addClass("x-dlg-collapsed");
29005             this.restoreHeight = this.el.getHeight();
29006             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29007         }
29008     },
29009
29010     /**
29011      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29012      * clicking the expand dialog button.
29013      */
29014     expand : function(){
29015         if(this.collapsed){
29016             this.collapsed = false;
29017             this.el.removeClass("x-dlg-collapsed");
29018             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29019         }
29020     },
29021
29022     /**
29023      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29024      * @return {Roo.TabPanel} The tabs component
29025      */
29026     initTabs : function(){
29027         var tabs = this.getTabs();
29028         while(tabs.getTab(0)){
29029             tabs.removeTab(0);
29030         }
29031         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29032             var dom = el.dom;
29033             tabs.addTab(Roo.id(dom), dom.title);
29034             dom.title = "";
29035         });
29036         tabs.activate(0);
29037         return tabs;
29038     },
29039
29040     // private
29041     beforeResize : function(){
29042         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29043     },
29044
29045     // private
29046     onResize : function(){
29047         this.refreshSize();
29048         this.syncBodyHeight();
29049         this.adjustAssets();
29050         this.focus();
29051         this.fireEvent("resize", this, this.size.width, this.size.height);
29052     },
29053
29054     // private
29055     onKeyDown : function(e){
29056         if(this.isVisible()){
29057             this.fireEvent("keydown", this, e);
29058         }
29059     },
29060
29061     /**
29062      * Resizes the dialog.
29063      * @param {Number} width
29064      * @param {Number} height
29065      * @return {Roo.BasicDialog} this
29066      */
29067     resizeTo : function(width, height){
29068         this.el.setSize(width, height);
29069         this.size = {width: width, height: height};
29070         this.syncBodyHeight();
29071         if(this.fixedcenter){
29072             this.center();
29073         }
29074         if(this.isVisible()){
29075             this.constrainXY();
29076             this.adjustAssets();
29077         }
29078         this.fireEvent("resize", this, width, height);
29079         return this;
29080     },
29081
29082
29083     /**
29084      * Resizes the dialog to fit the specified content size.
29085      * @param {Number} width
29086      * @param {Number} height
29087      * @return {Roo.BasicDialog} this
29088      */
29089     setContentSize : function(w, h){
29090         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29091         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29092         //if(!this.el.isBorderBox()){
29093             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29094             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29095         //}
29096         if(this.tabs){
29097             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29098             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29099         }
29100         this.resizeTo(w, h);
29101         return this;
29102     },
29103
29104     /**
29105      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29106      * executed in response to a particular key being pressed while the dialog is active.
29107      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29108      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29109      * @param {Function} fn The function to call
29110      * @param {Object} scope (optional) The scope of the function
29111      * @return {Roo.BasicDialog} this
29112      */
29113     addKeyListener : function(key, fn, scope){
29114         var keyCode, shift, ctrl, alt;
29115         if(typeof key == "object" && !(key instanceof Array)){
29116             keyCode = key["key"];
29117             shift = key["shift"];
29118             ctrl = key["ctrl"];
29119             alt = key["alt"];
29120         }else{
29121             keyCode = key;
29122         }
29123         var handler = function(dlg, e){
29124             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29125                 var k = e.getKey();
29126                 if(keyCode instanceof Array){
29127                     for(var i = 0, len = keyCode.length; i < len; i++){
29128                         if(keyCode[i] == k){
29129                           fn.call(scope || window, dlg, k, e);
29130                           return;
29131                         }
29132                     }
29133                 }else{
29134                     if(k == keyCode){
29135                         fn.call(scope || window, dlg, k, e);
29136                     }
29137                 }
29138             }
29139         };
29140         this.on("keydown", handler);
29141         return this;
29142     },
29143
29144     /**
29145      * Returns the TabPanel component (creates it if it doesn't exist).
29146      * Note: If you wish to simply check for the existence of tabs without creating them,
29147      * check for a null 'tabs' property.
29148      * @return {Roo.TabPanel} The tabs component
29149      */
29150     getTabs : function(){
29151         if(!this.tabs){
29152             this.el.addClass("x-dlg-auto-tabs");
29153             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29154             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29155         }
29156         return this.tabs;
29157     },
29158
29159     /**
29160      * Adds a button to the footer section of the dialog.
29161      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29162      * object or a valid Roo.DomHelper element config
29163      * @param {Function} handler The function called when the button is clicked
29164      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29165      * @return {Roo.Button} The new button
29166      */
29167     addButton : function(config, handler, scope){
29168         var dh = Roo.DomHelper;
29169         if(!this.footer){
29170             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29171         }
29172         if(!this.btnContainer){
29173             var tb = this.footer.createChild({
29174
29175                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29176                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29177             }, null, true);
29178             this.btnContainer = tb.firstChild.firstChild.firstChild;
29179         }
29180         var bconfig = {
29181             handler: handler,
29182             scope: scope,
29183             minWidth: this.minButtonWidth,
29184             hideParent:true
29185         };
29186         if(typeof config == "string"){
29187             bconfig.text = config;
29188         }else{
29189             if(config.tag){
29190                 bconfig.dhconfig = config;
29191             }else{
29192                 Roo.apply(bconfig, config);
29193             }
29194         }
29195         var fc = false;
29196         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29197             bconfig.position = Math.max(0, bconfig.position);
29198             fc = this.btnContainer.childNodes[bconfig.position];
29199         }
29200          
29201         var btn = new Roo.Button(
29202             fc ? 
29203                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29204                 : this.btnContainer.appendChild(document.createElement("td")),
29205             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29206             bconfig
29207         );
29208         this.syncBodyHeight();
29209         if(!this.buttons){
29210             /**
29211              * Array of all the buttons that have been added to this dialog via addButton
29212              * @type Array
29213              */
29214             this.buttons = [];
29215         }
29216         this.buttons.push(btn);
29217         return btn;
29218     },
29219
29220     /**
29221      * Sets the default button to be focused when the dialog is displayed.
29222      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29223      * @return {Roo.BasicDialog} this
29224      */
29225     setDefaultButton : function(btn){
29226         this.defaultButton = btn;
29227         return this;
29228     },
29229
29230     // private
29231     getHeaderFooterHeight : function(safe){
29232         var height = 0;
29233         if(this.header){
29234            height += this.header.getHeight();
29235         }
29236         if(this.footer){
29237            var fm = this.footer.getMargins();
29238             height += (this.footer.getHeight()+fm.top+fm.bottom);
29239         }
29240         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29241         height += this.centerBg.getPadding("tb");
29242         return height;
29243     },
29244
29245     // private
29246     syncBodyHeight : function(){
29247         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29248         var height = this.size.height - this.getHeaderFooterHeight(false);
29249         bd.setHeight(height-bd.getMargins("tb"));
29250         var hh = this.header.getHeight();
29251         var h = this.size.height-hh;
29252         cb.setHeight(h);
29253         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29254         bw.setHeight(h-cb.getPadding("tb"));
29255         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29256         bd.setWidth(bw.getWidth(true));
29257         if(this.tabs){
29258             this.tabs.syncHeight();
29259             if(Roo.isIE){
29260                 this.tabs.el.repaint();
29261             }
29262         }
29263     },
29264
29265     /**
29266      * Restores the previous state of the dialog if Roo.state is configured.
29267      * @return {Roo.BasicDialog} this
29268      */
29269     restoreState : function(){
29270         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29271         if(box && box.width){
29272             this.xy = [box.x, box.y];
29273             this.resizeTo(box.width, box.height);
29274         }
29275         return this;
29276     },
29277
29278     // private
29279     beforeShow : function(){
29280         this.expand();
29281         if(this.fixedcenter){
29282             this.xy = this.el.getCenterXY(true);
29283         }
29284         if(this.modal){
29285             Roo.get(document.body).addClass("x-body-masked");
29286             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29287             this.mask.show();
29288         }
29289         this.constrainXY();
29290     },
29291
29292     // private
29293     animShow : function(){
29294         var b = Roo.get(this.animateTarget).getBox();
29295         this.proxy.setSize(b.width, b.height);
29296         this.proxy.setLocation(b.x, b.y);
29297         this.proxy.show();
29298         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29299                     true, .35, this.showEl.createDelegate(this));
29300     },
29301
29302     /**
29303      * Shows the dialog.
29304      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29305      * @return {Roo.BasicDialog} this
29306      */
29307     show : function(animateTarget){
29308         if (this.fireEvent("beforeshow", this) === false){
29309             return;
29310         }
29311         if(this.syncHeightBeforeShow){
29312             this.syncBodyHeight();
29313         }else if(this.firstShow){
29314             this.firstShow = false;
29315             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29316         }
29317         this.animateTarget = animateTarget || this.animateTarget;
29318         if(!this.el.isVisible()){
29319             this.beforeShow();
29320             if(this.animateTarget && Roo.get(this.animateTarget)){
29321                 this.animShow();
29322             }else{
29323                 this.showEl();
29324             }
29325         }
29326         return this;
29327     },
29328
29329     // private
29330     showEl : function(){
29331         this.proxy.hide();
29332         this.el.setXY(this.xy);
29333         this.el.show();
29334         this.adjustAssets(true);
29335         this.toFront();
29336         this.focus();
29337         // IE peekaboo bug - fix found by Dave Fenwick
29338         if(Roo.isIE){
29339             this.el.repaint();
29340         }
29341         this.fireEvent("show", this);
29342     },
29343
29344     /**
29345      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29346      * dialog itself will receive focus.
29347      */
29348     focus : function(){
29349         if(this.defaultButton){
29350             this.defaultButton.focus();
29351         }else{
29352             this.focusEl.focus();
29353         }
29354     },
29355
29356     // private
29357     constrainXY : function(){
29358         if(this.constraintoviewport !== false){
29359             if(!this.viewSize){
29360                 if(this.container){
29361                     var s = this.container.getSize();
29362                     this.viewSize = [s.width, s.height];
29363                 }else{
29364                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29365                 }
29366             }
29367             var s = Roo.get(this.container||document).getScroll();
29368
29369             var x = this.xy[0], y = this.xy[1];
29370             var w = this.size.width, h = this.size.height;
29371             var vw = this.viewSize[0], vh = this.viewSize[1];
29372             // only move it if it needs it
29373             var moved = false;
29374             // first validate right/bottom
29375             if(x + w > vw+s.left){
29376                 x = vw - w;
29377                 moved = true;
29378             }
29379             if(y + h > vh+s.top){
29380                 y = vh - h;
29381                 moved = true;
29382             }
29383             // then make sure top/left isn't negative
29384             if(x < s.left){
29385                 x = s.left;
29386                 moved = true;
29387             }
29388             if(y < s.top){
29389                 y = s.top;
29390                 moved = true;
29391             }
29392             if(moved){
29393                 // cache xy
29394                 this.xy = [x, y];
29395                 if(this.isVisible()){
29396                     this.el.setLocation(x, y);
29397                     this.adjustAssets();
29398                 }
29399             }
29400         }
29401     },
29402
29403     // private
29404     onDrag : function(){
29405         if(!this.proxyDrag){
29406             this.xy = this.el.getXY();
29407             this.adjustAssets();
29408         }
29409     },
29410
29411     // private
29412     adjustAssets : function(doShow){
29413         var x = this.xy[0], y = this.xy[1];
29414         var w = this.size.width, h = this.size.height;
29415         if(doShow === true){
29416             if(this.shadow){
29417                 this.shadow.show(this.el);
29418             }
29419             if(this.shim){
29420                 this.shim.show();
29421             }
29422         }
29423         if(this.shadow && this.shadow.isVisible()){
29424             this.shadow.show(this.el);
29425         }
29426         if(this.shim && this.shim.isVisible()){
29427             this.shim.setBounds(x, y, w, h);
29428         }
29429     },
29430
29431     // private
29432     adjustViewport : function(w, h){
29433         if(!w || !h){
29434             w = Roo.lib.Dom.getViewWidth();
29435             h = Roo.lib.Dom.getViewHeight();
29436         }
29437         // cache the size
29438         this.viewSize = [w, h];
29439         if(this.modal && this.mask.isVisible()){
29440             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29441             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29442         }
29443         if(this.isVisible()){
29444             this.constrainXY();
29445         }
29446     },
29447
29448     /**
29449      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29450      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29451      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29452      */
29453     destroy : function(removeEl){
29454         if(this.isVisible()){
29455             this.animateTarget = null;
29456             this.hide();
29457         }
29458         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29459         if(this.tabs){
29460             this.tabs.destroy(removeEl);
29461         }
29462         Roo.destroy(
29463              this.shim,
29464              this.proxy,
29465              this.resizer,
29466              this.close,
29467              this.mask
29468         );
29469         if(this.dd){
29470             this.dd.unreg();
29471         }
29472         if(this.buttons){
29473            for(var i = 0, len = this.buttons.length; i < len; i++){
29474                this.buttons[i].destroy();
29475            }
29476         }
29477         this.el.removeAllListeners();
29478         if(removeEl === true){
29479             this.el.update("");
29480             this.el.remove();
29481         }
29482         Roo.DialogManager.unregister(this);
29483     },
29484
29485     // private
29486     startMove : function(){
29487         if(this.proxyDrag){
29488             this.proxy.show();
29489         }
29490         if(this.constraintoviewport !== false){
29491             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29492         }
29493     },
29494
29495     // private
29496     endMove : function(){
29497         if(!this.proxyDrag){
29498             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29499         }else{
29500             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29501             this.proxy.hide();
29502         }
29503         this.refreshSize();
29504         this.adjustAssets();
29505         this.focus();
29506         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29507     },
29508
29509     /**
29510      * Brings this dialog to the front of any other visible dialogs
29511      * @return {Roo.BasicDialog} this
29512      */
29513     toFront : function(){
29514         Roo.DialogManager.bringToFront(this);
29515         return this;
29516     },
29517
29518     /**
29519      * Sends this dialog to the back (under) of any other visible dialogs
29520      * @return {Roo.BasicDialog} this
29521      */
29522     toBack : function(){
29523         Roo.DialogManager.sendToBack(this);
29524         return this;
29525     },
29526
29527     /**
29528      * Centers this dialog in the viewport
29529      * @return {Roo.BasicDialog} this
29530      */
29531     center : function(){
29532         var xy = this.el.getCenterXY(true);
29533         this.moveTo(xy[0], xy[1]);
29534         return this;
29535     },
29536
29537     /**
29538      * Moves the dialog's top-left corner to the specified point
29539      * @param {Number} x
29540      * @param {Number} y
29541      * @return {Roo.BasicDialog} this
29542      */
29543     moveTo : function(x, y){
29544         this.xy = [x,y];
29545         if(this.isVisible()){
29546             this.el.setXY(this.xy);
29547             this.adjustAssets();
29548         }
29549         return this;
29550     },
29551
29552     /**
29553      * Aligns the dialog to the specified element
29554      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29555      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29556      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29557      * @return {Roo.BasicDialog} this
29558      */
29559     alignTo : function(element, position, offsets){
29560         this.xy = this.el.getAlignToXY(element, position, offsets);
29561         if(this.isVisible()){
29562             this.el.setXY(this.xy);
29563             this.adjustAssets();
29564         }
29565         return this;
29566     },
29567
29568     /**
29569      * Anchors an element to another element and realigns it when the window is resized.
29570      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29571      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29572      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29573      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29574      * is a number, it is used as the buffer delay (defaults to 50ms).
29575      * @return {Roo.BasicDialog} this
29576      */
29577     anchorTo : function(el, alignment, offsets, monitorScroll){
29578         var action = function(){
29579             this.alignTo(el, alignment, offsets);
29580         };
29581         Roo.EventManager.onWindowResize(action, this);
29582         var tm = typeof monitorScroll;
29583         if(tm != 'undefined'){
29584             Roo.EventManager.on(window, 'scroll', action, this,
29585                 {buffer: tm == 'number' ? monitorScroll : 50});
29586         }
29587         action.call(this);
29588         return this;
29589     },
29590
29591     /**
29592      * Returns true if the dialog is visible
29593      * @return {Boolean}
29594      */
29595     isVisible : function(){
29596         return this.el.isVisible();
29597     },
29598
29599     // private
29600     animHide : function(callback){
29601         var b = Roo.get(this.animateTarget).getBox();
29602         this.proxy.show();
29603         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29604         this.el.hide();
29605         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29606                     this.hideEl.createDelegate(this, [callback]));
29607     },
29608
29609     /**
29610      * Hides the dialog.
29611      * @param {Function} callback (optional) Function to call when the dialog is hidden
29612      * @return {Roo.BasicDialog} this
29613      */
29614     hide : function(callback){
29615         if (this.fireEvent("beforehide", this) === false){
29616             return;
29617         }
29618         if(this.shadow){
29619             this.shadow.hide();
29620         }
29621         if(this.shim) {
29622           this.shim.hide();
29623         }
29624         // sometimes animateTarget seems to get set.. causing problems...
29625         // this just double checks..
29626         if(this.animateTarget && Roo.get(this.animateTarget)) {
29627            this.animHide(callback);
29628         }else{
29629             this.el.hide();
29630             this.hideEl(callback);
29631         }
29632         return this;
29633     },
29634
29635     // private
29636     hideEl : function(callback){
29637         this.proxy.hide();
29638         if(this.modal){
29639             this.mask.hide();
29640             Roo.get(document.body).removeClass("x-body-masked");
29641         }
29642         this.fireEvent("hide", this);
29643         if(typeof callback == "function"){
29644             callback();
29645         }
29646     },
29647
29648     // private
29649     hideAction : function(){
29650         this.setLeft("-10000px");
29651         this.setTop("-10000px");
29652         this.setStyle("visibility", "hidden");
29653     },
29654
29655     // private
29656     refreshSize : function(){
29657         this.size = this.el.getSize();
29658         this.xy = this.el.getXY();
29659         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29660     },
29661
29662     // private
29663     // z-index is managed by the DialogManager and may be overwritten at any time
29664     setZIndex : function(index){
29665         if(this.modal){
29666             this.mask.setStyle("z-index", index);
29667         }
29668         if(this.shim){
29669             this.shim.setStyle("z-index", ++index);
29670         }
29671         if(this.shadow){
29672             this.shadow.setZIndex(++index);
29673         }
29674         this.el.setStyle("z-index", ++index);
29675         if(this.proxy){
29676             this.proxy.setStyle("z-index", ++index);
29677         }
29678         if(this.resizer){
29679             this.resizer.proxy.setStyle("z-index", ++index);
29680         }
29681
29682         this.lastZIndex = index;
29683     },
29684
29685     /**
29686      * Returns the element for this dialog
29687      * @return {Roo.Element} The underlying dialog Element
29688      */
29689     getEl : function(){
29690         return this.el;
29691     }
29692 });
29693
29694 /**
29695  * @class Roo.DialogManager
29696  * Provides global access to BasicDialogs that have been created and
29697  * support for z-indexing (layering) multiple open dialogs.
29698  */
29699 Roo.DialogManager = function(){
29700     var list = {};
29701     var accessList = [];
29702     var front = null;
29703
29704     // private
29705     var sortDialogs = function(d1, d2){
29706         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29707     };
29708
29709     // private
29710     var orderDialogs = function(){
29711         accessList.sort(sortDialogs);
29712         var seed = Roo.DialogManager.zseed;
29713         for(var i = 0, len = accessList.length; i < len; i++){
29714             var dlg = accessList[i];
29715             if(dlg){
29716                 dlg.setZIndex(seed + (i*10));
29717             }
29718         }
29719     };
29720
29721     return {
29722         /**
29723          * The starting z-index for BasicDialogs (defaults to 9000)
29724          * @type Number The z-index value
29725          */
29726         zseed : 9000,
29727
29728         // private
29729         register : function(dlg){
29730             list[dlg.id] = dlg;
29731             accessList.push(dlg);
29732         },
29733
29734         // private
29735         unregister : function(dlg){
29736             delete list[dlg.id];
29737             var i=0;
29738             var len=0;
29739             if(!accessList.indexOf){
29740                 for(  i = 0, len = accessList.length; i < len; i++){
29741                     if(accessList[i] == dlg){
29742                         accessList.splice(i, 1);
29743                         return;
29744                     }
29745                 }
29746             }else{
29747                  i = accessList.indexOf(dlg);
29748                 if(i != -1){
29749                     accessList.splice(i, 1);
29750                 }
29751             }
29752         },
29753
29754         /**
29755          * Gets a registered dialog by id
29756          * @param {String/Object} id The id of the dialog or a dialog
29757          * @return {Roo.BasicDialog} this
29758          */
29759         get : function(id){
29760             return typeof id == "object" ? id : list[id];
29761         },
29762
29763         /**
29764          * Brings the specified dialog to the front
29765          * @param {String/Object} dlg The id of the dialog or a dialog
29766          * @return {Roo.BasicDialog} this
29767          */
29768         bringToFront : function(dlg){
29769             dlg = this.get(dlg);
29770             if(dlg != front){
29771                 front = dlg;
29772                 dlg._lastAccess = new Date().getTime();
29773                 orderDialogs();
29774             }
29775             return dlg;
29776         },
29777
29778         /**
29779          * Sends the specified dialog to the back
29780          * @param {String/Object} dlg The id of the dialog or a dialog
29781          * @return {Roo.BasicDialog} this
29782          */
29783         sendToBack : function(dlg){
29784             dlg = this.get(dlg);
29785             dlg._lastAccess = -(new Date().getTime());
29786             orderDialogs();
29787             return dlg;
29788         },
29789
29790         /**
29791          * Hides all dialogs
29792          */
29793         hideAll : function(){
29794             for(var id in list){
29795                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29796                     list[id].hide();
29797                 }
29798             }
29799         }
29800     };
29801 }();
29802
29803 /**
29804  * @class Roo.LayoutDialog
29805  * @extends Roo.BasicDialog
29806  * Dialog which provides adjustments for working with a layout in a Dialog.
29807  * Add your necessary layout config options to the dialog's config.<br>
29808  * Example usage (including a nested layout):
29809  * <pre><code>
29810 if(!dialog){
29811     dialog = new Roo.LayoutDialog("download-dlg", {
29812         modal: true,
29813         width:600,
29814         height:450,
29815         shadow:true,
29816         minWidth:500,
29817         minHeight:350,
29818         autoTabs:true,
29819         proxyDrag:true,
29820         // layout config merges with the dialog config
29821         center:{
29822             tabPosition: "top",
29823             alwaysShowTabs: true
29824         }
29825     });
29826     dialog.addKeyListener(27, dialog.hide, dialog);
29827     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29828     dialog.addButton("Build It!", this.getDownload, this);
29829
29830     // we can even add nested layouts
29831     var innerLayout = new Roo.BorderLayout("dl-inner", {
29832         east: {
29833             initialSize: 200,
29834             autoScroll:true,
29835             split:true
29836         },
29837         center: {
29838             autoScroll:true
29839         }
29840     });
29841     innerLayout.beginUpdate();
29842     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29843     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29844     innerLayout.endUpdate(true);
29845
29846     var layout = dialog.getLayout();
29847     layout.beginUpdate();
29848     layout.add("center", new Roo.ContentPanel("standard-panel",
29849                         {title: "Download the Source", fitToFrame:true}));
29850     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29851                {title: "Build your own roo.js"}));
29852     layout.getRegion("center").showPanel(sp);
29853     layout.endUpdate();
29854 }
29855 </code></pre>
29856     * @constructor
29857     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29858     * @param {Object} config configuration options
29859   */
29860 Roo.LayoutDialog = function(el, cfg){
29861     
29862     var config=  cfg;
29863     if (typeof(cfg) == 'undefined') {
29864         config = Roo.apply({}, el);
29865         // not sure why we use documentElement here.. - it should always be body.
29866         // IE7 borks horribly if we use documentElement.
29867         // webkit also does not like documentElement - it creates a body element...
29868         el = Roo.get( document.body || document.documentElement ).createChild();
29869         //config.autoCreate = true;
29870     }
29871     
29872     
29873     config.autoTabs = false;
29874     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29875     this.body.setStyle({overflow:"hidden", position:"relative"});
29876     this.layout = new Roo.BorderLayout(this.body.dom, config);
29877     this.layout.monitorWindowResize = false;
29878     this.el.addClass("x-dlg-auto-layout");
29879     // fix case when center region overwrites center function
29880     this.center = Roo.BasicDialog.prototype.center;
29881     this.on("show", this.layout.layout, this.layout, true);
29882     if (config.items) {
29883         var xitems = config.items;
29884         delete config.items;
29885         Roo.each(xitems, this.addxtype, this);
29886     }
29887     
29888     
29889 };
29890 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29891     /**
29892      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29893      * @deprecated
29894      */
29895     endUpdate : function(){
29896         this.layout.endUpdate();
29897     },
29898
29899     /**
29900      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29901      *  @deprecated
29902      */
29903     beginUpdate : function(){
29904         this.layout.beginUpdate();
29905     },
29906
29907     /**
29908      * Get the BorderLayout for this dialog
29909      * @return {Roo.BorderLayout}
29910      */
29911     getLayout : function(){
29912         return this.layout;
29913     },
29914
29915     showEl : function(){
29916         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29917         if(Roo.isIE7){
29918             this.layout.layout();
29919         }
29920     },
29921
29922     // private
29923     // Use the syncHeightBeforeShow config option to control this automatically
29924     syncBodyHeight : function(){
29925         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29926         if(this.layout){this.layout.layout();}
29927     },
29928     
29929       /**
29930      * Add an xtype element (actually adds to the layout.)
29931      * @return {Object} xdata xtype object data.
29932      */
29933     
29934     addxtype : function(c) {
29935         return this.layout.addxtype(c);
29936     }
29937 });/*
29938  * Based on:
29939  * Ext JS Library 1.1.1
29940  * Copyright(c) 2006-2007, Ext JS, LLC.
29941  *
29942  * Originally Released Under LGPL - original licence link has changed is not relivant.
29943  *
29944  * Fork - LGPL
29945  * <script type="text/javascript">
29946  */
29947  
29948 /**
29949  * @class Roo.MessageBox
29950  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29951  * Example usage:
29952  *<pre><code>
29953 // Basic alert:
29954 Roo.Msg.alert('Status', 'Changes saved successfully.');
29955
29956 // Prompt for user data:
29957 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29958     if (btn == 'ok'){
29959         // process text value...
29960     }
29961 });
29962
29963 // Show a dialog using config options:
29964 Roo.Msg.show({
29965    title:'Save Changes?',
29966    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29967    buttons: Roo.Msg.YESNOCANCEL,
29968    fn: processResult,
29969    animEl: 'elId'
29970 });
29971 </code></pre>
29972  * @singleton
29973  */
29974 Roo.MessageBox = function(){
29975     var dlg, opt, mask, waitTimer;
29976     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29977     var buttons, activeTextEl, bwidth;
29978
29979     // private
29980     var handleButton = function(button){
29981         dlg.hide();
29982         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29983     };
29984
29985     // private
29986     var handleHide = function(){
29987         if(opt && opt.cls){
29988             dlg.el.removeClass(opt.cls);
29989         }
29990         if(waitTimer){
29991             Roo.TaskMgr.stop(waitTimer);
29992             waitTimer = null;
29993         }
29994     };
29995
29996     // private
29997     var updateButtons = function(b){
29998         var width = 0;
29999         if(!b){
30000             buttons["ok"].hide();
30001             buttons["cancel"].hide();
30002             buttons["yes"].hide();
30003             buttons["no"].hide();
30004             dlg.footer.dom.style.display = 'none';
30005             return width;
30006         }
30007         dlg.footer.dom.style.display = '';
30008         for(var k in buttons){
30009             if(typeof buttons[k] != "function"){
30010                 if(b[k]){
30011                     buttons[k].show();
30012                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30013                     width += buttons[k].el.getWidth()+15;
30014                 }else{
30015                     buttons[k].hide();
30016                 }
30017             }
30018         }
30019         return width;
30020     };
30021
30022     // private
30023     var handleEsc = function(d, k, e){
30024         if(opt && opt.closable !== false){
30025             dlg.hide();
30026         }
30027         if(e){
30028             e.stopEvent();
30029         }
30030     };
30031
30032     return {
30033         /**
30034          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30035          * @return {Roo.BasicDialog} The BasicDialog element
30036          */
30037         getDialog : function(){
30038            if(!dlg){
30039                 dlg = new Roo.BasicDialog("x-msg-box", {
30040                     autoCreate : true,
30041                     shadow: true,
30042                     draggable: true,
30043                     resizable:false,
30044                     constraintoviewport:false,
30045                     fixedcenter:true,
30046                     collapsible : false,
30047                     shim:true,
30048                     modal: true,
30049                     width:400, height:100,
30050                     buttonAlign:"center",
30051                     closeClick : function(){
30052                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30053                             handleButton("no");
30054                         }else{
30055                             handleButton("cancel");
30056                         }
30057                     }
30058                 });
30059                 dlg.on("hide", handleHide);
30060                 mask = dlg.mask;
30061                 dlg.addKeyListener(27, handleEsc);
30062                 buttons = {};
30063                 var bt = this.buttonText;
30064                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30065                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30066                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30067                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30068                 bodyEl = dlg.body.createChild({
30069
30070                     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>'
30071                 });
30072                 msgEl = bodyEl.dom.firstChild;
30073                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30074                 textboxEl.enableDisplayMode();
30075                 textboxEl.addKeyListener([10,13], function(){
30076                     if(dlg.isVisible() && opt && opt.buttons){
30077                         if(opt.buttons.ok){
30078                             handleButton("ok");
30079                         }else if(opt.buttons.yes){
30080                             handleButton("yes");
30081                         }
30082                     }
30083                 });
30084                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30085                 textareaEl.enableDisplayMode();
30086                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30087                 progressEl.enableDisplayMode();
30088                 var pf = progressEl.dom.firstChild;
30089                 if (pf) {
30090                     pp = Roo.get(pf.firstChild);
30091                     pp.setHeight(pf.offsetHeight);
30092                 }
30093                 
30094             }
30095             return dlg;
30096         },
30097
30098         /**
30099          * Updates the message box body text
30100          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30101          * the XHTML-compliant non-breaking space character '&amp;#160;')
30102          * @return {Roo.MessageBox} This message box
30103          */
30104         updateText : function(text){
30105             if(!dlg.isVisible() && !opt.width){
30106                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30107             }
30108             msgEl.innerHTML = text || '&#160;';
30109       
30110             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30111             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30112             var w = Math.max(
30113                     Math.min(opt.width || cw , this.maxWidth), 
30114                     Math.max(opt.minWidth || this.minWidth, bwidth)
30115             );
30116             if(opt.prompt){
30117                 activeTextEl.setWidth(w);
30118             }
30119             if(dlg.isVisible()){
30120                 dlg.fixedcenter = false;
30121             }
30122             // to big, make it scroll. = But as usual stupid IE does not support
30123             // !important..
30124             
30125             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30126                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30127                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30128             } else {
30129                 bodyEl.dom.style.height = '';
30130                 bodyEl.dom.style.overflowY = '';
30131             }
30132             if (cw > w) {
30133                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30134             } else {
30135                 bodyEl.dom.style.overflowX = '';
30136             }
30137             
30138             dlg.setContentSize(w, bodyEl.getHeight());
30139             if(dlg.isVisible()){
30140                 dlg.fixedcenter = true;
30141             }
30142             return this;
30143         },
30144
30145         /**
30146          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30147          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30148          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30149          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30150          * @return {Roo.MessageBox} This message box
30151          */
30152         updateProgress : function(value, text){
30153             if(text){
30154                 this.updateText(text);
30155             }
30156             if (pp) { // weird bug on my firefox - for some reason this is not defined
30157                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30158             }
30159             return this;
30160         },        
30161
30162         /**
30163          * Returns true if the message box is currently displayed
30164          * @return {Boolean} True if the message box is visible, else false
30165          */
30166         isVisible : function(){
30167             return dlg && dlg.isVisible();  
30168         },
30169
30170         /**
30171          * Hides the message box if it is displayed
30172          */
30173         hide : function(){
30174             if(this.isVisible()){
30175                 dlg.hide();
30176             }  
30177         },
30178
30179         /**
30180          * Displays a new message box, or reinitializes an existing message box, based on the config options
30181          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30182          * The following config object properties are supported:
30183          * <pre>
30184 Property    Type             Description
30185 ----------  ---------------  ------------------------------------------------------------------------------------
30186 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30187                                    closes (defaults to undefined)
30188 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30189                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30190 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30191                                    progress and wait dialogs will ignore this property and always hide the
30192                                    close button as they can only be closed programmatically.
30193 cls               String           A custom CSS class to apply to the message box element
30194 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30195                                    displayed (defaults to 75)
30196 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30197                                    function will be btn (the name of the button that was clicked, if applicable,
30198                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30199                                    Progress and wait dialogs will ignore this option since they do not respond to
30200                                    user actions and can only be closed programmatically, so any required function
30201                                    should be called by the same code after it closes the dialog.
30202 icon              String           A CSS class that provides a background image to be used as an icon for
30203                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30204 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30205 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30206 modal             Boolean          False to allow user interaction with the page while the message box is
30207                                    displayed (defaults to true)
30208 msg               String           A string that will replace the existing message box body text (defaults
30209                                    to the XHTML-compliant non-breaking space character '&#160;')
30210 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30211 progress          Boolean          True to display a progress bar (defaults to false)
30212 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30213 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30214 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30215 title             String           The title text
30216 value             String           The string value to set into the active textbox element if displayed
30217 wait              Boolean          True to display a progress bar (defaults to false)
30218 width             Number           The width of the dialog in pixels
30219 </pre>
30220          *
30221          * Example usage:
30222          * <pre><code>
30223 Roo.Msg.show({
30224    title: 'Address',
30225    msg: 'Please enter your address:',
30226    width: 300,
30227    buttons: Roo.MessageBox.OKCANCEL,
30228    multiline: true,
30229    fn: saveAddress,
30230    animEl: 'addAddressBtn'
30231 });
30232 </code></pre>
30233          * @param {Object} config Configuration options
30234          * @return {Roo.MessageBox} This message box
30235          */
30236         show : function(options)
30237         {
30238             
30239             // this causes nightmares if you show one dialog after another
30240             // especially on callbacks..
30241              
30242             if(this.isVisible()){
30243                 
30244                 this.hide();
30245                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30246                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30247                 Roo.log("New Dialog Message:" +  options.msg )
30248                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30249                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30250                 
30251             }
30252             var d = this.getDialog();
30253             opt = options;
30254             d.setTitle(opt.title || "&#160;");
30255             d.close.setDisplayed(opt.closable !== false);
30256             activeTextEl = textboxEl;
30257             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30258             if(opt.prompt){
30259                 if(opt.multiline){
30260                     textboxEl.hide();
30261                     textareaEl.show();
30262                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30263                         opt.multiline : this.defaultTextHeight);
30264                     activeTextEl = textareaEl;
30265                 }else{
30266                     textboxEl.show();
30267                     textareaEl.hide();
30268                 }
30269             }else{
30270                 textboxEl.hide();
30271                 textareaEl.hide();
30272             }
30273             progressEl.setDisplayed(opt.progress === true);
30274             this.updateProgress(0);
30275             activeTextEl.dom.value = opt.value || "";
30276             if(opt.prompt){
30277                 dlg.setDefaultButton(activeTextEl);
30278             }else{
30279                 var bs = opt.buttons;
30280                 var db = null;
30281                 if(bs && bs.ok){
30282                     db = buttons["ok"];
30283                 }else if(bs && bs.yes){
30284                     db = buttons["yes"];
30285                 }
30286                 dlg.setDefaultButton(db);
30287             }
30288             bwidth = updateButtons(opt.buttons);
30289             this.updateText(opt.msg);
30290             if(opt.cls){
30291                 d.el.addClass(opt.cls);
30292             }
30293             d.proxyDrag = opt.proxyDrag === true;
30294             d.modal = opt.modal !== false;
30295             d.mask = opt.modal !== false ? mask : false;
30296             if(!d.isVisible()){
30297                 // force it to the end of the z-index stack so it gets a cursor in FF
30298                 document.body.appendChild(dlg.el.dom);
30299                 d.animateTarget = null;
30300                 d.show(options.animEl);
30301             }
30302             return this;
30303         },
30304
30305         /**
30306          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30307          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30308          * and closing the message box when the process is complete.
30309          * @param {String} title The title bar text
30310          * @param {String} msg The message box body text
30311          * @return {Roo.MessageBox} This message box
30312          */
30313         progress : function(title, msg){
30314             this.show({
30315                 title : title,
30316                 msg : msg,
30317                 buttons: false,
30318                 progress:true,
30319                 closable:false,
30320                 minWidth: this.minProgressWidth,
30321                 modal : true
30322             });
30323             return this;
30324         },
30325
30326         /**
30327          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30328          * If a callback function is passed it will be called after the user clicks the button, and the
30329          * id of the button that was clicked will be passed as the only parameter to the callback
30330          * (could also be the top-right close button).
30331          * @param {String} title The title bar text
30332          * @param {String} msg The message box body text
30333          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30334          * @param {Object} scope (optional) The scope of the callback function
30335          * @return {Roo.MessageBox} This message box
30336          */
30337         alert : function(title, msg, fn, scope){
30338             this.show({
30339                 title : title,
30340                 msg : msg,
30341                 buttons: this.OK,
30342                 fn: fn,
30343                 scope : scope,
30344                 modal : true
30345             });
30346             return this;
30347         },
30348
30349         /**
30350          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30351          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30352          * You are responsible for closing the message box when the process is complete.
30353          * @param {String} msg The message box body text
30354          * @param {String} title (optional) The title bar text
30355          * @return {Roo.MessageBox} This message box
30356          */
30357         wait : function(msg, title){
30358             this.show({
30359                 title : title,
30360                 msg : msg,
30361                 buttons: false,
30362                 closable:false,
30363                 progress:true,
30364                 modal:true,
30365                 width:300,
30366                 wait:true
30367             });
30368             waitTimer = Roo.TaskMgr.start({
30369                 run: function(i){
30370                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30371                 },
30372                 interval: 1000
30373             });
30374             return this;
30375         },
30376
30377         /**
30378          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30379          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30380          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30381          * @param {String} title The title bar text
30382          * @param {String} msg The message box body text
30383          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30384          * @param {Object} scope (optional) The scope of the callback function
30385          * @return {Roo.MessageBox} This message box
30386          */
30387         confirm : function(title, msg, fn, scope){
30388             this.show({
30389                 title : title,
30390                 msg : msg,
30391                 buttons: this.YESNO,
30392                 fn: fn,
30393                 scope : scope,
30394                 modal : true
30395             });
30396             return this;
30397         },
30398
30399         /**
30400          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30401          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30402          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30403          * (could also be the top-right close button) and the text that was entered will be passed as the two
30404          * parameters to the callback.
30405          * @param {String} title The title bar text
30406          * @param {String} msg The message box body text
30407          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30408          * @param {Object} scope (optional) The scope of the callback function
30409          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30410          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30411          * @return {Roo.MessageBox} This message box
30412          */
30413         prompt : function(title, msg, fn, scope, multiline){
30414             this.show({
30415                 title : title,
30416                 msg : msg,
30417                 buttons: this.OKCANCEL,
30418                 fn: fn,
30419                 minWidth:250,
30420                 scope : scope,
30421                 prompt:true,
30422                 multiline: multiline,
30423                 modal : true
30424             });
30425             return this;
30426         },
30427
30428         /**
30429          * Button config that displays a single OK button
30430          * @type Object
30431          */
30432         OK : {ok:true},
30433         /**
30434          * Button config that displays Yes and No buttons
30435          * @type Object
30436          */
30437         YESNO : {yes:true, no:true},
30438         /**
30439          * Button config that displays OK and Cancel buttons
30440          * @type Object
30441          */
30442         OKCANCEL : {ok:true, cancel:true},
30443         /**
30444          * Button config that displays Yes, No and Cancel buttons
30445          * @type Object
30446          */
30447         YESNOCANCEL : {yes:true, no:true, cancel:true},
30448
30449         /**
30450          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30451          * @type Number
30452          */
30453         defaultTextHeight : 75,
30454         /**
30455          * The maximum width in pixels of the message box (defaults to 600)
30456          * @type Number
30457          */
30458         maxWidth : 600,
30459         /**
30460          * The minimum width in pixels of the message box (defaults to 100)
30461          * @type Number
30462          */
30463         minWidth : 100,
30464         /**
30465          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30466          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30467          * @type Number
30468          */
30469         minProgressWidth : 250,
30470         /**
30471          * An object containing the default button text strings that can be overriden for localized language support.
30472          * Supported properties are: ok, cancel, yes and no.
30473          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30474          * @type Object
30475          */
30476         buttonText : {
30477             ok : "OK",
30478             cancel : "Cancel",
30479             yes : "Yes",
30480             no : "No"
30481         }
30482     };
30483 }();
30484
30485 /**
30486  * Shorthand for {@link Roo.MessageBox}
30487  */
30488 Roo.Msg = Roo.MessageBox;/*
30489  * Based on:
30490  * Ext JS Library 1.1.1
30491  * Copyright(c) 2006-2007, Ext JS, LLC.
30492  *
30493  * Originally Released Under LGPL - original licence link has changed is not relivant.
30494  *
30495  * Fork - LGPL
30496  * <script type="text/javascript">
30497  */
30498 /**
30499  * @class Roo.QuickTips
30500  * Provides attractive and customizable tooltips for any element.
30501  * @singleton
30502  */
30503 Roo.QuickTips = function(){
30504     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30505     var ce, bd, xy, dd;
30506     var visible = false, disabled = true, inited = false;
30507     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30508     
30509     var onOver = function(e){
30510         if(disabled){
30511             return;
30512         }
30513         var t = e.getTarget();
30514         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30515             return;
30516         }
30517         if(ce && t == ce.el){
30518             clearTimeout(hideProc);
30519             return;
30520         }
30521         if(t && tagEls[t.id]){
30522             tagEls[t.id].el = t;
30523             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30524             return;
30525         }
30526         var ttp, et = Roo.fly(t);
30527         var ns = cfg.namespace;
30528         if(tm.interceptTitles && t.title){
30529             ttp = t.title;
30530             t.qtip = ttp;
30531             t.removeAttribute("title");
30532             e.preventDefault();
30533         }else{
30534             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30535         }
30536         if(ttp){
30537             showProc = show.defer(tm.showDelay, tm, [{
30538                 el: t, 
30539                 text: ttp, 
30540                 width: et.getAttributeNS(ns, cfg.width),
30541                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30542                 title: et.getAttributeNS(ns, cfg.title),
30543                     cls: et.getAttributeNS(ns, cfg.cls)
30544             }]);
30545         }
30546     };
30547     
30548     var onOut = function(e){
30549         clearTimeout(showProc);
30550         var t = e.getTarget();
30551         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30552             hideProc = setTimeout(hide, tm.hideDelay);
30553         }
30554     };
30555     
30556     var onMove = function(e){
30557         if(disabled){
30558             return;
30559         }
30560         xy = e.getXY();
30561         xy[1] += 18;
30562         if(tm.trackMouse && ce){
30563             el.setXY(xy);
30564         }
30565     };
30566     
30567     var onDown = function(e){
30568         clearTimeout(showProc);
30569         clearTimeout(hideProc);
30570         if(!e.within(el)){
30571             if(tm.hideOnClick){
30572                 hide();
30573                 tm.disable();
30574                 tm.enable.defer(100, tm);
30575             }
30576         }
30577     };
30578     
30579     var getPad = function(){
30580         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30581     };
30582
30583     var show = function(o){
30584         if(disabled){
30585             return;
30586         }
30587         clearTimeout(dismissProc);
30588         ce = o;
30589         if(removeCls){ // in case manually hidden
30590             el.removeClass(removeCls);
30591             removeCls = null;
30592         }
30593         if(ce.cls){
30594             el.addClass(ce.cls);
30595             removeCls = ce.cls;
30596         }
30597         if(ce.title){
30598             tipTitle.update(ce.title);
30599             tipTitle.show();
30600         }else{
30601             tipTitle.update('');
30602             tipTitle.hide();
30603         }
30604         el.dom.style.width  = tm.maxWidth+'px';
30605         //tipBody.dom.style.width = '';
30606         tipBodyText.update(o.text);
30607         var p = getPad(), w = ce.width;
30608         if(!w){
30609             var td = tipBodyText.dom;
30610             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30611             if(aw > tm.maxWidth){
30612                 w = tm.maxWidth;
30613             }else if(aw < tm.minWidth){
30614                 w = tm.minWidth;
30615             }else{
30616                 w = aw;
30617             }
30618         }
30619         //tipBody.setWidth(w);
30620         el.setWidth(parseInt(w, 10) + p);
30621         if(ce.autoHide === false){
30622             close.setDisplayed(true);
30623             if(dd){
30624                 dd.unlock();
30625             }
30626         }else{
30627             close.setDisplayed(false);
30628             if(dd){
30629                 dd.lock();
30630             }
30631         }
30632         if(xy){
30633             el.avoidY = xy[1]-18;
30634             el.setXY(xy);
30635         }
30636         if(tm.animate){
30637             el.setOpacity(.1);
30638             el.setStyle("visibility", "visible");
30639             el.fadeIn({callback: afterShow});
30640         }else{
30641             afterShow();
30642         }
30643     };
30644     
30645     var afterShow = function(){
30646         if(ce){
30647             el.show();
30648             esc.enable();
30649             if(tm.autoDismiss && ce.autoHide !== false){
30650                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30651             }
30652         }
30653     };
30654     
30655     var hide = function(noanim){
30656         clearTimeout(dismissProc);
30657         clearTimeout(hideProc);
30658         ce = null;
30659         if(el.isVisible()){
30660             esc.disable();
30661             if(noanim !== true && tm.animate){
30662                 el.fadeOut({callback: afterHide});
30663             }else{
30664                 afterHide();
30665             } 
30666         }
30667     };
30668     
30669     var afterHide = function(){
30670         el.hide();
30671         if(removeCls){
30672             el.removeClass(removeCls);
30673             removeCls = null;
30674         }
30675     };
30676     
30677     return {
30678         /**
30679         * @cfg {Number} minWidth
30680         * The minimum width of the quick tip (defaults to 40)
30681         */
30682        minWidth : 40,
30683         /**
30684         * @cfg {Number} maxWidth
30685         * The maximum width of the quick tip (defaults to 300)
30686         */
30687        maxWidth : 300,
30688         /**
30689         * @cfg {Boolean} interceptTitles
30690         * True to automatically use the element's DOM title value if available (defaults to false)
30691         */
30692        interceptTitles : false,
30693         /**
30694         * @cfg {Boolean} trackMouse
30695         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30696         */
30697        trackMouse : false,
30698         /**
30699         * @cfg {Boolean} hideOnClick
30700         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30701         */
30702        hideOnClick : true,
30703         /**
30704         * @cfg {Number} showDelay
30705         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30706         */
30707        showDelay : 500,
30708         /**
30709         * @cfg {Number} hideDelay
30710         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30711         */
30712        hideDelay : 200,
30713         /**
30714         * @cfg {Boolean} autoHide
30715         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30716         * Used in conjunction with hideDelay.
30717         */
30718        autoHide : true,
30719         /**
30720         * @cfg {Boolean}
30721         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30722         * (defaults to true).  Used in conjunction with autoDismissDelay.
30723         */
30724        autoDismiss : true,
30725         /**
30726         * @cfg {Number}
30727         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30728         */
30729        autoDismissDelay : 5000,
30730        /**
30731         * @cfg {Boolean} animate
30732         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30733         */
30734        animate : false,
30735
30736        /**
30737         * @cfg {String} title
30738         * Title text to display (defaults to '').  This can be any valid HTML markup.
30739         */
30740         title: '',
30741        /**
30742         * @cfg {String} text
30743         * Body text to display (defaults to '').  This can be any valid HTML markup.
30744         */
30745         text : '',
30746        /**
30747         * @cfg {String} cls
30748         * A CSS class to apply to the base quick tip element (defaults to '').
30749         */
30750         cls : '',
30751        /**
30752         * @cfg {Number} width
30753         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30754         * minWidth or maxWidth.
30755         */
30756         width : null,
30757
30758     /**
30759      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30760      * or display QuickTips in a page.
30761      */
30762        init : function(){
30763           tm = Roo.QuickTips;
30764           cfg = tm.tagConfig;
30765           if(!inited){
30766               if(!Roo.isReady){ // allow calling of init() before onReady
30767                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30768                   return;
30769               }
30770               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30771               el.fxDefaults = {stopFx: true};
30772               // maximum custom styling
30773               //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>');
30774               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>');              
30775               tipTitle = el.child('h3');
30776               tipTitle.enableDisplayMode("block");
30777               tipBody = el.child('div.x-tip-bd');
30778               tipBodyText = el.child('div.x-tip-bd-inner');
30779               //bdLeft = el.child('div.x-tip-bd-left');
30780               //bdRight = el.child('div.x-tip-bd-right');
30781               close = el.child('div.x-tip-close');
30782               close.enableDisplayMode("block");
30783               close.on("click", hide);
30784               var d = Roo.get(document);
30785               d.on("mousedown", onDown);
30786               d.on("mouseover", onOver);
30787               d.on("mouseout", onOut);
30788               d.on("mousemove", onMove);
30789               esc = d.addKeyListener(27, hide);
30790               esc.disable();
30791               if(Roo.dd.DD){
30792                   dd = el.initDD("default", null, {
30793                       onDrag : function(){
30794                           el.sync();  
30795                       }
30796                   });
30797                   dd.setHandleElId(tipTitle.id);
30798                   dd.lock();
30799               }
30800               inited = true;
30801           }
30802           this.enable(); 
30803        },
30804
30805     /**
30806      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30807      * are supported:
30808      * <pre>
30809 Property    Type                   Description
30810 ----------  ---------------------  ------------------------------------------------------------------------
30811 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30812      * </ul>
30813      * @param {Object} config The config object
30814      */
30815        register : function(config){
30816            var cs = config instanceof Array ? config : arguments;
30817            for(var i = 0, len = cs.length; i < len; i++) {
30818                var c = cs[i];
30819                var target = c.target;
30820                if(target){
30821                    if(target instanceof Array){
30822                        for(var j = 0, jlen = target.length; j < jlen; j++){
30823                            tagEls[target[j]] = c;
30824                        }
30825                    }else{
30826                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30827                    }
30828                }
30829            }
30830        },
30831
30832     /**
30833      * Removes this quick tip from its element and destroys it.
30834      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30835      */
30836        unregister : function(el){
30837            delete tagEls[Roo.id(el)];
30838        },
30839
30840     /**
30841      * Enable this quick tip.
30842      */
30843        enable : function(){
30844            if(inited && disabled){
30845                locks.pop();
30846                if(locks.length < 1){
30847                    disabled = false;
30848                }
30849            }
30850        },
30851
30852     /**
30853      * Disable this quick tip.
30854      */
30855        disable : function(){
30856           disabled = true;
30857           clearTimeout(showProc);
30858           clearTimeout(hideProc);
30859           clearTimeout(dismissProc);
30860           if(ce){
30861               hide(true);
30862           }
30863           locks.push(1);
30864        },
30865
30866     /**
30867      * Returns true if the quick tip is enabled, else false.
30868      */
30869        isEnabled : function(){
30870             return !disabled;
30871        },
30872
30873         // private
30874        tagConfig : {
30875            namespace : "ext",
30876            attribute : "qtip",
30877            width : "width",
30878            target : "target",
30879            title : "qtitle",
30880            hide : "hide",
30881            cls : "qclass"
30882        }
30883    };
30884 }();
30885
30886 // backwards compat
30887 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30888  * Based on:
30889  * Ext JS Library 1.1.1
30890  * Copyright(c) 2006-2007, Ext JS, LLC.
30891  *
30892  * Originally Released Under LGPL - original licence link has changed is not relivant.
30893  *
30894  * Fork - LGPL
30895  * <script type="text/javascript">
30896  */
30897  
30898
30899 /**
30900  * @class Roo.tree.TreePanel
30901  * @extends Roo.data.Tree
30902
30903  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30904  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30905  * @cfg {Boolean} enableDD true to enable drag and drop
30906  * @cfg {Boolean} enableDrag true to enable just drag
30907  * @cfg {Boolean} enableDrop true to enable just drop
30908  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30909  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30910  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30911  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30912  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30913  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30914  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30915  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30916  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30917  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30918  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30919  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30920  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30921  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30922  * @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>
30923  * @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>
30924  * 
30925  * @constructor
30926  * @param {String/HTMLElement/Element} el The container element
30927  * @param {Object} config
30928  */
30929 Roo.tree.TreePanel = function(el, config){
30930     var root = false;
30931     var loader = false;
30932     if (config.root) {
30933         root = config.root;
30934         delete config.root;
30935     }
30936     if (config.loader) {
30937         loader = config.loader;
30938         delete config.loader;
30939     }
30940     
30941     Roo.apply(this, config);
30942     Roo.tree.TreePanel.superclass.constructor.call(this);
30943     this.el = Roo.get(el);
30944     this.el.addClass('x-tree');
30945     //console.log(root);
30946     if (root) {
30947         this.setRootNode( Roo.factory(root, Roo.tree));
30948     }
30949     if (loader) {
30950         this.loader = Roo.factory(loader, Roo.tree);
30951     }
30952    /**
30953     * Read-only. The id of the container element becomes this TreePanel's id.
30954     */
30955     this.id = this.el.id;
30956     this.addEvents({
30957         /**
30958         * @event beforeload
30959         * Fires before a node is loaded, return false to cancel
30960         * @param {Node} node The node being loaded
30961         */
30962         "beforeload" : true,
30963         /**
30964         * @event load
30965         * Fires when a node is loaded
30966         * @param {Node} node The node that was loaded
30967         */
30968         "load" : true,
30969         /**
30970         * @event textchange
30971         * Fires when the text for a node is changed
30972         * @param {Node} node The node
30973         * @param {String} text The new text
30974         * @param {String} oldText The old text
30975         */
30976         "textchange" : true,
30977         /**
30978         * @event beforeexpand
30979         * Fires before a node is expanded, return false to cancel.
30980         * @param {Node} node The node
30981         * @param {Boolean} deep
30982         * @param {Boolean} anim
30983         */
30984         "beforeexpand" : true,
30985         /**
30986         * @event beforecollapse
30987         * Fires before a node is collapsed, return false to cancel.
30988         * @param {Node} node The node
30989         * @param {Boolean} deep
30990         * @param {Boolean} anim
30991         */
30992         "beforecollapse" : true,
30993         /**
30994         * @event expand
30995         * Fires when a node is expanded
30996         * @param {Node} node The node
30997         */
30998         "expand" : true,
30999         /**
31000         * @event disabledchange
31001         * Fires when the disabled status of a node changes
31002         * @param {Node} node The node
31003         * @param {Boolean} disabled
31004         */
31005         "disabledchange" : true,
31006         /**
31007         * @event collapse
31008         * Fires when a node is collapsed
31009         * @param {Node} node The node
31010         */
31011         "collapse" : true,
31012         /**
31013         * @event beforeclick
31014         * Fires before click processing on a node. Return false to cancel the default action.
31015         * @param {Node} node The node
31016         * @param {Roo.EventObject} e The event object
31017         */
31018         "beforeclick":true,
31019         /**
31020         * @event checkchange
31021         * Fires when a node with a checkbox's checked property changes
31022         * @param {Node} this This node
31023         * @param {Boolean} checked
31024         */
31025         "checkchange":true,
31026         /**
31027         * @event click
31028         * Fires when a node is clicked
31029         * @param {Node} node The node
31030         * @param {Roo.EventObject} e The event object
31031         */
31032         "click":true,
31033         /**
31034         * @event dblclick
31035         * Fires when a node is double clicked
31036         * @param {Node} node The node
31037         * @param {Roo.EventObject} e The event object
31038         */
31039         "dblclick":true,
31040         /**
31041         * @event contextmenu
31042         * Fires when a node is right clicked
31043         * @param {Node} node The node
31044         * @param {Roo.EventObject} e The event object
31045         */
31046         "contextmenu":true,
31047         /**
31048         * @event beforechildrenrendered
31049         * Fires right before the child nodes for a node are rendered
31050         * @param {Node} node The node
31051         */
31052         "beforechildrenrendered":true,
31053         /**
31054         * @event startdrag
31055         * Fires when a node starts being dragged
31056         * @param {Roo.tree.TreePanel} this
31057         * @param {Roo.tree.TreeNode} node
31058         * @param {event} e The raw browser event
31059         */ 
31060        "startdrag" : true,
31061        /**
31062         * @event enddrag
31063         * Fires when a drag operation is complete
31064         * @param {Roo.tree.TreePanel} this
31065         * @param {Roo.tree.TreeNode} node
31066         * @param {event} e The raw browser event
31067         */
31068        "enddrag" : true,
31069        /**
31070         * @event dragdrop
31071         * Fires when a dragged node is dropped on a valid DD target
31072         * @param {Roo.tree.TreePanel} this
31073         * @param {Roo.tree.TreeNode} node
31074         * @param {DD} dd The dd it was dropped on
31075         * @param {event} e The raw browser event
31076         */
31077        "dragdrop" : true,
31078        /**
31079         * @event beforenodedrop
31080         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31081         * passed to handlers has the following properties:<br />
31082         * <ul style="padding:5px;padding-left:16px;">
31083         * <li>tree - The TreePanel</li>
31084         * <li>target - The node being targeted for the drop</li>
31085         * <li>data - The drag data from the drag source</li>
31086         * <li>point - The point of the drop - append, above or below</li>
31087         * <li>source - The drag source</li>
31088         * <li>rawEvent - Raw mouse event</li>
31089         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31090         * to be inserted by setting them on this object.</li>
31091         * <li>cancel - Set this to true to cancel the drop.</li>
31092         * </ul>
31093         * @param {Object} dropEvent
31094         */
31095        "beforenodedrop" : true,
31096        /**
31097         * @event nodedrop
31098         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31099         * passed to handlers has the following properties:<br />
31100         * <ul style="padding:5px;padding-left:16px;">
31101         * <li>tree - The TreePanel</li>
31102         * <li>target - The node being targeted for the drop</li>
31103         * <li>data - The drag data from the drag source</li>
31104         * <li>point - The point of the drop - append, above or below</li>
31105         * <li>source - The drag source</li>
31106         * <li>rawEvent - Raw mouse event</li>
31107         * <li>dropNode - Dropped node(s).</li>
31108         * </ul>
31109         * @param {Object} dropEvent
31110         */
31111        "nodedrop" : true,
31112         /**
31113         * @event nodedragover
31114         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31115         * passed to handlers has the following properties:<br />
31116         * <ul style="padding:5px;padding-left:16px;">
31117         * <li>tree - The TreePanel</li>
31118         * <li>target - The node being targeted for the drop</li>
31119         * <li>data - The drag data from the drag source</li>
31120         * <li>point - The point of the drop - append, above or below</li>
31121         * <li>source - The drag source</li>
31122         * <li>rawEvent - Raw mouse event</li>
31123         * <li>dropNode - Drop node(s) provided by the source.</li>
31124         * <li>cancel - Set this to true to signal drop not allowed.</li>
31125         * </ul>
31126         * @param {Object} dragOverEvent
31127         */
31128        "nodedragover" : true
31129         
31130     });
31131     if(this.singleExpand){
31132        this.on("beforeexpand", this.restrictExpand, this);
31133     }
31134     if (this.editor) {
31135         this.editor.tree = this;
31136         this.editor = Roo.factory(this.editor, Roo.tree);
31137     }
31138     
31139     if (this.selModel) {
31140         this.selModel = Roo.factory(this.selModel, Roo.tree);
31141     }
31142    
31143 };
31144 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31145     rootVisible : true,
31146     animate: Roo.enableFx,
31147     lines : true,
31148     enableDD : false,
31149     hlDrop : Roo.enableFx,
31150   
31151     renderer: false,
31152     
31153     rendererTip: false,
31154     // private
31155     restrictExpand : function(node){
31156         var p = node.parentNode;
31157         if(p){
31158             if(p.expandedChild && p.expandedChild.parentNode == p){
31159                 p.expandedChild.collapse();
31160             }
31161             p.expandedChild = node;
31162         }
31163     },
31164
31165     // private override
31166     setRootNode : function(node){
31167         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31168         if(!this.rootVisible){
31169             node.ui = new Roo.tree.RootTreeNodeUI(node);
31170         }
31171         return node;
31172     },
31173
31174     /**
31175      * Returns the container element for this TreePanel
31176      */
31177     getEl : function(){
31178         return this.el;
31179     },
31180
31181     /**
31182      * Returns the default TreeLoader for this TreePanel
31183      */
31184     getLoader : function(){
31185         return this.loader;
31186     },
31187
31188     /**
31189      * Expand all nodes
31190      */
31191     expandAll : function(){
31192         this.root.expand(true);
31193     },
31194
31195     /**
31196      * Collapse all nodes
31197      */
31198     collapseAll : function(){
31199         this.root.collapse(true);
31200     },
31201
31202     /**
31203      * Returns the selection model used by this TreePanel
31204      */
31205     getSelectionModel : function(){
31206         if(!this.selModel){
31207             this.selModel = new Roo.tree.DefaultSelectionModel();
31208         }
31209         return this.selModel;
31210     },
31211
31212     /**
31213      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31214      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31215      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31216      * @return {Array}
31217      */
31218     getChecked : function(a, startNode){
31219         startNode = startNode || this.root;
31220         var r = [];
31221         var f = function(){
31222             if(this.attributes.checked){
31223                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31224             }
31225         }
31226         startNode.cascade(f);
31227         return r;
31228     },
31229
31230     /**
31231      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31232      * @param {String} path
31233      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31234      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31235      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31236      */
31237     expandPath : function(path, attr, callback){
31238         attr = attr || "id";
31239         var keys = path.split(this.pathSeparator);
31240         var curNode = this.root;
31241         if(curNode.attributes[attr] != keys[1]){ // invalid root
31242             if(callback){
31243                 callback(false, null);
31244             }
31245             return;
31246         }
31247         var index = 1;
31248         var f = function(){
31249             if(++index == keys.length){
31250                 if(callback){
31251                     callback(true, curNode);
31252                 }
31253                 return;
31254             }
31255             var c = curNode.findChild(attr, keys[index]);
31256             if(!c){
31257                 if(callback){
31258                     callback(false, curNode);
31259                 }
31260                 return;
31261             }
31262             curNode = c;
31263             c.expand(false, false, f);
31264         };
31265         curNode.expand(false, false, f);
31266     },
31267
31268     /**
31269      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31270      * @param {String} path
31271      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31272      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31273      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31274      */
31275     selectPath : function(path, attr, callback){
31276         attr = attr || "id";
31277         var keys = path.split(this.pathSeparator);
31278         var v = keys.pop();
31279         if(keys.length > 0){
31280             var f = function(success, node){
31281                 if(success && node){
31282                     var n = node.findChild(attr, v);
31283                     if(n){
31284                         n.select();
31285                         if(callback){
31286                             callback(true, n);
31287                         }
31288                     }else if(callback){
31289                         callback(false, n);
31290                     }
31291                 }else{
31292                     if(callback){
31293                         callback(false, n);
31294                     }
31295                 }
31296             };
31297             this.expandPath(keys.join(this.pathSeparator), attr, f);
31298         }else{
31299             this.root.select();
31300             if(callback){
31301                 callback(true, this.root);
31302             }
31303         }
31304     },
31305
31306     getTreeEl : function(){
31307         return this.el;
31308     },
31309
31310     /**
31311      * Trigger rendering of this TreePanel
31312      */
31313     render : function(){
31314         if (this.innerCt) {
31315             return this; // stop it rendering more than once!!
31316         }
31317         
31318         this.innerCt = this.el.createChild({tag:"ul",
31319                cls:"x-tree-root-ct " +
31320                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31321
31322         if(this.containerScroll){
31323             Roo.dd.ScrollManager.register(this.el);
31324         }
31325         if((this.enableDD || this.enableDrop) && !this.dropZone){
31326            /**
31327             * The dropZone used by this tree if drop is enabled
31328             * @type Roo.tree.TreeDropZone
31329             */
31330              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31331                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31332            });
31333         }
31334         if((this.enableDD || this.enableDrag) && !this.dragZone){
31335            /**
31336             * The dragZone used by this tree if drag is enabled
31337             * @type Roo.tree.TreeDragZone
31338             */
31339             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31340                ddGroup: this.ddGroup || "TreeDD",
31341                scroll: this.ddScroll
31342            });
31343         }
31344         this.getSelectionModel().init(this);
31345         if (!this.root) {
31346             console.log("ROOT not set in tree");
31347             return;
31348         }
31349         this.root.render();
31350         if(!this.rootVisible){
31351             this.root.renderChildren();
31352         }
31353         return this;
31354     }
31355 });/*
31356  * Based on:
31357  * Ext JS Library 1.1.1
31358  * Copyright(c) 2006-2007, Ext JS, LLC.
31359  *
31360  * Originally Released Under LGPL - original licence link has changed is not relivant.
31361  *
31362  * Fork - LGPL
31363  * <script type="text/javascript">
31364  */
31365  
31366
31367 /**
31368  * @class Roo.tree.DefaultSelectionModel
31369  * @extends Roo.util.Observable
31370  * The default single selection for a TreePanel.
31371  * @param {Object} cfg Configuration
31372  */
31373 Roo.tree.DefaultSelectionModel = function(cfg){
31374    this.selNode = null;
31375    
31376    
31377    
31378    this.addEvents({
31379        /**
31380         * @event selectionchange
31381         * Fires when the selected node changes
31382         * @param {DefaultSelectionModel} this
31383         * @param {TreeNode} node the new selection
31384         */
31385        "selectionchange" : true,
31386
31387        /**
31388         * @event beforeselect
31389         * Fires before the selected node changes, return false to cancel the change
31390         * @param {DefaultSelectionModel} this
31391         * @param {TreeNode} node the new selection
31392         * @param {TreeNode} node the old selection
31393         */
31394        "beforeselect" : true
31395    });
31396    
31397     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31398 };
31399
31400 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31401     init : function(tree){
31402         this.tree = tree;
31403         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31404         tree.on("click", this.onNodeClick, this);
31405     },
31406     
31407     onNodeClick : function(node, e){
31408         if (e.ctrlKey && this.selNode == node)  {
31409             this.unselect(node);
31410             return;
31411         }
31412         this.select(node);
31413     },
31414     
31415     /**
31416      * Select a node.
31417      * @param {TreeNode} node The node to select
31418      * @return {TreeNode} The selected node
31419      */
31420     select : function(node){
31421         var last = this.selNode;
31422         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31423             if(last){
31424                 last.ui.onSelectedChange(false);
31425             }
31426             this.selNode = node;
31427             node.ui.onSelectedChange(true);
31428             this.fireEvent("selectionchange", this, node, last);
31429         }
31430         return node;
31431     },
31432     
31433     /**
31434      * Deselect a node.
31435      * @param {TreeNode} node The node to unselect
31436      */
31437     unselect : function(node){
31438         if(this.selNode == node){
31439             this.clearSelections();
31440         }    
31441     },
31442     
31443     /**
31444      * Clear all selections
31445      */
31446     clearSelections : function(){
31447         var n = this.selNode;
31448         if(n){
31449             n.ui.onSelectedChange(false);
31450             this.selNode = null;
31451             this.fireEvent("selectionchange", this, null);
31452         }
31453         return n;
31454     },
31455     
31456     /**
31457      * Get the selected node
31458      * @return {TreeNode} The selected node
31459      */
31460     getSelectedNode : function(){
31461         return this.selNode;    
31462     },
31463     
31464     /**
31465      * Returns true if the node is selected
31466      * @param {TreeNode} node The node to check
31467      * @return {Boolean}
31468      */
31469     isSelected : function(node){
31470         return this.selNode == node;  
31471     },
31472
31473     /**
31474      * Selects the node above the selected node in the tree, intelligently walking the nodes
31475      * @return TreeNode The new selection
31476      */
31477     selectPrevious : function(){
31478         var s = this.selNode || this.lastSelNode;
31479         if(!s){
31480             return null;
31481         }
31482         var ps = s.previousSibling;
31483         if(ps){
31484             if(!ps.isExpanded() || ps.childNodes.length < 1){
31485                 return this.select(ps);
31486             } else{
31487                 var lc = ps.lastChild;
31488                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31489                     lc = lc.lastChild;
31490                 }
31491                 return this.select(lc);
31492             }
31493         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31494             return this.select(s.parentNode);
31495         }
31496         return null;
31497     },
31498
31499     /**
31500      * Selects the node above the selected node in the tree, intelligently walking the nodes
31501      * @return TreeNode The new selection
31502      */
31503     selectNext : function(){
31504         var s = this.selNode || this.lastSelNode;
31505         if(!s){
31506             return null;
31507         }
31508         if(s.firstChild && s.isExpanded()){
31509              return this.select(s.firstChild);
31510          }else if(s.nextSibling){
31511              return this.select(s.nextSibling);
31512          }else if(s.parentNode){
31513             var newS = null;
31514             s.parentNode.bubble(function(){
31515                 if(this.nextSibling){
31516                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31517                     return false;
31518                 }
31519             });
31520             return newS;
31521          }
31522         return null;
31523     },
31524
31525     onKeyDown : function(e){
31526         var s = this.selNode || this.lastSelNode;
31527         // undesirable, but required
31528         var sm = this;
31529         if(!s){
31530             return;
31531         }
31532         var k = e.getKey();
31533         switch(k){
31534              case e.DOWN:
31535                  e.stopEvent();
31536                  this.selectNext();
31537              break;
31538              case e.UP:
31539                  e.stopEvent();
31540                  this.selectPrevious();
31541              break;
31542              case e.RIGHT:
31543                  e.preventDefault();
31544                  if(s.hasChildNodes()){
31545                      if(!s.isExpanded()){
31546                          s.expand();
31547                      }else if(s.firstChild){
31548                          this.select(s.firstChild, e);
31549                      }
31550                  }
31551              break;
31552              case e.LEFT:
31553                  e.preventDefault();
31554                  if(s.hasChildNodes() && s.isExpanded()){
31555                      s.collapse();
31556                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31557                      this.select(s.parentNode, e);
31558                  }
31559              break;
31560         };
31561     }
31562 });
31563
31564 /**
31565  * @class Roo.tree.MultiSelectionModel
31566  * @extends Roo.util.Observable
31567  * Multi selection for a TreePanel.
31568  * @param {Object} cfg Configuration
31569  */
31570 Roo.tree.MultiSelectionModel = function(){
31571    this.selNodes = [];
31572    this.selMap = {};
31573    this.addEvents({
31574        /**
31575         * @event selectionchange
31576         * Fires when the selected nodes change
31577         * @param {MultiSelectionModel} this
31578         * @param {Array} nodes Array of the selected nodes
31579         */
31580        "selectionchange" : true
31581    });
31582    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31583    
31584 };
31585
31586 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31587     init : function(tree){
31588         this.tree = tree;
31589         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31590         tree.on("click", this.onNodeClick, this);
31591     },
31592     
31593     onNodeClick : function(node, e){
31594         this.select(node, e, e.ctrlKey);
31595     },
31596     
31597     /**
31598      * Select a node.
31599      * @param {TreeNode} node The node to select
31600      * @param {EventObject} e (optional) An event associated with the selection
31601      * @param {Boolean} keepExisting True to retain existing selections
31602      * @return {TreeNode} The selected node
31603      */
31604     select : function(node, e, keepExisting){
31605         if(keepExisting !== true){
31606             this.clearSelections(true);
31607         }
31608         if(this.isSelected(node)){
31609             this.lastSelNode = node;
31610             return node;
31611         }
31612         this.selNodes.push(node);
31613         this.selMap[node.id] = node;
31614         this.lastSelNode = node;
31615         node.ui.onSelectedChange(true);
31616         this.fireEvent("selectionchange", this, this.selNodes);
31617         return node;
31618     },
31619     
31620     /**
31621      * Deselect a node.
31622      * @param {TreeNode} node The node to unselect
31623      */
31624     unselect : function(node){
31625         if(this.selMap[node.id]){
31626             node.ui.onSelectedChange(false);
31627             var sn = this.selNodes;
31628             var index = -1;
31629             if(sn.indexOf){
31630                 index = sn.indexOf(node);
31631             }else{
31632                 for(var i = 0, len = sn.length; i < len; i++){
31633                     if(sn[i] == node){
31634                         index = i;
31635                         break;
31636                     }
31637                 }
31638             }
31639             if(index != -1){
31640                 this.selNodes.splice(index, 1);
31641             }
31642             delete this.selMap[node.id];
31643             this.fireEvent("selectionchange", this, this.selNodes);
31644         }
31645     },
31646     
31647     /**
31648      * Clear all selections
31649      */
31650     clearSelections : function(suppressEvent){
31651         var sn = this.selNodes;
31652         if(sn.length > 0){
31653             for(var i = 0, len = sn.length; i < len; i++){
31654                 sn[i].ui.onSelectedChange(false);
31655             }
31656             this.selNodes = [];
31657             this.selMap = {};
31658             if(suppressEvent !== true){
31659                 this.fireEvent("selectionchange", this, this.selNodes);
31660             }
31661         }
31662     },
31663     
31664     /**
31665      * Returns true if the node is selected
31666      * @param {TreeNode} node The node to check
31667      * @return {Boolean}
31668      */
31669     isSelected : function(node){
31670         return this.selMap[node.id] ? true : false;  
31671     },
31672     
31673     /**
31674      * Returns an array of the selected nodes
31675      * @return {Array}
31676      */
31677     getSelectedNodes : function(){
31678         return this.selNodes;    
31679     },
31680
31681     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31682
31683     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31684
31685     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31686 });/*
31687  * Based on:
31688  * Ext JS Library 1.1.1
31689  * Copyright(c) 2006-2007, Ext JS, LLC.
31690  *
31691  * Originally Released Under LGPL - original licence link has changed is not relivant.
31692  *
31693  * Fork - LGPL
31694  * <script type="text/javascript">
31695  */
31696  
31697 /**
31698  * @class Roo.tree.TreeNode
31699  * @extends Roo.data.Node
31700  * @cfg {String} text The text for this node
31701  * @cfg {Boolean} expanded true to start the node expanded
31702  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31703  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31704  * @cfg {Boolean} disabled true to start the node disabled
31705  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31706  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31707  * @cfg {String} cls A css class to be added to the node
31708  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31709  * @cfg {String} href URL of the link used for the node (defaults to #)
31710  * @cfg {String} hrefTarget target frame for the link
31711  * @cfg {String} qtip An Ext QuickTip for the node
31712  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31713  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31714  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31715  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31716  * (defaults to undefined with no checkbox rendered)
31717  * @constructor
31718  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31719  */
31720 Roo.tree.TreeNode = function(attributes){
31721     attributes = attributes || {};
31722     if(typeof attributes == "string"){
31723         attributes = {text: attributes};
31724     }
31725     this.childrenRendered = false;
31726     this.rendered = false;
31727     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31728     this.expanded = attributes.expanded === true;
31729     this.isTarget = attributes.isTarget !== false;
31730     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31731     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31732
31733     /**
31734      * Read-only. The text for this node. To change it use setText().
31735      * @type String
31736      */
31737     this.text = attributes.text;
31738     /**
31739      * True if this node is disabled.
31740      * @type Boolean
31741      */
31742     this.disabled = attributes.disabled === true;
31743
31744     this.addEvents({
31745         /**
31746         * @event textchange
31747         * Fires when the text for this node is changed
31748         * @param {Node} this This node
31749         * @param {String} text The new text
31750         * @param {String} oldText The old text
31751         */
31752         "textchange" : true,
31753         /**
31754         * @event beforeexpand
31755         * Fires before this node is expanded, return false to cancel.
31756         * @param {Node} this This node
31757         * @param {Boolean} deep
31758         * @param {Boolean} anim
31759         */
31760         "beforeexpand" : true,
31761         /**
31762         * @event beforecollapse
31763         * Fires before this node is collapsed, return false to cancel.
31764         * @param {Node} this This node
31765         * @param {Boolean} deep
31766         * @param {Boolean} anim
31767         */
31768         "beforecollapse" : true,
31769         /**
31770         * @event expand
31771         * Fires when this node is expanded
31772         * @param {Node} this This node
31773         */
31774         "expand" : true,
31775         /**
31776         * @event disabledchange
31777         * Fires when the disabled status of this node changes
31778         * @param {Node} this This node
31779         * @param {Boolean} disabled
31780         */
31781         "disabledchange" : true,
31782         /**
31783         * @event collapse
31784         * Fires when this node is collapsed
31785         * @param {Node} this This node
31786         */
31787         "collapse" : true,
31788         /**
31789         * @event beforeclick
31790         * Fires before click processing. Return false to cancel the default action.
31791         * @param {Node} this This node
31792         * @param {Roo.EventObject} e The event object
31793         */
31794         "beforeclick":true,
31795         /**
31796         * @event checkchange
31797         * Fires when a node with a checkbox's checked property changes
31798         * @param {Node} this This node
31799         * @param {Boolean} checked
31800         */
31801         "checkchange":true,
31802         /**
31803         * @event click
31804         * Fires when this node is clicked
31805         * @param {Node} this This node
31806         * @param {Roo.EventObject} e The event object
31807         */
31808         "click":true,
31809         /**
31810         * @event dblclick
31811         * Fires when this node is double clicked
31812         * @param {Node} this This node
31813         * @param {Roo.EventObject} e The event object
31814         */
31815         "dblclick":true,
31816         /**
31817         * @event contextmenu
31818         * Fires when this node is right clicked
31819         * @param {Node} this This node
31820         * @param {Roo.EventObject} e The event object
31821         */
31822         "contextmenu":true,
31823         /**
31824         * @event beforechildrenrendered
31825         * Fires right before the child nodes for this node are rendered
31826         * @param {Node} this This node
31827         */
31828         "beforechildrenrendered":true
31829     });
31830
31831     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31832
31833     /**
31834      * Read-only. The UI for this node
31835      * @type TreeNodeUI
31836      */
31837     this.ui = new uiClass(this);
31838     
31839     // finally support items[]
31840     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31841         return;
31842     }
31843     
31844     
31845     Roo.each(this.attributes.items, function(c) {
31846         this.appendChild(Roo.factory(c,Roo.Tree));
31847     }, this);
31848     delete this.attributes.items;
31849     
31850     
31851     
31852 };
31853 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31854     preventHScroll: true,
31855     /**
31856      * Returns true if this node is expanded
31857      * @return {Boolean}
31858      */
31859     isExpanded : function(){
31860         return this.expanded;
31861     },
31862
31863     /**
31864      * Returns the UI object for this node
31865      * @return {TreeNodeUI}
31866      */
31867     getUI : function(){
31868         return this.ui;
31869     },
31870
31871     // private override
31872     setFirstChild : function(node){
31873         var of = this.firstChild;
31874         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31875         if(this.childrenRendered && of && node != of){
31876             of.renderIndent(true, true);
31877         }
31878         if(this.rendered){
31879             this.renderIndent(true, true);
31880         }
31881     },
31882
31883     // private override
31884     setLastChild : function(node){
31885         var ol = this.lastChild;
31886         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31887         if(this.childrenRendered && ol && node != ol){
31888             ol.renderIndent(true, true);
31889         }
31890         if(this.rendered){
31891             this.renderIndent(true, true);
31892         }
31893     },
31894
31895     // these methods are overridden to provide lazy rendering support
31896     // private override
31897     appendChild : function()
31898     {
31899         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31900         if(node && this.childrenRendered){
31901             node.render();
31902         }
31903         this.ui.updateExpandIcon();
31904         return node;
31905     },
31906
31907     // private override
31908     removeChild : function(node){
31909         this.ownerTree.getSelectionModel().unselect(node);
31910         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31911         // if it's been rendered remove dom node
31912         if(this.childrenRendered){
31913             node.ui.remove();
31914         }
31915         if(this.childNodes.length < 1){
31916             this.collapse(false, false);
31917         }else{
31918             this.ui.updateExpandIcon();
31919         }
31920         if(!this.firstChild) {
31921             this.childrenRendered = false;
31922         }
31923         return node;
31924     },
31925
31926     // private override
31927     insertBefore : function(node, refNode){
31928         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31929         if(newNode && refNode && this.childrenRendered){
31930             node.render();
31931         }
31932         this.ui.updateExpandIcon();
31933         return newNode;
31934     },
31935
31936     /**
31937      * Sets the text for this node
31938      * @param {String} text
31939      */
31940     setText : function(text){
31941         var oldText = this.text;
31942         this.text = text;
31943         this.attributes.text = text;
31944         if(this.rendered){ // event without subscribing
31945             this.ui.onTextChange(this, text, oldText);
31946         }
31947         this.fireEvent("textchange", this, text, oldText);
31948     },
31949
31950     /**
31951      * Triggers selection of this node
31952      */
31953     select : function(){
31954         this.getOwnerTree().getSelectionModel().select(this);
31955     },
31956
31957     /**
31958      * Triggers deselection of this node
31959      */
31960     unselect : function(){
31961         this.getOwnerTree().getSelectionModel().unselect(this);
31962     },
31963
31964     /**
31965      * Returns true if this node is selected
31966      * @return {Boolean}
31967      */
31968     isSelected : function(){
31969         return this.getOwnerTree().getSelectionModel().isSelected(this);
31970     },
31971
31972     /**
31973      * Expand this node.
31974      * @param {Boolean} deep (optional) True to expand all children as well
31975      * @param {Boolean} anim (optional) false to cancel the default animation
31976      * @param {Function} callback (optional) A callback to be called when
31977      * expanding this node completes (does not wait for deep expand to complete).
31978      * Called with 1 parameter, this node.
31979      */
31980     expand : function(deep, anim, callback){
31981         if(!this.expanded){
31982             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31983                 return;
31984             }
31985             if(!this.childrenRendered){
31986                 this.renderChildren();
31987             }
31988             this.expanded = true;
31989             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31990                 this.ui.animExpand(function(){
31991                     this.fireEvent("expand", this);
31992                     if(typeof callback == "function"){
31993                         callback(this);
31994                     }
31995                     if(deep === true){
31996                         this.expandChildNodes(true);
31997                     }
31998                 }.createDelegate(this));
31999                 return;
32000             }else{
32001                 this.ui.expand();
32002                 this.fireEvent("expand", this);
32003                 if(typeof callback == "function"){
32004                     callback(this);
32005                 }
32006             }
32007         }else{
32008            if(typeof callback == "function"){
32009                callback(this);
32010            }
32011         }
32012         if(deep === true){
32013             this.expandChildNodes(true);
32014         }
32015     },
32016
32017     isHiddenRoot : function(){
32018         return this.isRoot && !this.getOwnerTree().rootVisible;
32019     },
32020
32021     /**
32022      * Collapse this node.
32023      * @param {Boolean} deep (optional) True to collapse all children as well
32024      * @param {Boolean} anim (optional) false to cancel the default animation
32025      */
32026     collapse : function(deep, anim){
32027         if(this.expanded && !this.isHiddenRoot()){
32028             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32029                 return;
32030             }
32031             this.expanded = false;
32032             if((this.getOwnerTree().animate && anim !== false) || anim){
32033                 this.ui.animCollapse(function(){
32034                     this.fireEvent("collapse", this);
32035                     if(deep === true){
32036                         this.collapseChildNodes(true);
32037                     }
32038                 }.createDelegate(this));
32039                 return;
32040             }else{
32041                 this.ui.collapse();
32042                 this.fireEvent("collapse", this);
32043             }
32044         }
32045         if(deep === true){
32046             var cs = this.childNodes;
32047             for(var i = 0, len = cs.length; i < len; i++) {
32048                 cs[i].collapse(true, false);
32049             }
32050         }
32051     },
32052
32053     // private
32054     delayedExpand : function(delay){
32055         if(!this.expandProcId){
32056             this.expandProcId = this.expand.defer(delay, this);
32057         }
32058     },
32059
32060     // private
32061     cancelExpand : function(){
32062         if(this.expandProcId){
32063             clearTimeout(this.expandProcId);
32064         }
32065         this.expandProcId = false;
32066     },
32067
32068     /**
32069      * Toggles expanded/collapsed state of the node
32070      */
32071     toggle : function(){
32072         if(this.expanded){
32073             this.collapse();
32074         }else{
32075             this.expand();
32076         }
32077     },
32078
32079     /**
32080      * Ensures all parent nodes are expanded
32081      */
32082     ensureVisible : function(callback){
32083         var tree = this.getOwnerTree();
32084         tree.expandPath(this.parentNode.getPath(), false, function(){
32085             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32086             Roo.callback(callback);
32087         }.createDelegate(this));
32088     },
32089
32090     /**
32091      * Expand all child nodes
32092      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32093      */
32094     expandChildNodes : function(deep){
32095         var cs = this.childNodes;
32096         for(var i = 0, len = cs.length; i < len; i++) {
32097                 cs[i].expand(deep);
32098         }
32099     },
32100
32101     /**
32102      * Collapse all child nodes
32103      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32104      */
32105     collapseChildNodes : function(deep){
32106         var cs = this.childNodes;
32107         for(var i = 0, len = cs.length; i < len; i++) {
32108                 cs[i].collapse(deep);
32109         }
32110     },
32111
32112     /**
32113      * Disables this node
32114      */
32115     disable : function(){
32116         this.disabled = true;
32117         this.unselect();
32118         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32119             this.ui.onDisableChange(this, true);
32120         }
32121         this.fireEvent("disabledchange", this, true);
32122     },
32123
32124     /**
32125      * Enables this node
32126      */
32127     enable : function(){
32128         this.disabled = false;
32129         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32130             this.ui.onDisableChange(this, false);
32131         }
32132         this.fireEvent("disabledchange", this, false);
32133     },
32134
32135     // private
32136     renderChildren : function(suppressEvent){
32137         if(suppressEvent !== false){
32138             this.fireEvent("beforechildrenrendered", this);
32139         }
32140         var cs = this.childNodes;
32141         for(var i = 0, len = cs.length; i < len; i++){
32142             cs[i].render(true);
32143         }
32144         this.childrenRendered = true;
32145     },
32146
32147     // private
32148     sort : function(fn, scope){
32149         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32150         if(this.childrenRendered){
32151             var cs = this.childNodes;
32152             for(var i = 0, len = cs.length; i < len; i++){
32153                 cs[i].render(true);
32154             }
32155         }
32156     },
32157
32158     // private
32159     render : function(bulkRender){
32160         this.ui.render(bulkRender);
32161         if(!this.rendered){
32162             this.rendered = true;
32163             if(this.expanded){
32164                 this.expanded = false;
32165                 this.expand(false, false);
32166             }
32167         }
32168     },
32169
32170     // private
32171     renderIndent : function(deep, refresh){
32172         if(refresh){
32173             this.ui.childIndent = null;
32174         }
32175         this.ui.renderIndent();
32176         if(deep === true && this.childrenRendered){
32177             var cs = this.childNodes;
32178             for(var i = 0, len = cs.length; i < len; i++){
32179                 cs[i].renderIndent(true, refresh);
32180             }
32181         }
32182     }
32183 });/*
32184  * Based on:
32185  * Ext JS Library 1.1.1
32186  * Copyright(c) 2006-2007, Ext JS, LLC.
32187  *
32188  * Originally Released Under LGPL - original licence link has changed is not relivant.
32189  *
32190  * Fork - LGPL
32191  * <script type="text/javascript">
32192  */
32193  
32194 /**
32195  * @class Roo.tree.AsyncTreeNode
32196  * @extends Roo.tree.TreeNode
32197  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32198  * @constructor
32199  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32200  */
32201  Roo.tree.AsyncTreeNode = function(config){
32202     this.loaded = false;
32203     this.loading = false;
32204     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32205     /**
32206     * @event beforeload
32207     * Fires before this node is loaded, return false to cancel
32208     * @param {Node} this This node
32209     */
32210     this.addEvents({'beforeload':true, 'load': true});
32211     /**
32212     * @event load
32213     * Fires when this node is loaded
32214     * @param {Node} this This node
32215     */
32216     /**
32217      * The loader used by this node (defaults to using the tree's defined loader)
32218      * @type TreeLoader
32219      * @property loader
32220      */
32221 };
32222 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32223     expand : function(deep, anim, callback){
32224         if(this.loading){ // if an async load is already running, waiting til it's done
32225             var timer;
32226             var f = function(){
32227                 if(!this.loading){ // done loading
32228                     clearInterval(timer);
32229                     this.expand(deep, anim, callback);
32230                 }
32231             }.createDelegate(this);
32232             timer = setInterval(f, 200);
32233             return;
32234         }
32235         if(!this.loaded){
32236             if(this.fireEvent("beforeload", this) === false){
32237                 return;
32238             }
32239             this.loading = true;
32240             this.ui.beforeLoad(this);
32241             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32242             if(loader){
32243                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32244                 return;
32245             }
32246         }
32247         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32248     },
32249     
32250     /**
32251      * Returns true if this node is currently loading
32252      * @return {Boolean}
32253      */
32254     isLoading : function(){
32255         return this.loading;  
32256     },
32257     
32258     loadComplete : function(deep, anim, callback){
32259         this.loading = false;
32260         this.loaded = true;
32261         this.ui.afterLoad(this);
32262         this.fireEvent("load", this);
32263         this.expand(deep, anim, callback);
32264     },
32265     
32266     /**
32267      * Returns true if this node has been loaded
32268      * @return {Boolean}
32269      */
32270     isLoaded : function(){
32271         return this.loaded;
32272     },
32273     
32274     hasChildNodes : function(){
32275         if(!this.isLeaf() && !this.loaded){
32276             return true;
32277         }else{
32278             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32279         }
32280     },
32281
32282     /**
32283      * Trigger a reload for this node
32284      * @param {Function} callback
32285      */
32286     reload : function(callback){
32287         this.collapse(false, false);
32288         while(this.firstChild){
32289             this.removeChild(this.firstChild);
32290         }
32291         this.childrenRendered = false;
32292         this.loaded = false;
32293         if(this.isHiddenRoot()){
32294             this.expanded = false;
32295         }
32296         this.expand(false, false, callback);
32297     }
32298 });/*
32299  * Based on:
32300  * Ext JS Library 1.1.1
32301  * Copyright(c) 2006-2007, Ext JS, LLC.
32302  *
32303  * Originally Released Under LGPL - original licence link has changed is not relivant.
32304  *
32305  * Fork - LGPL
32306  * <script type="text/javascript">
32307  */
32308  
32309 /**
32310  * @class Roo.tree.TreeNodeUI
32311  * @constructor
32312  * @param {Object} node The node to render
32313  * The TreeNode UI implementation is separate from the
32314  * tree implementation. Unless you are customizing the tree UI,
32315  * you should never have to use this directly.
32316  */
32317 Roo.tree.TreeNodeUI = function(node){
32318     this.node = node;
32319     this.rendered = false;
32320     this.animating = false;
32321     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32322 };
32323
32324 Roo.tree.TreeNodeUI.prototype = {
32325     removeChild : function(node){
32326         if(this.rendered){
32327             this.ctNode.removeChild(node.ui.getEl());
32328         }
32329     },
32330
32331     beforeLoad : function(){
32332          this.addClass("x-tree-node-loading");
32333     },
32334
32335     afterLoad : function(){
32336          this.removeClass("x-tree-node-loading");
32337     },
32338
32339     onTextChange : function(node, text, oldText){
32340         if(this.rendered){
32341             this.textNode.innerHTML = text;
32342         }
32343     },
32344
32345     onDisableChange : function(node, state){
32346         this.disabled = state;
32347         if(state){
32348             this.addClass("x-tree-node-disabled");
32349         }else{
32350             this.removeClass("x-tree-node-disabled");
32351         }
32352     },
32353
32354     onSelectedChange : function(state){
32355         if(state){
32356             this.focus();
32357             this.addClass("x-tree-selected");
32358         }else{
32359             //this.blur();
32360             this.removeClass("x-tree-selected");
32361         }
32362     },
32363
32364     onMove : function(tree, node, oldParent, newParent, index, refNode){
32365         this.childIndent = null;
32366         if(this.rendered){
32367             var targetNode = newParent.ui.getContainer();
32368             if(!targetNode){//target not rendered
32369                 this.holder = document.createElement("div");
32370                 this.holder.appendChild(this.wrap);
32371                 return;
32372             }
32373             var insertBefore = refNode ? refNode.ui.getEl() : null;
32374             if(insertBefore){
32375                 targetNode.insertBefore(this.wrap, insertBefore);
32376             }else{
32377                 targetNode.appendChild(this.wrap);
32378             }
32379             this.node.renderIndent(true);
32380         }
32381     },
32382
32383     addClass : function(cls){
32384         if(this.elNode){
32385             Roo.fly(this.elNode).addClass(cls);
32386         }
32387     },
32388
32389     removeClass : function(cls){
32390         if(this.elNode){
32391             Roo.fly(this.elNode).removeClass(cls);
32392         }
32393     },
32394
32395     remove : function(){
32396         if(this.rendered){
32397             this.holder = document.createElement("div");
32398             this.holder.appendChild(this.wrap);
32399         }
32400     },
32401
32402     fireEvent : function(){
32403         return this.node.fireEvent.apply(this.node, arguments);
32404     },
32405
32406     initEvents : function(){
32407         this.node.on("move", this.onMove, this);
32408         var E = Roo.EventManager;
32409         var a = this.anchor;
32410
32411         var el = Roo.fly(a, '_treeui');
32412
32413         if(Roo.isOpera){ // opera render bug ignores the CSS
32414             el.setStyle("text-decoration", "none");
32415         }
32416
32417         el.on("click", this.onClick, this);
32418         el.on("dblclick", this.onDblClick, this);
32419
32420         if(this.checkbox){
32421             Roo.EventManager.on(this.checkbox,
32422                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32423         }
32424
32425         el.on("contextmenu", this.onContextMenu, this);
32426
32427         var icon = Roo.fly(this.iconNode);
32428         icon.on("click", this.onClick, this);
32429         icon.on("dblclick", this.onDblClick, this);
32430         icon.on("contextmenu", this.onContextMenu, this);
32431         E.on(this.ecNode, "click", this.ecClick, this, true);
32432
32433         if(this.node.disabled){
32434             this.addClass("x-tree-node-disabled");
32435         }
32436         if(this.node.hidden){
32437             this.addClass("x-tree-node-disabled");
32438         }
32439         var ot = this.node.getOwnerTree();
32440         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32441         if(dd && (!this.node.isRoot || ot.rootVisible)){
32442             Roo.dd.Registry.register(this.elNode, {
32443                 node: this.node,
32444                 handles: this.getDDHandles(),
32445                 isHandle: false
32446             });
32447         }
32448     },
32449
32450     getDDHandles : function(){
32451         return [this.iconNode, this.textNode];
32452     },
32453
32454     hide : function(){
32455         if(this.rendered){
32456             this.wrap.style.display = "none";
32457         }
32458     },
32459
32460     show : function(){
32461         if(this.rendered){
32462             this.wrap.style.display = "";
32463         }
32464     },
32465
32466     onContextMenu : function(e){
32467         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32468             e.preventDefault();
32469             this.focus();
32470             this.fireEvent("contextmenu", this.node, e);
32471         }
32472     },
32473
32474     onClick : function(e){
32475         if(this.dropping){
32476             e.stopEvent();
32477             return;
32478         }
32479         if(this.fireEvent("beforeclick", this.node, e) !== false){
32480             if(!this.disabled && this.node.attributes.href){
32481                 this.fireEvent("click", this.node, e);
32482                 return;
32483             }
32484             e.preventDefault();
32485             if(this.disabled){
32486                 return;
32487             }
32488
32489             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32490                 this.node.toggle();
32491             }
32492
32493             this.fireEvent("click", this.node, e);
32494         }else{
32495             e.stopEvent();
32496         }
32497     },
32498
32499     onDblClick : function(e){
32500         e.preventDefault();
32501         if(this.disabled){
32502             return;
32503         }
32504         if(this.checkbox){
32505             this.toggleCheck();
32506         }
32507         if(!this.animating && this.node.hasChildNodes()){
32508             this.node.toggle();
32509         }
32510         this.fireEvent("dblclick", this.node, e);
32511     },
32512
32513     onCheckChange : function(){
32514         var checked = this.checkbox.checked;
32515         this.node.attributes.checked = checked;
32516         this.fireEvent('checkchange', this.node, checked);
32517     },
32518
32519     ecClick : function(e){
32520         if(!this.animating && this.node.hasChildNodes()){
32521             this.node.toggle();
32522         }
32523     },
32524
32525     startDrop : function(){
32526         this.dropping = true;
32527     },
32528
32529     // delayed drop so the click event doesn't get fired on a drop
32530     endDrop : function(){
32531        setTimeout(function(){
32532            this.dropping = false;
32533        }.createDelegate(this), 50);
32534     },
32535
32536     expand : function(){
32537         this.updateExpandIcon();
32538         this.ctNode.style.display = "";
32539     },
32540
32541     focus : function(){
32542         if(!this.node.preventHScroll){
32543             try{this.anchor.focus();
32544             }catch(e){}
32545         }else if(!Roo.isIE){
32546             try{
32547                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32548                 var l = noscroll.scrollLeft;
32549                 this.anchor.focus();
32550                 noscroll.scrollLeft = l;
32551             }catch(e){}
32552         }
32553     },
32554
32555     toggleCheck : function(value){
32556         var cb = this.checkbox;
32557         if(cb){
32558             cb.checked = (value === undefined ? !cb.checked : value);
32559         }
32560     },
32561
32562     blur : function(){
32563         try{
32564             this.anchor.blur();
32565         }catch(e){}
32566     },
32567
32568     animExpand : function(callback){
32569         var ct = Roo.get(this.ctNode);
32570         ct.stopFx();
32571         if(!this.node.hasChildNodes()){
32572             this.updateExpandIcon();
32573             this.ctNode.style.display = "";
32574             Roo.callback(callback);
32575             return;
32576         }
32577         this.animating = true;
32578         this.updateExpandIcon();
32579
32580         ct.slideIn('t', {
32581            callback : function(){
32582                this.animating = false;
32583                Roo.callback(callback);
32584             },
32585             scope: this,
32586             duration: this.node.ownerTree.duration || .25
32587         });
32588     },
32589
32590     highlight : function(){
32591         var tree = this.node.getOwnerTree();
32592         Roo.fly(this.wrap).highlight(
32593             tree.hlColor || "C3DAF9",
32594             {endColor: tree.hlBaseColor}
32595         );
32596     },
32597
32598     collapse : function(){
32599         this.updateExpandIcon();
32600         this.ctNode.style.display = "none";
32601     },
32602
32603     animCollapse : function(callback){
32604         var ct = Roo.get(this.ctNode);
32605         ct.enableDisplayMode('block');
32606         ct.stopFx();
32607
32608         this.animating = true;
32609         this.updateExpandIcon();
32610
32611         ct.slideOut('t', {
32612             callback : function(){
32613                this.animating = false;
32614                Roo.callback(callback);
32615             },
32616             scope: this,
32617             duration: this.node.ownerTree.duration || .25
32618         });
32619     },
32620
32621     getContainer : function(){
32622         return this.ctNode;
32623     },
32624
32625     getEl : function(){
32626         return this.wrap;
32627     },
32628
32629     appendDDGhost : function(ghostNode){
32630         ghostNode.appendChild(this.elNode.cloneNode(true));
32631     },
32632
32633     getDDRepairXY : function(){
32634         return Roo.lib.Dom.getXY(this.iconNode);
32635     },
32636
32637     onRender : function(){
32638         this.render();
32639     },
32640
32641     render : function(bulkRender){
32642         var n = this.node, a = n.attributes;
32643         var targetNode = n.parentNode ?
32644               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32645
32646         if(!this.rendered){
32647             this.rendered = true;
32648
32649             this.renderElements(n, a, targetNode, bulkRender);
32650
32651             if(a.qtip){
32652                if(this.textNode.setAttributeNS){
32653                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32654                    if(a.qtipTitle){
32655                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32656                    }
32657                }else{
32658                    this.textNode.setAttribute("ext:qtip", a.qtip);
32659                    if(a.qtipTitle){
32660                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32661                    }
32662                }
32663             }else if(a.qtipCfg){
32664                 a.qtipCfg.target = Roo.id(this.textNode);
32665                 Roo.QuickTips.register(a.qtipCfg);
32666             }
32667             this.initEvents();
32668             if(!this.node.expanded){
32669                 this.updateExpandIcon();
32670             }
32671         }else{
32672             if(bulkRender === true) {
32673                 targetNode.appendChild(this.wrap);
32674             }
32675         }
32676     },
32677
32678     renderElements : function(n, a, targetNode, bulkRender)
32679     {
32680         // add some indent caching, this helps performance when rendering a large tree
32681         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32682         var t = n.getOwnerTree();
32683         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32684         if (typeof(n.attributes.html) != 'undefined') {
32685             txt = n.attributes.html;
32686         }
32687         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32688         var cb = typeof a.checked == 'boolean';
32689         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32690         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32691             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32692             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32693             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32694             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32695             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32696              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32697                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32698             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32699             "</li>"];
32700
32701         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32702             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32703                                 n.nextSibling.ui.getEl(), buf.join(""));
32704         }else{
32705             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32706         }
32707
32708         this.elNode = this.wrap.childNodes[0];
32709         this.ctNode = this.wrap.childNodes[1];
32710         var cs = this.elNode.childNodes;
32711         this.indentNode = cs[0];
32712         this.ecNode = cs[1];
32713         this.iconNode = cs[2];
32714         var index = 3;
32715         if(cb){
32716             this.checkbox = cs[3];
32717             index++;
32718         }
32719         this.anchor = cs[index];
32720         this.textNode = cs[index].firstChild;
32721     },
32722
32723     getAnchor : function(){
32724         return this.anchor;
32725     },
32726
32727     getTextEl : function(){
32728         return this.textNode;
32729     },
32730
32731     getIconEl : function(){
32732         return this.iconNode;
32733     },
32734
32735     isChecked : function(){
32736         return this.checkbox ? this.checkbox.checked : false;
32737     },
32738
32739     updateExpandIcon : function(){
32740         if(this.rendered){
32741             var n = this.node, c1, c2;
32742             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32743             var hasChild = n.hasChildNodes();
32744             if(hasChild){
32745                 if(n.expanded){
32746                     cls += "-minus";
32747                     c1 = "x-tree-node-collapsed";
32748                     c2 = "x-tree-node-expanded";
32749                 }else{
32750                     cls += "-plus";
32751                     c1 = "x-tree-node-expanded";
32752                     c2 = "x-tree-node-collapsed";
32753                 }
32754                 if(this.wasLeaf){
32755                     this.removeClass("x-tree-node-leaf");
32756                     this.wasLeaf = false;
32757                 }
32758                 if(this.c1 != c1 || this.c2 != c2){
32759                     Roo.fly(this.elNode).replaceClass(c1, c2);
32760                     this.c1 = c1; this.c2 = c2;
32761                 }
32762             }else{
32763                 // this changes non-leafs into leafs if they have no children.
32764                 // it's not very rational behaviour..
32765                 
32766                 if(!this.wasLeaf && this.node.leaf){
32767                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32768                     delete this.c1;
32769                     delete this.c2;
32770                     this.wasLeaf = true;
32771                 }
32772             }
32773             var ecc = "x-tree-ec-icon "+cls;
32774             if(this.ecc != ecc){
32775                 this.ecNode.className = ecc;
32776                 this.ecc = ecc;
32777             }
32778         }
32779     },
32780
32781     getChildIndent : function(){
32782         if(!this.childIndent){
32783             var buf = [];
32784             var p = this.node;
32785             while(p){
32786                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32787                     if(!p.isLast()) {
32788                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32789                     } else {
32790                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32791                     }
32792                 }
32793                 p = p.parentNode;
32794             }
32795             this.childIndent = buf.join("");
32796         }
32797         return this.childIndent;
32798     },
32799
32800     renderIndent : function(){
32801         if(this.rendered){
32802             var indent = "";
32803             var p = this.node.parentNode;
32804             if(p){
32805                 indent = p.ui.getChildIndent();
32806             }
32807             if(this.indentMarkup != indent){ // don't rerender if not required
32808                 this.indentNode.innerHTML = indent;
32809                 this.indentMarkup = indent;
32810             }
32811             this.updateExpandIcon();
32812         }
32813     }
32814 };
32815
32816 Roo.tree.RootTreeNodeUI = function(){
32817     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32818 };
32819 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32820     render : function(){
32821         if(!this.rendered){
32822             var targetNode = this.node.ownerTree.innerCt.dom;
32823             this.node.expanded = true;
32824             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32825             this.wrap = this.ctNode = targetNode.firstChild;
32826         }
32827     },
32828     collapse : function(){
32829     },
32830     expand : function(){
32831     }
32832 });/*
32833  * Based on:
32834  * Ext JS Library 1.1.1
32835  * Copyright(c) 2006-2007, Ext JS, LLC.
32836  *
32837  * Originally Released Under LGPL - original licence link has changed is not relivant.
32838  *
32839  * Fork - LGPL
32840  * <script type="text/javascript">
32841  */
32842 /**
32843  * @class Roo.tree.TreeLoader
32844  * @extends Roo.util.Observable
32845  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32846  * nodes from a specified URL. The response must be a javascript Array definition
32847  * who's elements are node definition objects. eg:
32848  * <pre><code>
32849    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32850     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32851 </code></pre>
32852  * <br><br>
32853  * A server request is sent, and child nodes are loaded only when a node is expanded.
32854  * The loading node's id is passed to the server under the parameter name "node" to
32855  * enable the server to produce the correct child nodes.
32856  * <br><br>
32857  * To pass extra parameters, an event handler may be attached to the "beforeload"
32858  * event, and the parameters specified in the TreeLoader's baseParams property:
32859  * <pre><code>
32860     myTreeLoader.on("beforeload", function(treeLoader, node) {
32861         this.baseParams.category = node.attributes.category;
32862     }, this);
32863 </code></pre><
32864  * This would pass an HTTP parameter called "category" to the server containing
32865  * the value of the Node's "category" attribute.
32866  * @constructor
32867  * Creates a new Treeloader.
32868  * @param {Object} config A config object containing config properties.
32869  */
32870 Roo.tree.TreeLoader = function(config){
32871     this.baseParams = {};
32872     this.requestMethod = "POST";
32873     Roo.apply(this, config);
32874
32875     this.addEvents({
32876     
32877         /**
32878          * @event beforeload
32879          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32880          * @param {Object} This TreeLoader object.
32881          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32882          * @param {Object} callback The callback function specified in the {@link #load} call.
32883          */
32884         beforeload : true,
32885         /**
32886          * @event load
32887          * Fires when the node has been successfuly loaded.
32888          * @param {Object} This TreeLoader object.
32889          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32890          * @param {Object} response The response object containing the data from the server.
32891          */
32892         load : true,
32893         /**
32894          * @event loadexception
32895          * Fires if the network request failed.
32896          * @param {Object} This TreeLoader object.
32897          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32898          * @param {Object} response The response object containing the data from the server.
32899          */
32900         loadexception : true,
32901         /**
32902          * @event create
32903          * Fires before a node is created, enabling you to return custom Node types 
32904          * @param {Object} This TreeLoader object.
32905          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32906          */
32907         create : true
32908     });
32909
32910     Roo.tree.TreeLoader.superclass.constructor.call(this);
32911 };
32912
32913 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32914     /**
32915     * @cfg {String} dataUrl The URL from which to request a Json string which
32916     * specifies an array of node definition object representing the child nodes
32917     * to be loaded.
32918     */
32919     /**
32920     * @cfg {Object} baseParams (optional) An object containing properties which
32921     * specify HTTP parameters to be passed to each request for child nodes.
32922     */
32923     /**
32924     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32925     * created by this loader. If the attributes sent by the server have an attribute in this object,
32926     * they take priority.
32927     */
32928     /**
32929     * @cfg {Object} uiProviders (optional) An object containing properties which
32930     * 
32931     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32932     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32933     * <i>uiProvider</i> attribute of a returned child node is a string rather
32934     * than a reference to a TreeNodeUI implementation, this that string value
32935     * is used as a property name in the uiProviders object. You can define the provider named
32936     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32937     */
32938     uiProviders : {},
32939
32940     /**
32941     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32942     * child nodes before loading.
32943     */
32944     clearOnLoad : true,
32945
32946     /**
32947     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32948     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32949     * Grid query { data : [ .....] }
32950     */
32951     
32952     root : false,
32953      /**
32954     * @cfg {String} queryParam (optional) 
32955     * Name of the query as it will be passed on the querystring (defaults to 'node')
32956     * eg. the request will be ?node=[id]
32957     */
32958     
32959     
32960     queryParam: false,
32961     
32962     /**
32963      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32964      * This is called automatically when a node is expanded, but may be used to reload
32965      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32966      * @param {Roo.tree.TreeNode} node
32967      * @param {Function} callback
32968      */
32969     load : function(node, callback){
32970         if(this.clearOnLoad){
32971             while(node.firstChild){
32972                 node.removeChild(node.firstChild);
32973             }
32974         }
32975         if(node.attributes.children){ // preloaded json children
32976             var cs = node.attributes.children;
32977             for(var i = 0, len = cs.length; i < len; i++){
32978                 node.appendChild(this.createNode(cs[i]));
32979             }
32980             if(typeof callback == "function"){
32981                 callback();
32982             }
32983         }else if(this.dataUrl){
32984             this.requestData(node, callback);
32985         }
32986     },
32987
32988     getParams: function(node){
32989         var buf = [], bp = this.baseParams;
32990         for(var key in bp){
32991             if(typeof bp[key] != "function"){
32992                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32993             }
32994         }
32995         var n = this.queryParam === false ? 'node' : this.queryParam;
32996         buf.push(n + "=", encodeURIComponent(node.id));
32997         return buf.join("");
32998     },
32999
33000     requestData : function(node, callback){
33001         if(this.fireEvent("beforeload", this, node, callback) !== false){
33002             this.transId = Roo.Ajax.request({
33003                 method:this.requestMethod,
33004                 url: this.dataUrl||this.url,
33005                 success: this.handleResponse,
33006                 failure: this.handleFailure,
33007                 scope: this,
33008                 argument: {callback: callback, node: node},
33009                 params: this.getParams(node)
33010             });
33011         }else{
33012             // if the load is cancelled, make sure we notify
33013             // the node that we are done
33014             if(typeof callback == "function"){
33015                 callback();
33016             }
33017         }
33018     },
33019
33020     isLoading : function(){
33021         return this.transId ? true : false;
33022     },
33023
33024     abort : function(){
33025         if(this.isLoading()){
33026             Roo.Ajax.abort(this.transId);
33027         }
33028     },
33029
33030     // private
33031     createNode : function(attr)
33032     {
33033         // apply baseAttrs, nice idea Corey!
33034         if(this.baseAttrs){
33035             Roo.applyIf(attr, this.baseAttrs);
33036         }
33037         if(this.applyLoader !== false){
33038             attr.loader = this;
33039         }
33040         // uiProvider = depreciated..
33041         
33042         if(typeof(attr.uiProvider) == 'string'){
33043            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33044                 /**  eval:var:attr */ eval(attr.uiProvider);
33045         }
33046         if(typeof(this.uiProviders['default']) != 'undefined') {
33047             attr.uiProvider = this.uiProviders['default'];
33048         }
33049         
33050         this.fireEvent('create', this, attr);
33051         
33052         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33053         return(attr.leaf ?
33054                         new Roo.tree.TreeNode(attr) :
33055                         new Roo.tree.AsyncTreeNode(attr));
33056     },
33057
33058     processResponse : function(response, node, callback)
33059     {
33060         var json = response.responseText;
33061         try {
33062             
33063             var o = Roo.decode(json);
33064             
33065             if (!o.success) {
33066                 // it's a failure condition.
33067                 var a = response.argument;
33068                 this.fireEvent("loadexception", this, a.node, response);
33069                 Roo.log("Load failed - should have a handler really");
33070                 return;
33071             }
33072             
33073             if (this.root !== false) {
33074                 o = o[this.root];
33075             }
33076             
33077             for(var i = 0, len = o.length; i < len; i++){
33078                 var n = this.createNode(o[i]);
33079                 if(n){
33080                     node.appendChild(n);
33081                 }
33082             }
33083             if(typeof callback == "function"){
33084                 callback(this, node);
33085             }
33086         }catch(e){
33087             this.handleFailure(response);
33088         }
33089     },
33090
33091     handleResponse : function(response){
33092         this.transId = false;
33093         var a = response.argument;
33094         this.processResponse(response, a.node, a.callback);
33095         this.fireEvent("load", this, a.node, response);
33096     },
33097
33098     handleFailure : function(response)
33099     {
33100         // should handle failure better..
33101         this.transId = false;
33102         var a = response.argument;
33103         this.fireEvent("loadexception", this, a.node, response);
33104         if(typeof a.callback == "function"){
33105             a.callback(this, a.node);
33106         }
33107     }
33108 });/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118
33119 /**
33120 * @class Roo.tree.TreeFilter
33121 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33122 * @param {TreePanel} tree
33123 * @param {Object} config (optional)
33124  */
33125 Roo.tree.TreeFilter = function(tree, config){
33126     this.tree = tree;
33127     this.filtered = {};
33128     Roo.apply(this, config);
33129 };
33130
33131 Roo.tree.TreeFilter.prototype = {
33132     clearBlank:false,
33133     reverse:false,
33134     autoClear:false,
33135     remove:false,
33136
33137      /**
33138      * Filter the data by a specific attribute.
33139      * @param {String/RegExp} value Either string that the attribute value
33140      * should start with or a RegExp to test against the attribute
33141      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33142      * @param {TreeNode} startNode (optional) The node to start the filter at.
33143      */
33144     filter : function(value, attr, startNode){
33145         attr = attr || "text";
33146         var f;
33147         if(typeof value == "string"){
33148             var vlen = value.length;
33149             // auto clear empty filter
33150             if(vlen == 0 && this.clearBlank){
33151                 this.clear();
33152                 return;
33153             }
33154             value = value.toLowerCase();
33155             f = function(n){
33156                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33157             };
33158         }else if(value.exec){ // regex?
33159             f = function(n){
33160                 return value.test(n.attributes[attr]);
33161             };
33162         }else{
33163             throw 'Illegal filter type, must be string or regex';
33164         }
33165         this.filterBy(f, null, startNode);
33166         },
33167
33168     /**
33169      * Filter by a function. The passed function will be called with each
33170      * node in the tree (or from the startNode). If the function returns true, the node is kept
33171      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33172      * @param {Function} fn The filter function
33173      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33174      */
33175     filterBy : function(fn, scope, startNode){
33176         startNode = startNode || this.tree.root;
33177         if(this.autoClear){
33178             this.clear();
33179         }
33180         var af = this.filtered, rv = this.reverse;
33181         var f = function(n){
33182             if(n == startNode){
33183                 return true;
33184             }
33185             if(af[n.id]){
33186                 return false;
33187             }
33188             var m = fn.call(scope || n, n);
33189             if(!m || rv){
33190                 af[n.id] = n;
33191                 n.ui.hide();
33192                 return false;
33193             }
33194             return true;
33195         };
33196         startNode.cascade(f);
33197         if(this.remove){
33198            for(var id in af){
33199                if(typeof id != "function"){
33200                    var n = af[id];
33201                    if(n && n.parentNode){
33202                        n.parentNode.removeChild(n);
33203                    }
33204                }
33205            }
33206         }
33207     },
33208
33209     /**
33210      * Clears the current filter. Note: with the "remove" option
33211      * set a filter cannot be cleared.
33212      */
33213     clear : function(){
33214         var t = this.tree;
33215         var af = this.filtered;
33216         for(var id in af){
33217             if(typeof id != "function"){
33218                 var n = af[id];
33219                 if(n){
33220                     n.ui.show();
33221                 }
33222             }
33223         }
33224         this.filtered = {};
33225     }
33226 };
33227 /*
33228  * Based on:
33229  * Ext JS Library 1.1.1
33230  * Copyright(c) 2006-2007, Ext JS, LLC.
33231  *
33232  * Originally Released Under LGPL - original licence link has changed is not relivant.
33233  *
33234  * Fork - LGPL
33235  * <script type="text/javascript">
33236  */
33237  
33238
33239 /**
33240  * @class Roo.tree.TreeSorter
33241  * Provides sorting of nodes in a TreePanel
33242  * 
33243  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33244  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33245  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33246  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33247  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33248  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33249  * @constructor
33250  * @param {TreePanel} tree
33251  * @param {Object} config
33252  */
33253 Roo.tree.TreeSorter = function(tree, config){
33254     Roo.apply(this, config);
33255     tree.on("beforechildrenrendered", this.doSort, this);
33256     tree.on("append", this.updateSort, this);
33257     tree.on("insert", this.updateSort, this);
33258     
33259     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33260     var p = this.property || "text";
33261     var sortType = this.sortType;
33262     var fs = this.folderSort;
33263     var cs = this.caseSensitive === true;
33264     var leafAttr = this.leafAttr || 'leaf';
33265
33266     this.sortFn = function(n1, n2){
33267         if(fs){
33268             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33269                 return 1;
33270             }
33271             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33272                 return -1;
33273             }
33274         }
33275         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33276         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33277         if(v1 < v2){
33278                         return dsc ? +1 : -1;
33279                 }else if(v1 > v2){
33280                         return dsc ? -1 : +1;
33281         }else{
33282                 return 0;
33283         }
33284     };
33285 };
33286
33287 Roo.tree.TreeSorter.prototype = {
33288     doSort : function(node){
33289         node.sort(this.sortFn);
33290     },
33291     
33292     compareNodes : function(n1, n2){
33293         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33294     },
33295     
33296     updateSort : function(tree, node){
33297         if(node.childrenRendered){
33298             this.doSort.defer(1, this, [node]);
33299         }
33300     }
33301 };/*
33302  * Based on:
33303  * Ext JS Library 1.1.1
33304  * Copyright(c) 2006-2007, Ext JS, LLC.
33305  *
33306  * Originally Released Under LGPL - original licence link has changed is not relivant.
33307  *
33308  * Fork - LGPL
33309  * <script type="text/javascript">
33310  */
33311
33312 if(Roo.dd.DropZone){
33313     
33314 Roo.tree.TreeDropZone = function(tree, config){
33315     this.allowParentInsert = false;
33316     this.allowContainerDrop = false;
33317     this.appendOnly = false;
33318     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33319     this.tree = tree;
33320     this.lastInsertClass = "x-tree-no-status";
33321     this.dragOverData = {};
33322 };
33323
33324 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33325     ddGroup : "TreeDD",
33326     
33327     expandDelay : 1000,
33328     
33329     expandNode : function(node){
33330         if(node.hasChildNodes() && !node.isExpanded()){
33331             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33332         }
33333     },
33334     
33335     queueExpand : function(node){
33336         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33337     },
33338     
33339     cancelExpand : function(){
33340         if(this.expandProcId){
33341             clearTimeout(this.expandProcId);
33342             this.expandProcId = false;
33343         }
33344     },
33345     
33346     isValidDropPoint : function(n, pt, dd, e, data){
33347         if(!n || !data){ return false; }
33348         var targetNode = n.node;
33349         var dropNode = data.node;
33350         // default drop rules
33351         if(!(targetNode && targetNode.isTarget && pt)){
33352             return false;
33353         }
33354         if(pt == "append" && targetNode.allowChildren === false){
33355             return false;
33356         }
33357         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33358             return false;
33359         }
33360         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33361             return false;
33362         }
33363         // reuse the object
33364         var overEvent = this.dragOverData;
33365         overEvent.tree = this.tree;
33366         overEvent.target = targetNode;
33367         overEvent.data = data;
33368         overEvent.point = pt;
33369         overEvent.source = dd;
33370         overEvent.rawEvent = e;
33371         overEvent.dropNode = dropNode;
33372         overEvent.cancel = false;  
33373         var result = this.tree.fireEvent("nodedragover", overEvent);
33374         return overEvent.cancel === false && result !== false;
33375     },
33376     
33377     getDropPoint : function(e, n, dd){
33378         var tn = n.node;
33379         if(tn.isRoot){
33380             return tn.allowChildren !== false ? "append" : false; // always append for root
33381         }
33382         var dragEl = n.ddel;
33383         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33384         var y = Roo.lib.Event.getPageY(e);
33385         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33386         
33387         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33388         var noAppend = tn.allowChildren === false;
33389         if(this.appendOnly || tn.parentNode.allowChildren === false){
33390             return noAppend ? false : "append";
33391         }
33392         var noBelow = false;
33393         if(!this.allowParentInsert){
33394             noBelow = tn.hasChildNodes() && tn.isExpanded();
33395         }
33396         var q = (b - t) / (noAppend ? 2 : 3);
33397         if(y >= t && y < (t + q)){
33398             return "above";
33399         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33400             return "below";
33401         }else{
33402             return "append";
33403         }
33404     },
33405     
33406     onNodeEnter : function(n, dd, e, data){
33407         this.cancelExpand();
33408     },
33409     
33410     onNodeOver : function(n, dd, e, data){
33411         var pt = this.getDropPoint(e, n, dd);
33412         var node = n.node;
33413         
33414         // auto node expand check
33415         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33416             this.queueExpand(node);
33417         }else if(pt != "append"){
33418             this.cancelExpand();
33419         }
33420         
33421         // set the insert point style on the target node
33422         var returnCls = this.dropNotAllowed;
33423         if(this.isValidDropPoint(n, pt, dd, e, data)){
33424            if(pt){
33425                var el = n.ddel;
33426                var cls;
33427                if(pt == "above"){
33428                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33429                    cls = "x-tree-drag-insert-above";
33430                }else if(pt == "below"){
33431                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33432                    cls = "x-tree-drag-insert-below";
33433                }else{
33434                    returnCls = "x-tree-drop-ok-append";
33435                    cls = "x-tree-drag-append";
33436                }
33437                if(this.lastInsertClass != cls){
33438                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33439                    this.lastInsertClass = cls;
33440                }
33441            }
33442        }
33443        return returnCls;
33444     },
33445     
33446     onNodeOut : function(n, dd, e, data){
33447         this.cancelExpand();
33448         this.removeDropIndicators(n);
33449     },
33450     
33451     onNodeDrop : function(n, dd, e, data){
33452         var point = this.getDropPoint(e, n, dd);
33453         var targetNode = n.node;
33454         targetNode.ui.startDrop();
33455         if(!this.isValidDropPoint(n, point, dd, e, data)){
33456             targetNode.ui.endDrop();
33457             return false;
33458         }
33459         // first try to find the drop node
33460         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33461         var dropEvent = {
33462             tree : this.tree,
33463             target: targetNode,
33464             data: data,
33465             point: point,
33466             source: dd,
33467             rawEvent: e,
33468             dropNode: dropNode,
33469             cancel: !dropNode   
33470         };
33471         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33472         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33473             targetNode.ui.endDrop();
33474             return false;
33475         }
33476         // allow target changing
33477         targetNode = dropEvent.target;
33478         if(point == "append" && !targetNode.isExpanded()){
33479             targetNode.expand(false, null, function(){
33480                 this.completeDrop(dropEvent);
33481             }.createDelegate(this));
33482         }else{
33483             this.completeDrop(dropEvent);
33484         }
33485         return true;
33486     },
33487     
33488     completeDrop : function(de){
33489         var ns = de.dropNode, p = de.point, t = de.target;
33490         if(!(ns instanceof Array)){
33491             ns = [ns];
33492         }
33493         var n;
33494         for(var i = 0, len = ns.length; i < len; i++){
33495             n = ns[i];
33496             if(p == "above"){
33497                 t.parentNode.insertBefore(n, t);
33498             }else if(p == "below"){
33499                 t.parentNode.insertBefore(n, t.nextSibling);
33500             }else{
33501                 t.appendChild(n);
33502             }
33503         }
33504         n.ui.focus();
33505         if(this.tree.hlDrop){
33506             n.ui.highlight();
33507         }
33508         t.ui.endDrop();
33509         this.tree.fireEvent("nodedrop", de);
33510     },
33511     
33512     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33513         if(this.tree.hlDrop){
33514             dropNode.ui.focus();
33515             dropNode.ui.highlight();
33516         }
33517         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33518     },
33519     
33520     getTree : function(){
33521         return this.tree;
33522     },
33523     
33524     removeDropIndicators : function(n){
33525         if(n && n.ddel){
33526             var el = n.ddel;
33527             Roo.fly(el).removeClass([
33528                     "x-tree-drag-insert-above",
33529                     "x-tree-drag-insert-below",
33530                     "x-tree-drag-append"]);
33531             this.lastInsertClass = "_noclass";
33532         }
33533     },
33534     
33535     beforeDragDrop : function(target, e, id){
33536         this.cancelExpand();
33537         return true;
33538     },
33539     
33540     afterRepair : function(data){
33541         if(data && Roo.enableFx){
33542             data.node.ui.highlight();
33543         }
33544         this.hideProxy();
33545     }    
33546 });
33547
33548 }
33549 /*
33550  * Based on:
33551  * Ext JS Library 1.1.1
33552  * Copyright(c) 2006-2007, Ext JS, LLC.
33553  *
33554  * Originally Released Under LGPL - original licence link has changed is not relivant.
33555  *
33556  * Fork - LGPL
33557  * <script type="text/javascript">
33558  */
33559  
33560
33561 if(Roo.dd.DragZone){
33562 Roo.tree.TreeDragZone = function(tree, config){
33563     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33564     this.tree = tree;
33565 };
33566
33567 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33568     ddGroup : "TreeDD",
33569     
33570     onBeforeDrag : function(data, e){
33571         var n = data.node;
33572         return n && n.draggable && !n.disabled;
33573     },
33574     
33575     onInitDrag : function(e){
33576         var data = this.dragData;
33577         this.tree.getSelectionModel().select(data.node);
33578         this.proxy.update("");
33579         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33580         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33581     },
33582     
33583     getRepairXY : function(e, data){
33584         return data.node.ui.getDDRepairXY();
33585     },
33586     
33587     onEndDrag : function(data, e){
33588         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33589     },
33590     
33591     onValidDrop : function(dd, e, id){
33592         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33593         this.hideProxy();
33594     },
33595     
33596     beforeInvalidDrop : function(e, id){
33597         // this scrolls the original position back into view
33598         var sm = this.tree.getSelectionModel();
33599         sm.clearSelections();
33600         sm.select(this.dragData.node);
33601     }
33602 });
33603 }/*
33604  * Based on:
33605  * Ext JS Library 1.1.1
33606  * Copyright(c) 2006-2007, Ext JS, LLC.
33607  *
33608  * Originally Released Under LGPL - original licence link has changed is not relivant.
33609  *
33610  * Fork - LGPL
33611  * <script type="text/javascript">
33612  */
33613 /**
33614  * @class Roo.tree.TreeEditor
33615  * @extends Roo.Editor
33616  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33617  * as the editor field.
33618  * @constructor
33619  * @param {Object} config (used to be the tree panel.)
33620  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33621  * 
33622  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33623  * @cfg {Roo.form.TextField|Object} field The field configuration
33624  *
33625  * 
33626  */
33627 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33628     var tree = config;
33629     var field;
33630     if (oldconfig) { // old style..
33631         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33632     } else {
33633         // new style..
33634         tree = config.tree;
33635         config.field = config.field  || {};
33636         config.field.xtype = 'TextField';
33637         field = Roo.factory(config.field, Roo.form);
33638     }
33639     config = config || {};
33640     
33641     
33642     this.addEvents({
33643         /**
33644          * @event beforenodeedit
33645          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33646          * false from the handler of this event.
33647          * @param {Editor} this
33648          * @param {Roo.tree.Node} node 
33649          */
33650         "beforenodeedit" : true
33651     });
33652     
33653     //Roo.log(config);
33654     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33655
33656     this.tree = tree;
33657
33658     tree.on('beforeclick', this.beforeNodeClick, this);
33659     tree.getTreeEl().on('mousedown', this.hide, this);
33660     this.on('complete', this.updateNode, this);
33661     this.on('beforestartedit', this.fitToTree, this);
33662     this.on('startedit', this.bindScroll, this, {delay:10});
33663     this.on('specialkey', this.onSpecialKey, this);
33664 };
33665
33666 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33667     /**
33668      * @cfg {String} alignment
33669      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33670      */
33671     alignment: "l-l",
33672     // inherit
33673     autoSize: false,
33674     /**
33675      * @cfg {Boolean} hideEl
33676      * True to hide the bound element while the editor is displayed (defaults to false)
33677      */
33678     hideEl : false,
33679     /**
33680      * @cfg {String} cls
33681      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33682      */
33683     cls: "x-small-editor x-tree-editor",
33684     /**
33685      * @cfg {Boolean} shim
33686      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33687      */
33688     shim:false,
33689     // inherit
33690     shadow:"frame",
33691     /**
33692      * @cfg {Number} maxWidth
33693      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33694      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33695      * scroll and client offsets into account prior to each edit.
33696      */
33697     maxWidth: 250,
33698
33699     editDelay : 350,
33700
33701     // private
33702     fitToTree : function(ed, el){
33703         var td = this.tree.getTreeEl().dom, nd = el.dom;
33704         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33705             td.scrollLeft = nd.offsetLeft;
33706         }
33707         var w = Math.min(
33708                 this.maxWidth,
33709                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33710         this.setSize(w, '');
33711         
33712         return this.fireEvent('beforenodeedit', this, this.editNode);
33713         
33714     },
33715
33716     // private
33717     triggerEdit : function(node){
33718         this.completeEdit();
33719         this.editNode = node;
33720         this.startEdit(node.ui.textNode, node.text);
33721     },
33722
33723     // private
33724     bindScroll : function(){
33725         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33726     },
33727
33728     // private
33729     beforeNodeClick : function(node, e){
33730         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33731         this.lastClick = new Date();
33732         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33733             e.stopEvent();
33734             this.triggerEdit(node);
33735             return false;
33736         }
33737         return true;
33738     },
33739
33740     // private
33741     updateNode : function(ed, value){
33742         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33743         this.editNode.setText(value);
33744     },
33745
33746     // private
33747     onHide : function(){
33748         Roo.tree.TreeEditor.superclass.onHide.call(this);
33749         if(this.editNode){
33750             this.editNode.ui.focus();
33751         }
33752     },
33753
33754     // private
33755     onSpecialKey : function(field, e){
33756         var k = e.getKey();
33757         if(k == e.ESC){
33758             e.stopEvent();
33759             this.cancelEdit();
33760         }else if(k == e.ENTER && !e.hasModifier()){
33761             e.stopEvent();
33762             this.completeEdit();
33763         }
33764     }
33765 });//<Script type="text/javascript">
33766 /*
33767  * Based on:
33768  * Ext JS Library 1.1.1
33769  * Copyright(c) 2006-2007, Ext JS, LLC.
33770  *
33771  * Originally Released Under LGPL - original licence link has changed is not relivant.
33772  *
33773  * Fork - LGPL
33774  * <script type="text/javascript">
33775  */
33776  
33777 /**
33778  * Not documented??? - probably should be...
33779  */
33780
33781 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33782     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33783     
33784     renderElements : function(n, a, targetNode, bulkRender){
33785         //consel.log("renderElements?");
33786         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33787
33788         var t = n.getOwnerTree();
33789         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33790         
33791         var cols = t.columns;
33792         var bw = t.borderWidth;
33793         var c = cols[0];
33794         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33795          var cb = typeof a.checked == "boolean";
33796         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33797         var colcls = 'x-t-' + tid + '-c0';
33798         var buf = [
33799             '<li class="x-tree-node">',
33800             
33801                 
33802                 '<div class="x-tree-node-el ', a.cls,'">',
33803                     // extran...
33804                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33805                 
33806                 
33807                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33808                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33809                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33810                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33811                            (a.iconCls ? ' '+a.iconCls : ''),
33812                            '" unselectable="on" />',
33813                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33814                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33815                              
33816                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33817                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33818                             '<span unselectable="on" qtip="' + tx + '">',
33819                              tx,
33820                              '</span></a>' ,
33821                     '</div>',
33822                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33823                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33824                  ];
33825         for(var i = 1, len = cols.length; i < len; i++){
33826             c = cols[i];
33827             colcls = 'x-t-' + tid + '-c' +i;
33828             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33829             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33830                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33831                       "</div>");
33832          }
33833          
33834          buf.push(
33835             '</a>',
33836             '<div class="x-clear"></div></div>',
33837             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33838             "</li>");
33839         
33840         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33841             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33842                                 n.nextSibling.ui.getEl(), buf.join(""));
33843         }else{
33844             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33845         }
33846         var el = this.wrap.firstChild;
33847         this.elRow = el;
33848         this.elNode = el.firstChild;
33849         this.ranchor = el.childNodes[1];
33850         this.ctNode = this.wrap.childNodes[1];
33851         var cs = el.firstChild.childNodes;
33852         this.indentNode = cs[0];
33853         this.ecNode = cs[1];
33854         this.iconNode = cs[2];
33855         var index = 3;
33856         if(cb){
33857             this.checkbox = cs[3];
33858             index++;
33859         }
33860         this.anchor = cs[index];
33861         
33862         this.textNode = cs[index].firstChild;
33863         
33864         //el.on("click", this.onClick, this);
33865         //el.on("dblclick", this.onDblClick, this);
33866         
33867         
33868        // console.log(this);
33869     },
33870     initEvents : function(){
33871         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33872         
33873             
33874         var a = this.ranchor;
33875
33876         var el = Roo.get(a);
33877
33878         if(Roo.isOpera){ // opera render bug ignores the CSS
33879             el.setStyle("text-decoration", "none");
33880         }
33881
33882         el.on("click", this.onClick, this);
33883         el.on("dblclick", this.onDblClick, this);
33884         el.on("contextmenu", this.onContextMenu, this);
33885         
33886     },
33887     
33888     /*onSelectedChange : function(state){
33889         if(state){
33890             this.focus();
33891             this.addClass("x-tree-selected");
33892         }else{
33893             //this.blur();
33894             this.removeClass("x-tree-selected");
33895         }
33896     },*/
33897     addClass : function(cls){
33898         if(this.elRow){
33899             Roo.fly(this.elRow).addClass(cls);
33900         }
33901         
33902     },
33903     
33904     
33905     removeClass : function(cls){
33906         if(this.elRow){
33907             Roo.fly(this.elRow).removeClass(cls);
33908         }
33909     }
33910
33911     
33912     
33913 });//<Script type="text/javascript">
33914
33915 /*
33916  * Based on:
33917  * Ext JS Library 1.1.1
33918  * Copyright(c) 2006-2007, Ext JS, LLC.
33919  *
33920  * Originally Released Under LGPL - original licence link has changed is not relivant.
33921  *
33922  * Fork - LGPL
33923  * <script type="text/javascript">
33924  */
33925  
33926
33927 /**
33928  * @class Roo.tree.ColumnTree
33929  * @extends Roo.data.TreePanel
33930  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33931  * @cfg {int} borderWidth  compined right/left border allowance
33932  * @constructor
33933  * @param {String/HTMLElement/Element} el The container element
33934  * @param {Object} config
33935  */
33936 Roo.tree.ColumnTree =  function(el, config)
33937 {
33938    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33939    this.addEvents({
33940         /**
33941         * @event resize
33942         * Fire this event on a container when it resizes
33943         * @param {int} w Width
33944         * @param {int} h Height
33945         */
33946        "resize" : true
33947     });
33948     this.on('resize', this.onResize, this);
33949 };
33950
33951 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33952     //lines:false,
33953     
33954     
33955     borderWidth: Roo.isBorderBox ? 0 : 2, 
33956     headEls : false,
33957     
33958     render : function(){
33959         // add the header.....
33960        
33961         Roo.tree.ColumnTree.superclass.render.apply(this);
33962         
33963         this.el.addClass('x-column-tree');
33964         
33965         this.headers = this.el.createChild(
33966             {cls:'x-tree-headers'},this.innerCt.dom);
33967    
33968         var cols = this.columns, c;
33969         var totalWidth = 0;
33970         this.headEls = [];
33971         var  len = cols.length;
33972         for(var i = 0; i < len; i++){
33973              c = cols[i];
33974              totalWidth += c.width;
33975             this.headEls.push(this.headers.createChild({
33976                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33977                  cn: {
33978                      cls:'x-tree-hd-text',
33979                      html: c.header
33980                  },
33981                  style:'width:'+(c.width-this.borderWidth)+'px;'
33982              }));
33983         }
33984         this.headers.createChild({cls:'x-clear'});
33985         // prevent floats from wrapping when clipped
33986         this.headers.setWidth(totalWidth);
33987         //this.innerCt.setWidth(totalWidth);
33988         this.innerCt.setStyle({ overflow: 'auto' });
33989         this.onResize(this.width, this.height);
33990              
33991         
33992     },
33993     onResize : function(w,h)
33994     {
33995         this.height = h;
33996         this.width = w;
33997         // resize cols..
33998         this.innerCt.setWidth(this.width);
33999         this.innerCt.setHeight(this.height-20);
34000         
34001         // headers...
34002         var cols = this.columns, c;
34003         var totalWidth = 0;
34004         var expEl = false;
34005         var len = cols.length;
34006         for(var i = 0; i < len; i++){
34007             c = cols[i];
34008             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34009                 // it's the expander..
34010                 expEl  = this.headEls[i];
34011                 continue;
34012             }
34013             totalWidth += c.width;
34014             
34015         }
34016         if (expEl) {
34017             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34018         }
34019         this.headers.setWidth(w-20);
34020
34021         
34022         
34023         
34024     }
34025 });
34026 /*
34027  * Based on:
34028  * Ext JS Library 1.1.1
34029  * Copyright(c) 2006-2007, Ext JS, LLC.
34030  *
34031  * Originally Released Under LGPL - original licence link has changed is not relivant.
34032  *
34033  * Fork - LGPL
34034  * <script type="text/javascript">
34035  */
34036  
34037 /**
34038  * @class Roo.menu.Menu
34039  * @extends Roo.util.Observable
34040  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34041  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34042  * @constructor
34043  * Creates a new Menu
34044  * @param {Object} config Configuration options
34045  */
34046 Roo.menu.Menu = function(config){
34047     Roo.apply(this, config);
34048     this.id = this.id || Roo.id();
34049     this.addEvents({
34050         /**
34051          * @event beforeshow
34052          * Fires before this menu is displayed
34053          * @param {Roo.menu.Menu} this
34054          */
34055         beforeshow : true,
34056         /**
34057          * @event beforehide
34058          * Fires before this menu is hidden
34059          * @param {Roo.menu.Menu} this
34060          */
34061         beforehide : true,
34062         /**
34063          * @event show
34064          * Fires after this menu is displayed
34065          * @param {Roo.menu.Menu} this
34066          */
34067         show : true,
34068         /**
34069          * @event hide
34070          * Fires after this menu is hidden
34071          * @param {Roo.menu.Menu} this
34072          */
34073         hide : true,
34074         /**
34075          * @event click
34076          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34077          * @param {Roo.menu.Menu} this
34078          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34079          * @param {Roo.EventObject} e
34080          */
34081         click : true,
34082         /**
34083          * @event mouseover
34084          * Fires when the mouse is hovering over this menu
34085          * @param {Roo.menu.Menu} this
34086          * @param {Roo.EventObject} e
34087          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34088          */
34089         mouseover : true,
34090         /**
34091          * @event mouseout
34092          * Fires when the mouse exits this menu
34093          * @param {Roo.menu.Menu} this
34094          * @param {Roo.EventObject} e
34095          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34096          */
34097         mouseout : true,
34098         /**
34099          * @event itemclick
34100          * Fires when a menu item contained in this menu is clicked
34101          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34102          * @param {Roo.EventObject} e
34103          */
34104         itemclick: true
34105     });
34106     if (this.registerMenu) {
34107         Roo.menu.MenuMgr.register(this);
34108     }
34109     
34110     var mis = this.items;
34111     this.items = new Roo.util.MixedCollection();
34112     if(mis){
34113         this.add.apply(this, mis);
34114     }
34115 };
34116
34117 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34118     /**
34119      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34120      */
34121     minWidth : 120,
34122     /**
34123      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34124      * for bottom-right shadow (defaults to "sides")
34125      */
34126     shadow : "sides",
34127     /**
34128      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34129      * this menu (defaults to "tl-tr?")
34130      */
34131     subMenuAlign : "tl-tr?",
34132     /**
34133      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34134      * relative to its element of origin (defaults to "tl-bl?")
34135      */
34136     defaultAlign : "tl-bl?",
34137     /**
34138      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34139      */
34140     allowOtherMenus : false,
34141     /**
34142      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34143      */
34144     registerMenu : true,
34145
34146     hidden:true,
34147
34148     // private
34149     render : function(){
34150         if(this.el){
34151             return;
34152         }
34153         var el = this.el = new Roo.Layer({
34154             cls: "x-menu",
34155             shadow:this.shadow,
34156             constrain: false,
34157             parentEl: this.parentEl || document.body,
34158             zindex:15000
34159         });
34160
34161         this.keyNav = new Roo.menu.MenuNav(this);
34162
34163         if(this.plain){
34164             el.addClass("x-menu-plain");
34165         }
34166         if(this.cls){
34167             el.addClass(this.cls);
34168         }
34169         // generic focus element
34170         this.focusEl = el.createChild({
34171             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34172         });
34173         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34174         ul.on("click", this.onClick, this);
34175         ul.on("mouseover", this.onMouseOver, this);
34176         ul.on("mouseout", this.onMouseOut, this);
34177         this.items.each(function(item){
34178             var li = document.createElement("li");
34179             li.className = "x-menu-list-item";
34180             ul.dom.appendChild(li);
34181             item.render(li, this);
34182         }, this);
34183         this.ul = ul;
34184         this.autoWidth();
34185     },
34186
34187     // private
34188     autoWidth : function(){
34189         var el = this.el, ul = this.ul;
34190         if(!el){
34191             return;
34192         }
34193         var w = this.width;
34194         if(w){
34195             el.setWidth(w);
34196         }else if(Roo.isIE){
34197             el.setWidth(this.minWidth);
34198             var t = el.dom.offsetWidth; // force recalc
34199             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34200         }
34201     },
34202
34203     // private
34204     delayAutoWidth : function(){
34205         if(this.rendered){
34206             if(!this.awTask){
34207                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34208             }
34209             this.awTask.delay(20);
34210         }
34211     },
34212
34213     // private
34214     findTargetItem : function(e){
34215         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34216         if(t && t.menuItemId){
34217             return this.items.get(t.menuItemId);
34218         }
34219     },
34220
34221     // private
34222     onClick : function(e){
34223         var t;
34224         if(t = this.findTargetItem(e)){
34225             t.onClick(e);
34226             this.fireEvent("click", this, t, e);
34227         }
34228     },
34229
34230     // private
34231     setActiveItem : function(item, autoExpand){
34232         if(item != this.activeItem){
34233             if(this.activeItem){
34234                 this.activeItem.deactivate();
34235             }
34236             this.activeItem = item;
34237             item.activate(autoExpand);
34238         }else if(autoExpand){
34239             item.expandMenu();
34240         }
34241     },
34242
34243     // private
34244     tryActivate : function(start, step){
34245         var items = this.items;
34246         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34247             var item = items.get(i);
34248             if(!item.disabled && item.canActivate){
34249                 this.setActiveItem(item, false);
34250                 return item;
34251             }
34252         }
34253         return false;
34254     },
34255
34256     // private
34257     onMouseOver : function(e){
34258         var t;
34259         if(t = this.findTargetItem(e)){
34260             if(t.canActivate && !t.disabled){
34261                 this.setActiveItem(t, true);
34262             }
34263         }
34264         this.fireEvent("mouseover", this, e, t);
34265     },
34266
34267     // private
34268     onMouseOut : function(e){
34269         var t;
34270         if(t = this.findTargetItem(e)){
34271             if(t == this.activeItem && t.shouldDeactivate(e)){
34272                 this.activeItem.deactivate();
34273                 delete this.activeItem;
34274             }
34275         }
34276         this.fireEvent("mouseout", this, e, t);
34277     },
34278
34279     /**
34280      * Read-only.  Returns true if the menu is currently displayed, else false.
34281      * @type Boolean
34282      */
34283     isVisible : function(){
34284         return this.el && !this.hidden;
34285     },
34286
34287     /**
34288      * Displays this menu relative to another element
34289      * @param {String/HTMLElement/Roo.Element} element The element to align to
34290      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34291      * the element (defaults to this.defaultAlign)
34292      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34293      */
34294     show : function(el, pos, parentMenu){
34295         this.parentMenu = parentMenu;
34296         if(!this.el){
34297             this.render();
34298         }
34299         this.fireEvent("beforeshow", this);
34300         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34301     },
34302
34303     /**
34304      * Displays this menu at a specific xy position
34305      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34306      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34307      */
34308     showAt : function(xy, parentMenu, /* private: */_e){
34309         this.parentMenu = parentMenu;
34310         if(!this.el){
34311             this.render();
34312         }
34313         if(_e !== false){
34314             this.fireEvent("beforeshow", this);
34315             xy = this.el.adjustForConstraints(xy);
34316         }
34317         this.el.setXY(xy);
34318         this.el.show();
34319         this.hidden = false;
34320         this.focus();
34321         this.fireEvent("show", this);
34322     },
34323
34324     focus : function(){
34325         if(!this.hidden){
34326             this.doFocus.defer(50, this);
34327         }
34328     },
34329
34330     doFocus : function(){
34331         if(!this.hidden){
34332             this.focusEl.focus();
34333         }
34334     },
34335
34336     /**
34337      * Hides this menu and optionally all parent menus
34338      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34339      */
34340     hide : function(deep){
34341         if(this.el && this.isVisible()){
34342             this.fireEvent("beforehide", this);
34343             if(this.activeItem){
34344                 this.activeItem.deactivate();
34345                 this.activeItem = null;
34346             }
34347             this.el.hide();
34348             this.hidden = true;
34349             this.fireEvent("hide", this);
34350         }
34351         if(deep === true && this.parentMenu){
34352             this.parentMenu.hide(true);
34353         }
34354     },
34355
34356     /**
34357      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34358      * Any of the following are valid:
34359      * <ul>
34360      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34361      * <li>An HTMLElement object which will be converted to a menu item</li>
34362      * <li>A menu item config object that will be created as a new menu item</li>
34363      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34364      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34365      * </ul>
34366      * Usage:
34367      * <pre><code>
34368 // Create the menu
34369 var menu = new Roo.menu.Menu();
34370
34371 // Create a menu item to add by reference
34372 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34373
34374 // Add a bunch of items at once using different methods.
34375 // Only the last item added will be returned.
34376 var item = menu.add(
34377     menuItem,                // add existing item by ref
34378     'Dynamic Item',          // new TextItem
34379     '-',                     // new separator
34380     { text: 'Config Item' }  // new item by config
34381 );
34382 </code></pre>
34383      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34384      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34385      */
34386     add : function(){
34387         var a = arguments, l = a.length, item;
34388         for(var i = 0; i < l; i++){
34389             var el = a[i];
34390             if ((typeof(el) == "object") && el.xtype && el.xns) {
34391                 el = Roo.factory(el, Roo.menu);
34392             }
34393             
34394             if(el.render){ // some kind of Item
34395                 item = this.addItem(el);
34396             }else if(typeof el == "string"){ // string
34397                 if(el == "separator" || el == "-"){
34398                     item = this.addSeparator();
34399                 }else{
34400                     item = this.addText(el);
34401                 }
34402             }else if(el.tagName || el.el){ // element
34403                 item = this.addElement(el);
34404             }else if(typeof el == "object"){ // must be menu item config?
34405                 item = this.addMenuItem(el);
34406             }
34407         }
34408         return item;
34409     },
34410
34411     /**
34412      * Returns this menu's underlying {@link Roo.Element} object
34413      * @return {Roo.Element} The element
34414      */
34415     getEl : function(){
34416         if(!this.el){
34417             this.render();
34418         }
34419         return this.el;
34420     },
34421
34422     /**
34423      * Adds a separator bar to the menu
34424      * @return {Roo.menu.Item} The menu item that was added
34425      */
34426     addSeparator : function(){
34427         return this.addItem(new Roo.menu.Separator());
34428     },
34429
34430     /**
34431      * Adds an {@link Roo.Element} object to the menu
34432      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34433      * @return {Roo.menu.Item} The menu item that was added
34434      */
34435     addElement : function(el){
34436         return this.addItem(new Roo.menu.BaseItem(el));
34437     },
34438
34439     /**
34440      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34441      * @param {Roo.menu.Item} item The menu item to add
34442      * @return {Roo.menu.Item} The menu item that was added
34443      */
34444     addItem : function(item){
34445         this.items.add(item);
34446         if(this.ul){
34447             var li = document.createElement("li");
34448             li.className = "x-menu-list-item";
34449             this.ul.dom.appendChild(li);
34450             item.render(li, this);
34451             this.delayAutoWidth();
34452         }
34453         return item;
34454     },
34455
34456     /**
34457      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34458      * @param {Object} config A MenuItem config object
34459      * @return {Roo.menu.Item} The menu item that was added
34460      */
34461     addMenuItem : function(config){
34462         if(!(config instanceof Roo.menu.Item)){
34463             if(typeof config.checked == "boolean"){ // must be check menu item config?
34464                 config = new Roo.menu.CheckItem(config);
34465             }else{
34466                 config = new Roo.menu.Item(config);
34467             }
34468         }
34469         return this.addItem(config);
34470     },
34471
34472     /**
34473      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34474      * @param {String} text The text to display in the menu item
34475      * @return {Roo.menu.Item} The menu item that was added
34476      */
34477     addText : function(text){
34478         return this.addItem(new Roo.menu.TextItem({ text : text }));
34479     },
34480
34481     /**
34482      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34483      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34484      * @param {Roo.menu.Item} item The menu item to add
34485      * @return {Roo.menu.Item} The menu item that was added
34486      */
34487     insert : function(index, item){
34488         this.items.insert(index, item);
34489         if(this.ul){
34490             var li = document.createElement("li");
34491             li.className = "x-menu-list-item";
34492             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34493             item.render(li, this);
34494             this.delayAutoWidth();
34495         }
34496         return item;
34497     },
34498
34499     /**
34500      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34501      * @param {Roo.menu.Item} item The menu item to remove
34502      */
34503     remove : function(item){
34504         this.items.removeKey(item.id);
34505         item.destroy();
34506     },
34507
34508     /**
34509      * Removes and destroys all items in the menu
34510      */
34511     removeAll : function(){
34512         var f;
34513         while(f = this.items.first()){
34514             this.remove(f);
34515         }
34516     }
34517 });
34518
34519 // MenuNav is a private utility class used internally by the Menu
34520 Roo.menu.MenuNav = function(menu){
34521     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34522     this.scope = this.menu = menu;
34523 };
34524
34525 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34526     doRelay : function(e, h){
34527         var k = e.getKey();
34528         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34529             this.menu.tryActivate(0, 1);
34530             return false;
34531         }
34532         return h.call(this.scope || this, e, this.menu);
34533     },
34534
34535     up : function(e, m){
34536         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34537             m.tryActivate(m.items.length-1, -1);
34538         }
34539     },
34540
34541     down : function(e, m){
34542         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34543             m.tryActivate(0, 1);
34544         }
34545     },
34546
34547     right : function(e, m){
34548         if(m.activeItem){
34549             m.activeItem.expandMenu(true);
34550         }
34551     },
34552
34553     left : function(e, m){
34554         m.hide();
34555         if(m.parentMenu && m.parentMenu.activeItem){
34556             m.parentMenu.activeItem.activate();
34557         }
34558     },
34559
34560     enter : function(e, m){
34561         if(m.activeItem){
34562             e.stopPropagation();
34563             m.activeItem.onClick(e);
34564             m.fireEvent("click", this, m.activeItem);
34565             return true;
34566         }
34567     }
34568 });/*
34569  * Based on:
34570  * Ext JS Library 1.1.1
34571  * Copyright(c) 2006-2007, Ext JS, LLC.
34572  *
34573  * Originally Released Under LGPL - original licence link has changed is not relivant.
34574  *
34575  * Fork - LGPL
34576  * <script type="text/javascript">
34577  */
34578  
34579 /**
34580  * @class Roo.menu.MenuMgr
34581  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34582  * @singleton
34583  */
34584 Roo.menu.MenuMgr = function(){
34585    var menus, active, groups = {}, attached = false, lastShow = new Date();
34586
34587    // private - called when first menu is created
34588    function init(){
34589        menus = {};
34590        active = new Roo.util.MixedCollection();
34591        Roo.get(document).addKeyListener(27, function(){
34592            if(active.length > 0){
34593                hideAll();
34594            }
34595        });
34596    }
34597
34598    // private
34599    function hideAll(){
34600        if(active && active.length > 0){
34601            var c = active.clone();
34602            c.each(function(m){
34603                m.hide();
34604            });
34605        }
34606    }
34607
34608    // private
34609    function onHide(m){
34610        active.remove(m);
34611        if(active.length < 1){
34612            Roo.get(document).un("mousedown", onMouseDown);
34613            attached = false;
34614        }
34615    }
34616
34617    // private
34618    function onShow(m){
34619        var last = active.last();
34620        lastShow = new Date();
34621        active.add(m);
34622        if(!attached){
34623            Roo.get(document).on("mousedown", onMouseDown);
34624            attached = true;
34625        }
34626        if(m.parentMenu){
34627           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34628           m.parentMenu.activeChild = m;
34629        }else if(last && last.isVisible()){
34630           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34631        }
34632    }
34633
34634    // private
34635    function onBeforeHide(m){
34636        if(m.activeChild){
34637            m.activeChild.hide();
34638        }
34639        if(m.autoHideTimer){
34640            clearTimeout(m.autoHideTimer);
34641            delete m.autoHideTimer;
34642        }
34643    }
34644
34645    // private
34646    function onBeforeShow(m){
34647        var pm = m.parentMenu;
34648        if(!pm && !m.allowOtherMenus){
34649            hideAll();
34650        }else if(pm && pm.activeChild && active != m){
34651            pm.activeChild.hide();
34652        }
34653    }
34654
34655    // private
34656    function onMouseDown(e){
34657        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34658            hideAll();
34659        }
34660    }
34661
34662    // private
34663    function onBeforeCheck(mi, state){
34664        if(state){
34665            var g = groups[mi.group];
34666            for(var i = 0, l = g.length; i < l; i++){
34667                if(g[i] != mi){
34668                    g[i].setChecked(false);
34669                }
34670            }
34671        }
34672    }
34673
34674    return {
34675
34676        /**
34677         * Hides all menus that are currently visible
34678         */
34679        hideAll : function(){
34680             hideAll();  
34681        },
34682
34683        // private
34684        register : function(menu){
34685            if(!menus){
34686                init();
34687            }
34688            menus[menu.id] = menu;
34689            menu.on("beforehide", onBeforeHide);
34690            menu.on("hide", onHide);
34691            menu.on("beforeshow", onBeforeShow);
34692            menu.on("show", onShow);
34693            var g = menu.group;
34694            if(g && menu.events["checkchange"]){
34695                if(!groups[g]){
34696                    groups[g] = [];
34697                }
34698                groups[g].push(menu);
34699                menu.on("checkchange", onCheck);
34700            }
34701        },
34702
34703         /**
34704          * Returns a {@link Roo.menu.Menu} object
34705          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34706          * be used to generate and return a new Menu instance.
34707          */
34708        get : function(menu){
34709            if(typeof menu == "string"){ // menu id
34710                return menus[menu];
34711            }else if(menu.events){  // menu instance
34712                return menu;
34713            }else if(typeof menu.length == 'number'){ // array of menu items?
34714                return new Roo.menu.Menu({items:menu});
34715            }else{ // otherwise, must be a config
34716                return new Roo.menu.Menu(menu);
34717            }
34718        },
34719
34720        // private
34721        unregister : function(menu){
34722            delete menus[menu.id];
34723            menu.un("beforehide", onBeforeHide);
34724            menu.un("hide", onHide);
34725            menu.un("beforeshow", onBeforeShow);
34726            menu.un("show", onShow);
34727            var g = menu.group;
34728            if(g && menu.events["checkchange"]){
34729                groups[g].remove(menu);
34730                menu.un("checkchange", onCheck);
34731            }
34732        },
34733
34734        // private
34735        registerCheckable : function(menuItem){
34736            var g = menuItem.group;
34737            if(g){
34738                if(!groups[g]){
34739                    groups[g] = [];
34740                }
34741                groups[g].push(menuItem);
34742                menuItem.on("beforecheckchange", onBeforeCheck);
34743            }
34744        },
34745
34746        // private
34747        unregisterCheckable : function(menuItem){
34748            var g = menuItem.group;
34749            if(g){
34750                groups[g].remove(menuItem);
34751                menuItem.un("beforecheckchange", onBeforeCheck);
34752            }
34753        }
34754    };
34755 }();/*
34756  * Based on:
34757  * Ext JS Library 1.1.1
34758  * Copyright(c) 2006-2007, Ext JS, LLC.
34759  *
34760  * Originally Released Under LGPL - original licence link has changed is not relivant.
34761  *
34762  * Fork - LGPL
34763  * <script type="text/javascript">
34764  */
34765  
34766
34767 /**
34768  * @class Roo.menu.BaseItem
34769  * @extends Roo.Component
34770  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34771  * management and base configuration options shared by all menu components.
34772  * @constructor
34773  * Creates a new BaseItem
34774  * @param {Object} config Configuration options
34775  */
34776 Roo.menu.BaseItem = function(config){
34777     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34778
34779     this.addEvents({
34780         /**
34781          * @event click
34782          * Fires when this item is clicked
34783          * @param {Roo.menu.BaseItem} this
34784          * @param {Roo.EventObject} e
34785          */
34786         click: true,
34787         /**
34788          * @event activate
34789          * Fires when this item is activated
34790          * @param {Roo.menu.BaseItem} this
34791          */
34792         activate : true,
34793         /**
34794          * @event deactivate
34795          * Fires when this item is deactivated
34796          * @param {Roo.menu.BaseItem} this
34797          */
34798         deactivate : true
34799     });
34800
34801     if(this.handler){
34802         this.on("click", this.handler, this.scope, true);
34803     }
34804 };
34805
34806 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34807     /**
34808      * @cfg {Function} handler
34809      * A function that will handle the click event of this menu item (defaults to undefined)
34810      */
34811     /**
34812      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34813      */
34814     canActivate : false,
34815     /**
34816      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34817      */
34818     activeClass : "x-menu-item-active",
34819     /**
34820      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34821      */
34822     hideOnClick : true,
34823     /**
34824      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34825      */
34826     hideDelay : 100,
34827
34828     // private
34829     ctype: "Roo.menu.BaseItem",
34830
34831     // private
34832     actionMode : "container",
34833
34834     // private
34835     render : function(container, parentMenu){
34836         this.parentMenu = parentMenu;
34837         Roo.menu.BaseItem.superclass.render.call(this, container);
34838         this.container.menuItemId = this.id;
34839     },
34840
34841     // private
34842     onRender : function(container, position){
34843         this.el = Roo.get(this.el);
34844         container.dom.appendChild(this.el.dom);
34845     },
34846
34847     // private
34848     onClick : function(e){
34849         if(!this.disabled && this.fireEvent("click", this, e) !== false
34850                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34851             this.handleClick(e);
34852         }else{
34853             e.stopEvent();
34854         }
34855     },
34856
34857     // private
34858     activate : function(){
34859         if(this.disabled){
34860             return false;
34861         }
34862         var li = this.container;
34863         li.addClass(this.activeClass);
34864         this.region = li.getRegion().adjust(2, 2, -2, -2);
34865         this.fireEvent("activate", this);
34866         return true;
34867     },
34868
34869     // private
34870     deactivate : function(){
34871         this.container.removeClass(this.activeClass);
34872         this.fireEvent("deactivate", this);
34873     },
34874
34875     // private
34876     shouldDeactivate : function(e){
34877         return !this.region || !this.region.contains(e.getPoint());
34878     },
34879
34880     // private
34881     handleClick : function(e){
34882         if(this.hideOnClick){
34883             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34884         }
34885     },
34886
34887     // private
34888     expandMenu : function(autoActivate){
34889         // do nothing
34890     },
34891
34892     // private
34893     hideMenu : function(){
34894         // do nothing
34895     }
34896 });/*
34897  * Based on:
34898  * Ext JS Library 1.1.1
34899  * Copyright(c) 2006-2007, Ext JS, LLC.
34900  *
34901  * Originally Released Under LGPL - original licence link has changed is not relivant.
34902  *
34903  * Fork - LGPL
34904  * <script type="text/javascript">
34905  */
34906  
34907 /**
34908  * @class Roo.menu.Adapter
34909  * @extends Roo.menu.BaseItem
34910  * 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.
34911  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34912  * @constructor
34913  * Creates a new Adapter
34914  * @param {Object} config Configuration options
34915  */
34916 Roo.menu.Adapter = function(component, config){
34917     Roo.menu.Adapter.superclass.constructor.call(this, config);
34918     this.component = component;
34919 };
34920 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34921     // private
34922     canActivate : true,
34923
34924     // private
34925     onRender : function(container, position){
34926         this.component.render(container);
34927         this.el = this.component.getEl();
34928     },
34929
34930     // private
34931     activate : function(){
34932         if(this.disabled){
34933             return false;
34934         }
34935         this.component.focus();
34936         this.fireEvent("activate", this);
34937         return true;
34938     },
34939
34940     // private
34941     deactivate : function(){
34942         this.fireEvent("deactivate", this);
34943     },
34944
34945     // private
34946     disable : function(){
34947         this.component.disable();
34948         Roo.menu.Adapter.superclass.disable.call(this);
34949     },
34950
34951     // private
34952     enable : function(){
34953         this.component.enable();
34954         Roo.menu.Adapter.superclass.enable.call(this);
34955     }
34956 });/*
34957  * Based on:
34958  * Ext JS Library 1.1.1
34959  * Copyright(c) 2006-2007, Ext JS, LLC.
34960  *
34961  * Originally Released Under LGPL - original licence link has changed is not relivant.
34962  *
34963  * Fork - LGPL
34964  * <script type="text/javascript">
34965  */
34966
34967 /**
34968  * @class Roo.menu.TextItem
34969  * @extends Roo.menu.BaseItem
34970  * Adds a static text string to a menu, usually used as either a heading or group separator.
34971  * Note: old style constructor with text is still supported.
34972  * 
34973  * @constructor
34974  * Creates a new TextItem
34975  * @param {Object} cfg Configuration
34976  */
34977 Roo.menu.TextItem = function(cfg){
34978     if (typeof(cfg) == 'string') {
34979         this.text = cfg;
34980     } else {
34981         Roo.apply(this,cfg);
34982     }
34983     
34984     Roo.menu.TextItem.superclass.constructor.call(this);
34985 };
34986
34987 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34988     /**
34989      * @cfg {Boolean} text Text to show on item.
34990      */
34991     text : '',
34992     
34993     /**
34994      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34995      */
34996     hideOnClick : false,
34997     /**
34998      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34999      */
35000     itemCls : "x-menu-text",
35001
35002     // private
35003     onRender : function(){
35004         var s = document.createElement("span");
35005         s.className = this.itemCls;
35006         s.innerHTML = this.text;
35007         this.el = s;
35008         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35009     }
35010 });/*
35011  * Based on:
35012  * Ext JS Library 1.1.1
35013  * Copyright(c) 2006-2007, Ext JS, LLC.
35014  *
35015  * Originally Released Under LGPL - original licence link has changed is not relivant.
35016  *
35017  * Fork - LGPL
35018  * <script type="text/javascript">
35019  */
35020
35021 /**
35022  * @class Roo.menu.Separator
35023  * @extends Roo.menu.BaseItem
35024  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35025  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35026  * @constructor
35027  * @param {Object} config Configuration options
35028  */
35029 Roo.menu.Separator = function(config){
35030     Roo.menu.Separator.superclass.constructor.call(this, config);
35031 };
35032
35033 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35034     /**
35035      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35036      */
35037     itemCls : "x-menu-sep",
35038     /**
35039      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35040      */
35041     hideOnClick : false,
35042
35043     // private
35044     onRender : function(li){
35045         var s = document.createElement("span");
35046         s.className = this.itemCls;
35047         s.innerHTML = "&#160;";
35048         this.el = s;
35049         li.addClass("x-menu-sep-li");
35050         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35051     }
35052 });/*
35053  * Based on:
35054  * Ext JS Library 1.1.1
35055  * Copyright(c) 2006-2007, Ext JS, LLC.
35056  *
35057  * Originally Released Under LGPL - original licence link has changed is not relivant.
35058  *
35059  * Fork - LGPL
35060  * <script type="text/javascript">
35061  */
35062 /**
35063  * @class Roo.menu.Item
35064  * @extends Roo.menu.BaseItem
35065  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35066  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35067  * activation and click handling.
35068  * @constructor
35069  * Creates a new Item
35070  * @param {Object} config Configuration options
35071  */
35072 Roo.menu.Item = function(config){
35073     Roo.menu.Item.superclass.constructor.call(this, config);
35074     if(this.menu){
35075         this.menu = Roo.menu.MenuMgr.get(this.menu);
35076     }
35077 };
35078 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35079     
35080     /**
35081      * @cfg {String} text
35082      * The text to show on the menu item.
35083      */
35084     text: '',
35085      /**
35086      * @cfg {String} HTML to render in menu
35087      * The text to show on the menu item (HTML version).
35088      */
35089     html: '',
35090     /**
35091      * @cfg {String} icon
35092      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35093      */
35094     icon: undefined,
35095     /**
35096      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35097      */
35098     itemCls : "x-menu-item",
35099     /**
35100      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35101      */
35102     canActivate : true,
35103     /**
35104      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35105      */
35106     showDelay: 200,
35107     // doc'd in BaseItem
35108     hideDelay: 200,
35109
35110     // private
35111     ctype: "Roo.menu.Item",
35112     
35113     // private
35114     onRender : function(container, position){
35115         var el = document.createElement("a");
35116         el.hideFocus = true;
35117         el.unselectable = "on";
35118         el.href = this.href || "#";
35119         if(this.hrefTarget){
35120             el.target = this.hrefTarget;
35121         }
35122         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35123         
35124         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35125         
35126         el.innerHTML = String.format(
35127                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35128                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35129         this.el = el;
35130         Roo.menu.Item.superclass.onRender.call(this, container, position);
35131     },
35132
35133     /**
35134      * Sets the text to display in this menu item
35135      * @param {String} text The text to display
35136      * @param {Boolean} isHTML true to indicate text is pure html.
35137      */
35138     setText : function(text, isHTML){
35139         if (isHTML) {
35140             this.html = text;
35141         } else {
35142             this.text = text;
35143             this.html = '';
35144         }
35145         if(this.rendered){
35146             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35147      
35148             this.el.update(String.format(
35149                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35150                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35151             this.parentMenu.autoWidth();
35152         }
35153     },
35154
35155     // private
35156     handleClick : function(e){
35157         if(!this.href){ // if no link defined, stop the event automatically
35158             e.stopEvent();
35159         }
35160         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35161     },
35162
35163     // private
35164     activate : function(autoExpand){
35165         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35166             this.focus();
35167             if(autoExpand){
35168                 this.expandMenu();
35169             }
35170         }
35171         return true;
35172     },
35173
35174     // private
35175     shouldDeactivate : function(e){
35176         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35177             if(this.menu && this.menu.isVisible()){
35178                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35179             }
35180             return true;
35181         }
35182         return false;
35183     },
35184
35185     // private
35186     deactivate : function(){
35187         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35188         this.hideMenu();
35189     },
35190
35191     // private
35192     expandMenu : function(autoActivate){
35193         if(!this.disabled && this.menu){
35194             clearTimeout(this.hideTimer);
35195             delete this.hideTimer;
35196             if(!this.menu.isVisible() && !this.showTimer){
35197                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35198             }else if (this.menu.isVisible() && autoActivate){
35199                 this.menu.tryActivate(0, 1);
35200             }
35201         }
35202     },
35203
35204     // private
35205     deferExpand : function(autoActivate){
35206         delete this.showTimer;
35207         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35208         if(autoActivate){
35209             this.menu.tryActivate(0, 1);
35210         }
35211     },
35212
35213     // private
35214     hideMenu : function(){
35215         clearTimeout(this.showTimer);
35216         delete this.showTimer;
35217         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35218             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35219         }
35220     },
35221
35222     // private
35223     deferHide : function(){
35224         delete this.hideTimer;
35225         this.menu.hide();
35226     }
35227 });/*
35228  * Based on:
35229  * Ext JS Library 1.1.1
35230  * Copyright(c) 2006-2007, Ext JS, LLC.
35231  *
35232  * Originally Released Under LGPL - original licence link has changed is not relivant.
35233  *
35234  * Fork - LGPL
35235  * <script type="text/javascript">
35236  */
35237  
35238 /**
35239  * @class Roo.menu.CheckItem
35240  * @extends Roo.menu.Item
35241  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35242  * @constructor
35243  * Creates a new CheckItem
35244  * @param {Object} config Configuration options
35245  */
35246 Roo.menu.CheckItem = function(config){
35247     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35248     this.addEvents({
35249         /**
35250          * @event beforecheckchange
35251          * Fires before the checked value is set, providing an opportunity to cancel if needed
35252          * @param {Roo.menu.CheckItem} this
35253          * @param {Boolean} checked The new checked value that will be set
35254          */
35255         "beforecheckchange" : true,
35256         /**
35257          * @event checkchange
35258          * Fires after the checked value has been set
35259          * @param {Roo.menu.CheckItem} this
35260          * @param {Boolean} checked The checked value that was set
35261          */
35262         "checkchange" : true
35263     });
35264     if(this.checkHandler){
35265         this.on('checkchange', this.checkHandler, this.scope);
35266     }
35267 };
35268 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35269     /**
35270      * @cfg {String} group
35271      * All check items with the same group name will automatically be grouped into a single-select
35272      * radio button group (defaults to '')
35273      */
35274     /**
35275      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35276      */
35277     itemCls : "x-menu-item x-menu-check-item",
35278     /**
35279      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35280      */
35281     groupClass : "x-menu-group-item",
35282
35283     /**
35284      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35285      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35286      * initialized with checked = true will be rendered as checked.
35287      */
35288     checked: false,
35289
35290     // private
35291     ctype: "Roo.menu.CheckItem",
35292
35293     // private
35294     onRender : function(c){
35295         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35296         if(this.group){
35297             this.el.addClass(this.groupClass);
35298         }
35299         Roo.menu.MenuMgr.registerCheckable(this);
35300         if(this.checked){
35301             this.checked = false;
35302             this.setChecked(true, true);
35303         }
35304     },
35305
35306     // private
35307     destroy : function(){
35308         if(this.rendered){
35309             Roo.menu.MenuMgr.unregisterCheckable(this);
35310         }
35311         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35312     },
35313
35314     /**
35315      * Set the checked state of this item
35316      * @param {Boolean} checked The new checked value
35317      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35318      */
35319     setChecked : function(state, suppressEvent){
35320         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35321             if(this.container){
35322                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35323             }
35324             this.checked = state;
35325             if(suppressEvent !== true){
35326                 this.fireEvent("checkchange", this, state);
35327             }
35328         }
35329     },
35330
35331     // private
35332     handleClick : function(e){
35333        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35334            this.setChecked(!this.checked);
35335        }
35336        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35337     }
35338 });/*
35339  * Based on:
35340  * Ext JS Library 1.1.1
35341  * Copyright(c) 2006-2007, Ext JS, LLC.
35342  *
35343  * Originally Released Under LGPL - original licence link has changed is not relivant.
35344  *
35345  * Fork - LGPL
35346  * <script type="text/javascript">
35347  */
35348  
35349 /**
35350  * @class Roo.menu.DateItem
35351  * @extends Roo.menu.Adapter
35352  * A menu item that wraps the {@link Roo.DatPicker} component.
35353  * @constructor
35354  * Creates a new DateItem
35355  * @param {Object} config Configuration options
35356  */
35357 Roo.menu.DateItem = function(config){
35358     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35359     /** The Roo.DatePicker object @type Roo.DatePicker */
35360     this.picker = this.component;
35361     this.addEvents({select: true});
35362     
35363     this.picker.on("render", function(picker){
35364         picker.getEl().swallowEvent("click");
35365         picker.container.addClass("x-menu-date-item");
35366     });
35367
35368     this.picker.on("select", this.onSelect, this);
35369 };
35370
35371 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35372     // private
35373     onSelect : function(picker, date){
35374         this.fireEvent("select", this, date, picker);
35375         Roo.menu.DateItem.superclass.handleClick.call(this);
35376     }
35377 });/*
35378  * Based on:
35379  * Ext JS Library 1.1.1
35380  * Copyright(c) 2006-2007, Ext JS, LLC.
35381  *
35382  * Originally Released Under LGPL - original licence link has changed is not relivant.
35383  *
35384  * Fork - LGPL
35385  * <script type="text/javascript">
35386  */
35387  
35388 /**
35389  * @class Roo.menu.ColorItem
35390  * @extends Roo.menu.Adapter
35391  * A menu item that wraps the {@link Roo.ColorPalette} component.
35392  * @constructor
35393  * Creates a new ColorItem
35394  * @param {Object} config Configuration options
35395  */
35396 Roo.menu.ColorItem = function(config){
35397     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35398     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35399     this.palette = this.component;
35400     this.relayEvents(this.palette, ["select"]);
35401     if(this.selectHandler){
35402         this.on('select', this.selectHandler, this.scope);
35403     }
35404 };
35405 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35406  * Based on:
35407  * Ext JS Library 1.1.1
35408  * Copyright(c) 2006-2007, Ext JS, LLC.
35409  *
35410  * Originally Released Under LGPL - original licence link has changed is not relivant.
35411  *
35412  * Fork - LGPL
35413  * <script type="text/javascript">
35414  */
35415  
35416
35417 /**
35418  * @class Roo.menu.DateMenu
35419  * @extends Roo.menu.Menu
35420  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35421  * @constructor
35422  * Creates a new DateMenu
35423  * @param {Object} config Configuration options
35424  */
35425 Roo.menu.DateMenu = function(config){
35426     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35427     this.plain = true;
35428     var di = new Roo.menu.DateItem(config);
35429     this.add(di);
35430     /**
35431      * The {@link Roo.DatePicker} instance for this DateMenu
35432      * @type DatePicker
35433      */
35434     this.picker = di.picker;
35435     /**
35436      * @event select
35437      * @param {DatePicker} picker
35438      * @param {Date} date
35439      */
35440     this.relayEvents(di, ["select"]);
35441
35442     this.on('beforeshow', function(){
35443         if(this.picker){
35444             this.picker.hideMonthPicker(true);
35445         }
35446     }, this);
35447 };
35448 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35449     cls:'x-date-menu'
35450 });/*
35451  * Based on:
35452  * Ext JS Library 1.1.1
35453  * Copyright(c) 2006-2007, Ext JS, LLC.
35454  *
35455  * Originally Released Under LGPL - original licence link has changed is not relivant.
35456  *
35457  * Fork - LGPL
35458  * <script type="text/javascript">
35459  */
35460  
35461
35462 /**
35463  * @class Roo.menu.ColorMenu
35464  * @extends Roo.menu.Menu
35465  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35466  * @constructor
35467  * Creates a new ColorMenu
35468  * @param {Object} config Configuration options
35469  */
35470 Roo.menu.ColorMenu = function(config){
35471     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35472     this.plain = true;
35473     var ci = new Roo.menu.ColorItem(config);
35474     this.add(ci);
35475     /**
35476      * The {@link Roo.ColorPalette} instance for this ColorMenu
35477      * @type ColorPalette
35478      */
35479     this.palette = ci.palette;
35480     /**
35481      * @event select
35482      * @param {ColorPalette} palette
35483      * @param {String} color
35484      */
35485     this.relayEvents(ci, ["select"]);
35486 };
35487 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35488  * Based on:
35489  * Ext JS Library 1.1.1
35490  * Copyright(c) 2006-2007, Ext JS, LLC.
35491  *
35492  * Originally Released Under LGPL - original licence link has changed is not relivant.
35493  *
35494  * Fork - LGPL
35495  * <script type="text/javascript">
35496  */
35497  
35498 /**
35499  * @class Roo.form.Field
35500  * @extends Roo.BoxComponent
35501  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35502  * @constructor
35503  * Creates a new Field
35504  * @param {Object} config Configuration options
35505  */
35506 Roo.form.Field = function(config){
35507     Roo.form.Field.superclass.constructor.call(this, config);
35508 };
35509
35510 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35511     /**
35512      * @cfg {String} fieldLabel Label to use when rendering a form.
35513      */
35514        /**
35515      * @cfg {String} qtip Mouse over tip
35516      */
35517      
35518     /**
35519      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35520      */
35521     invalidClass : "x-form-invalid",
35522     /**
35523      * @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")
35524      */
35525     invalidText : "The value in this field is invalid",
35526     /**
35527      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35528      */
35529     focusClass : "x-form-focus",
35530     /**
35531      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35532       automatic validation (defaults to "keyup").
35533      */
35534     validationEvent : "keyup",
35535     /**
35536      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35537      */
35538     validateOnBlur : true,
35539     /**
35540      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35541      */
35542     validationDelay : 250,
35543     /**
35544      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35545      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35546      */
35547     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35548     /**
35549      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35550      */
35551     fieldClass : "x-form-field",
35552     /**
35553      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35554      *<pre>
35555 Value         Description
35556 -----------   ----------------------------------------------------------------------
35557 qtip          Display a quick tip when the user hovers over the field
35558 title         Display a default browser title attribute popup
35559 under         Add a block div beneath the field containing the error text
35560 side          Add an error icon to the right of the field with a popup on hover
35561 [element id]  Add the error text directly to the innerHTML of the specified element
35562 </pre>
35563      */
35564     msgTarget : 'qtip',
35565     /**
35566      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35567      */
35568     msgFx : 'normal',
35569
35570     /**
35571      * @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.
35572      */
35573     readOnly : false,
35574
35575     /**
35576      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35577      */
35578     disabled : false,
35579
35580     /**
35581      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35582      */
35583     inputType : undefined,
35584     
35585     /**
35586      * @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).
35587          */
35588         tabIndex : undefined,
35589         
35590     // private
35591     isFormField : true,
35592
35593     // private
35594     hasFocus : false,
35595     /**
35596      * @property {Roo.Element} fieldEl
35597      * Element Containing the rendered Field (with label etc.)
35598      */
35599     /**
35600      * @cfg {Mixed} value A value to initialize this field with.
35601      */
35602     value : undefined,
35603
35604     /**
35605      * @cfg {String} name The field's HTML name attribute.
35606      */
35607     /**
35608      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35609      */
35610
35611         // private ??
35612         initComponent : function(){
35613         Roo.form.Field.superclass.initComponent.call(this);
35614         this.addEvents({
35615             /**
35616              * @event focus
35617              * Fires when this field receives input focus.
35618              * @param {Roo.form.Field} this
35619              */
35620             focus : true,
35621             /**
35622              * @event blur
35623              * Fires when this field loses input focus.
35624              * @param {Roo.form.Field} this
35625              */
35626             blur : true,
35627             /**
35628              * @event specialkey
35629              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35630              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35631              * @param {Roo.form.Field} this
35632              * @param {Roo.EventObject} e The event object
35633              */
35634             specialkey : true,
35635             /**
35636              * @event change
35637              * Fires just before the field blurs if the field value has changed.
35638              * @param {Roo.form.Field} this
35639              * @param {Mixed} newValue The new value
35640              * @param {Mixed} oldValue The original value
35641              */
35642             change : true,
35643             /**
35644              * @event invalid
35645              * Fires after the field has been marked as invalid.
35646              * @param {Roo.form.Field} this
35647              * @param {String} msg The validation message
35648              */
35649             invalid : true,
35650             /**
35651              * @event valid
35652              * Fires after the field has been validated with no errors.
35653              * @param {Roo.form.Field} this
35654              */
35655             valid : true,
35656              /**
35657              * @event keyup
35658              * Fires after the key up
35659              * @param {Roo.form.Field} this
35660              * @param {Roo.EventObject}  e The event Object
35661              */
35662             keyup : true
35663         });
35664     },
35665
35666     /**
35667      * Returns the name attribute of the field if available
35668      * @return {String} name The field name
35669      */
35670     getName: function(){
35671          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35672     },
35673
35674     // private
35675     onRender : function(ct, position){
35676         Roo.form.Field.superclass.onRender.call(this, ct, position);
35677         if(!this.el){
35678             var cfg = this.getAutoCreate();
35679             if(!cfg.name){
35680                 cfg.name = this.name || this.id;
35681             }
35682             if(this.inputType){
35683                 cfg.type = this.inputType;
35684             }
35685             this.el = ct.createChild(cfg, position);
35686         }
35687         var type = this.el.dom.type;
35688         if(type){
35689             if(type == 'password'){
35690                 type = 'text';
35691             }
35692             this.el.addClass('x-form-'+type);
35693         }
35694         if(this.readOnly){
35695             this.el.dom.readOnly = true;
35696         }
35697         if(this.tabIndex !== undefined){
35698             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35699         }
35700
35701         this.el.addClass([this.fieldClass, this.cls]);
35702         this.initValue();
35703     },
35704
35705     /**
35706      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35707      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35708      * @return {Roo.form.Field} this
35709      */
35710     applyTo : function(target){
35711         this.allowDomMove = false;
35712         this.el = Roo.get(target);
35713         this.render(this.el.dom.parentNode);
35714         return this;
35715     },
35716
35717     // private
35718     initValue : function(){
35719         if(this.value !== undefined){
35720             this.setValue(this.value);
35721         }else if(this.el.dom.value.length > 0){
35722             this.setValue(this.el.dom.value);
35723         }
35724     },
35725
35726     /**
35727      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35728      */
35729     isDirty : function() {
35730         if(this.disabled) {
35731             return false;
35732         }
35733         return String(this.getValue()) !== String(this.originalValue);
35734     },
35735
35736     // private
35737     afterRender : function(){
35738         Roo.form.Field.superclass.afterRender.call(this);
35739         this.initEvents();
35740     },
35741
35742     // private
35743     fireKey : function(e){
35744         //Roo.log('field ' + e.getKey());
35745         if(e.isNavKeyPress()){
35746             this.fireEvent("specialkey", this, e);
35747         }
35748     },
35749
35750     /**
35751      * Resets the current field value to the originally loaded value and clears any validation messages
35752      */
35753     reset : function(){
35754         this.setValue(this.originalValue);
35755         this.clearInvalid();
35756     },
35757
35758     // private
35759     initEvents : function(){
35760         // safari killled keypress - so keydown is now used..
35761         this.el.on("keydown" , this.fireKey,  this);
35762         this.el.on("focus", this.onFocus,  this);
35763         this.el.on("blur", this.onBlur,  this);
35764         this.el.relayEvent('keyup', this);
35765
35766         // reference to original value for reset
35767         this.originalValue = this.getValue();
35768     },
35769
35770     // private
35771     onFocus : function(){
35772         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35773             this.el.addClass(this.focusClass);
35774         }
35775         if(!this.hasFocus){
35776             this.hasFocus = true;
35777             this.startValue = this.getValue();
35778             this.fireEvent("focus", this);
35779         }
35780     },
35781
35782     beforeBlur : Roo.emptyFn,
35783
35784     // private
35785     onBlur : function(){
35786         this.beforeBlur();
35787         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35788             this.el.removeClass(this.focusClass);
35789         }
35790         this.hasFocus = false;
35791         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35792             this.validate();
35793         }
35794         var v = this.getValue();
35795         if(String(v) !== String(this.startValue)){
35796             this.fireEvent('change', this, v, this.startValue);
35797         }
35798         this.fireEvent("blur", this);
35799     },
35800
35801     /**
35802      * Returns whether or not the field value is currently valid
35803      * @param {Boolean} preventMark True to disable marking the field invalid
35804      * @return {Boolean} True if the value is valid, else false
35805      */
35806     isValid : function(preventMark){
35807         if(this.disabled){
35808             return true;
35809         }
35810         var restore = this.preventMark;
35811         this.preventMark = preventMark === true;
35812         var v = this.validateValue(this.processValue(this.getRawValue()));
35813         this.preventMark = restore;
35814         return v;
35815     },
35816
35817     /**
35818      * Validates the field value
35819      * @return {Boolean} True if the value is valid, else false
35820      */
35821     validate : function(){
35822         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35823             this.clearInvalid();
35824             return true;
35825         }
35826         return false;
35827     },
35828
35829     processValue : function(value){
35830         return value;
35831     },
35832
35833     // private
35834     // Subclasses should provide the validation implementation by overriding this
35835     validateValue : function(value){
35836         return true;
35837     },
35838
35839     /**
35840      * Mark this field as invalid
35841      * @param {String} msg The validation message
35842      */
35843     markInvalid : function(msg){
35844         if(!this.rendered || this.preventMark){ // not rendered
35845             return;
35846         }
35847         this.el.addClass(this.invalidClass);
35848         msg = msg || this.invalidText;
35849         switch(this.msgTarget){
35850             case 'qtip':
35851                 this.el.dom.qtip = msg;
35852                 this.el.dom.qclass = 'x-form-invalid-tip';
35853                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35854                     Roo.QuickTips.enable();
35855                 }
35856                 break;
35857             case 'title':
35858                 this.el.dom.title = msg;
35859                 break;
35860             case 'under':
35861                 if(!this.errorEl){
35862                     var elp = this.el.findParent('.x-form-element', 5, true);
35863                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35864                     this.errorEl.setWidth(elp.getWidth(true)-20);
35865                 }
35866                 this.errorEl.update(msg);
35867                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35868                 break;
35869             case 'side':
35870                 if(!this.errorIcon){
35871                     var elp = this.el.findParent('.x-form-element', 5, true);
35872                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35873                 }
35874                 this.alignErrorIcon();
35875                 this.errorIcon.dom.qtip = msg;
35876                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35877                 this.errorIcon.show();
35878                 this.on('resize', this.alignErrorIcon, this);
35879                 break;
35880             default:
35881                 var t = Roo.getDom(this.msgTarget);
35882                 t.innerHTML = msg;
35883                 t.style.display = this.msgDisplay;
35884                 break;
35885         }
35886         this.fireEvent('invalid', this, msg);
35887     },
35888
35889     // private
35890     alignErrorIcon : function(){
35891         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35892     },
35893
35894     /**
35895      * Clear any invalid styles/messages for this field
35896      */
35897     clearInvalid : function(){
35898         if(!this.rendered || this.preventMark){ // not rendered
35899             return;
35900         }
35901         this.el.removeClass(this.invalidClass);
35902         switch(this.msgTarget){
35903             case 'qtip':
35904                 this.el.dom.qtip = '';
35905                 break;
35906             case 'title':
35907                 this.el.dom.title = '';
35908                 break;
35909             case 'under':
35910                 if(this.errorEl){
35911                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35912                 }
35913                 break;
35914             case 'side':
35915                 if(this.errorIcon){
35916                     this.errorIcon.dom.qtip = '';
35917                     this.errorIcon.hide();
35918                     this.un('resize', this.alignErrorIcon, this);
35919                 }
35920                 break;
35921             default:
35922                 var t = Roo.getDom(this.msgTarget);
35923                 t.innerHTML = '';
35924                 t.style.display = 'none';
35925                 break;
35926         }
35927         this.fireEvent('valid', this);
35928     },
35929
35930     /**
35931      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35932      * @return {Mixed} value The field value
35933      */
35934     getRawValue : function(){
35935         var v = this.el.getValue();
35936         if(v === this.emptyText){
35937             v = '';
35938         }
35939         return v;
35940     },
35941
35942     /**
35943      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35944      * @return {Mixed} value The field value
35945      */
35946     getValue : function(){
35947         var v = this.el.getValue();
35948         if(v === this.emptyText || v === undefined){
35949             v = '';
35950         }
35951         return v;
35952     },
35953
35954     /**
35955      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35956      * @param {Mixed} value The value to set
35957      */
35958     setRawValue : function(v){
35959         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35960     },
35961
35962     /**
35963      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35964      * @param {Mixed} value The value to set
35965      */
35966     setValue : function(v){
35967         this.value = v;
35968         if(this.rendered){
35969             this.el.dom.value = (v === null || v === undefined ? '' : v);
35970              this.validate();
35971         }
35972     },
35973
35974     adjustSize : function(w, h){
35975         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35976         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35977         return s;
35978     },
35979
35980     adjustWidth : function(tag, w){
35981         tag = tag.toLowerCase();
35982         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35983             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35984                 if(tag == 'input'){
35985                     return w + 2;
35986                 }
35987                 if(tag = 'textarea'){
35988                     return w-2;
35989                 }
35990             }else if(Roo.isOpera){
35991                 if(tag == 'input'){
35992                     return w + 2;
35993                 }
35994                 if(tag = 'textarea'){
35995                     return w-2;
35996                 }
35997             }
35998         }
35999         return w;
36000     }
36001 });
36002
36003
36004 // anything other than normal should be considered experimental
36005 Roo.form.Field.msgFx = {
36006     normal : {
36007         show: function(msgEl, f){
36008             msgEl.setDisplayed('block');
36009         },
36010
36011         hide : function(msgEl, f){
36012             msgEl.setDisplayed(false).update('');
36013         }
36014     },
36015
36016     slide : {
36017         show: function(msgEl, f){
36018             msgEl.slideIn('t', {stopFx:true});
36019         },
36020
36021         hide : function(msgEl, f){
36022             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36023         }
36024     },
36025
36026     slideRight : {
36027         show: function(msgEl, f){
36028             msgEl.fixDisplay();
36029             msgEl.alignTo(f.el, 'tl-tr');
36030             msgEl.slideIn('l', {stopFx:true});
36031         },
36032
36033         hide : function(msgEl, f){
36034             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36035         }
36036     }
36037 };/*
36038  * Based on:
36039  * Ext JS Library 1.1.1
36040  * Copyright(c) 2006-2007, Ext JS, LLC.
36041  *
36042  * Originally Released Under LGPL - original licence link has changed is not relivant.
36043  *
36044  * Fork - LGPL
36045  * <script type="text/javascript">
36046  */
36047  
36048
36049 /**
36050  * @class Roo.form.TextField
36051  * @extends Roo.form.Field
36052  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36053  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36054  * @constructor
36055  * Creates a new TextField
36056  * @param {Object} config Configuration options
36057  */
36058 Roo.form.TextField = function(config){
36059     Roo.form.TextField.superclass.constructor.call(this, config);
36060     this.addEvents({
36061         /**
36062          * @event autosize
36063          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36064          * according to the default logic, but this event provides a hook for the developer to apply additional
36065          * logic at runtime to resize the field if needed.
36066              * @param {Roo.form.Field} this This text field
36067              * @param {Number} width The new field width
36068              */
36069         autosize : true
36070     });
36071 };
36072
36073 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36074     /**
36075      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36076      */
36077     grow : false,
36078     /**
36079      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36080      */
36081     growMin : 30,
36082     /**
36083      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36084      */
36085     growMax : 800,
36086     /**
36087      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36088      */
36089     vtype : null,
36090     /**
36091      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36092      */
36093     maskRe : null,
36094     /**
36095      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36096      */
36097     disableKeyFilter : false,
36098     /**
36099      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36100      */
36101     allowBlank : true,
36102     /**
36103      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36104      */
36105     minLength : 0,
36106     /**
36107      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36108      */
36109     maxLength : Number.MAX_VALUE,
36110     /**
36111      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36112      */
36113     minLengthText : "The minimum length for this field is {0}",
36114     /**
36115      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36116      */
36117     maxLengthText : "The maximum length for this field is {0}",
36118     /**
36119      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36120      */
36121     selectOnFocus : false,
36122     /**
36123      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36124      */
36125     blankText : "This field is required",
36126     /**
36127      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36128      * If available, this function will be called only after the basic validators all return true, and will be passed the
36129      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36130      */
36131     validator : null,
36132     /**
36133      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36134      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36135      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36136      */
36137     regex : null,
36138     /**
36139      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36140      */
36141     regexText : "",
36142     /**
36143      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36144      */
36145     emptyText : null,
36146     /**
36147      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36148      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36149      */
36150     emptyClass : 'x-form-empty-field',
36151
36152     // private
36153     initEvents : function(){
36154         Roo.form.TextField.superclass.initEvents.call(this);
36155         if(this.validationEvent == 'keyup'){
36156             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36157             this.el.on('keyup', this.filterValidation, this);
36158         }
36159         else if(this.validationEvent !== false){
36160             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36161         }
36162         if(this.selectOnFocus || this.emptyText){
36163             this.on("focus", this.preFocus, this);
36164             if(this.emptyText){
36165                 this.on('blur', this.postBlur, this);
36166                 this.applyEmptyText();
36167             }
36168         }
36169         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36170             this.el.on("keypress", this.filterKeys, this);
36171         }
36172         if(this.grow){
36173             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36174             this.el.on("click", this.autoSize,  this);
36175         }
36176     },
36177
36178     processValue : function(value){
36179         if(this.stripCharsRe){
36180             var newValue = value.replace(this.stripCharsRe, '');
36181             if(newValue !== value){
36182                 this.setRawValue(newValue);
36183                 return newValue;
36184             }
36185         }
36186         return value;
36187     },
36188
36189     filterValidation : function(e){
36190         if(!e.isNavKeyPress()){
36191             this.validationTask.delay(this.validationDelay);
36192         }
36193     },
36194
36195     // private
36196     onKeyUp : function(e){
36197         if(!e.isNavKeyPress()){
36198             this.autoSize();
36199         }
36200     },
36201
36202     /**
36203      * Resets the current field value to the originally-loaded value and clears any validation messages.
36204      * Also adds emptyText and emptyClass if the original value was blank.
36205      */
36206     reset : function(){
36207         Roo.form.TextField.superclass.reset.call(this);
36208         this.applyEmptyText();
36209     },
36210
36211     applyEmptyText : function(){
36212         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36213             this.setRawValue(this.emptyText);
36214             this.el.addClass(this.emptyClass);
36215         }
36216     },
36217
36218     // private
36219     preFocus : function(){
36220         if(this.emptyText){
36221             if(this.el.dom.value == this.emptyText){
36222                 this.setRawValue('');
36223             }
36224             this.el.removeClass(this.emptyClass);
36225         }
36226         if(this.selectOnFocus){
36227             this.el.dom.select();
36228         }
36229     },
36230
36231     // private
36232     postBlur : function(){
36233         this.applyEmptyText();
36234     },
36235
36236     // private
36237     filterKeys : function(e){
36238         var k = e.getKey();
36239         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36240             return;
36241         }
36242         var c = e.getCharCode(), cc = String.fromCharCode(c);
36243         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36244             return;
36245         }
36246         if(!this.maskRe.test(cc)){
36247             e.stopEvent();
36248         }
36249     },
36250
36251     setValue : function(v){
36252         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36253             this.el.removeClass(this.emptyClass);
36254         }
36255         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36256         this.applyEmptyText();
36257         this.autoSize();
36258     },
36259
36260     /**
36261      * Validates a value according to the field's validation rules and marks the field as invalid
36262      * if the validation fails
36263      * @param {Mixed} value The value to validate
36264      * @return {Boolean} True if the value is valid, else false
36265      */
36266     validateValue : function(value){
36267         if(value.length < 1 || value === this.emptyText){ // if it's blank
36268              if(this.allowBlank){
36269                 this.clearInvalid();
36270                 return true;
36271              }else{
36272                 this.markInvalid(this.blankText);
36273                 return false;
36274              }
36275         }
36276         if(value.length < this.minLength){
36277             this.markInvalid(String.format(this.minLengthText, this.minLength));
36278             return false;
36279         }
36280         if(value.length > this.maxLength){
36281             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36282             return false;
36283         }
36284         if(this.vtype){
36285             var vt = Roo.form.VTypes;
36286             if(!vt[this.vtype](value, this)){
36287                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36288                 return false;
36289             }
36290         }
36291         if(typeof this.validator == "function"){
36292             var msg = this.validator(value);
36293             if(msg !== true){
36294                 this.markInvalid(msg);
36295                 return false;
36296             }
36297         }
36298         if(this.regex && !this.regex.test(value)){
36299             this.markInvalid(this.regexText);
36300             return false;
36301         }
36302         return true;
36303     },
36304
36305     /**
36306      * Selects text in this field
36307      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36308      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36309      */
36310     selectText : function(start, end){
36311         var v = this.getRawValue();
36312         if(v.length > 0){
36313             start = start === undefined ? 0 : start;
36314             end = end === undefined ? v.length : end;
36315             var d = this.el.dom;
36316             if(d.setSelectionRange){
36317                 d.setSelectionRange(start, end);
36318             }else if(d.createTextRange){
36319                 var range = d.createTextRange();
36320                 range.moveStart("character", start);
36321                 range.moveEnd("character", v.length-end);
36322                 range.select();
36323             }
36324         }
36325     },
36326
36327     /**
36328      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36329      * This only takes effect if grow = true, and fires the autosize event.
36330      */
36331     autoSize : function(){
36332         if(!this.grow || !this.rendered){
36333             return;
36334         }
36335         if(!this.metrics){
36336             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36337         }
36338         var el = this.el;
36339         var v = el.dom.value;
36340         var d = document.createElement('div');
36341         d.appendChild(document.createTextNode(v));
36342         v = d.innerHTML;
36343         d = null;
36344         v += "&#160;";
36345         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36346         this.el.setWidth(w);
36347         this.fireEvent("autosize", this, w);
36348     }
36349 });/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359  
36360 /**
36361  * @class Roo.form.Hidden
36362  * @extends Roo.form.TextField
36363  * Simple Hidden element used on forms 
36364  * 
36365  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36366  * 
36367  * @constructor
36368  * Creates a new Hidden form element.
36369  * @param {Object} config Configuration options
36370  */
36371
36372
36373
36374 // easy hidden field...
36375 Roo.form.Hidden = function(config){
36376     Roo.form.Hidden.superclass.constructor.call(this, config);
36377 };
36378   
36379 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36380     fieldLabel:      '',
36381     inputType:      'hidden',
36382     width:          50,
36383     allowBlank:     true,
36384     labelSeparator: '',
36385     hidden:         true,
36386     itemCls :       'x-form-item-display-none'
36387
36388
36389 });
36390
36391
36392 /*
36393  * Based on:
36394  * Ext JS Library 1.1.1
36395  * Copyright(c) 2006-2007, Ext JS, LLC.
36396  *
36397  * Originally Released Under LGPL - original licence link has changed is not relivant.
36398  *
36399  * Fork - LGPL
36400  * <script type="text/javascript">
36401  */
36402  
36403 /**
36404  * @class Roo.form.TriggerField
36405  * @extends Roo.form.TextField
36406  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36407  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36408  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36409  * for which you can provide a custom implementation.  For example:
36410  * <pre><code>
36411 var trigger = new Roo.form.TriggerField();
36412 trigger.onTriggerClick = myTriggerFn;
36413 trigger.applyTo('my-field');
36414 </code></pre>
36415  *
36416  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36417  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36418  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36419  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36420  * @constructor
36421  * Create a new TriggerField.
36422  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36423  * to the base TextField)
36424  */
36425 Roo.form.TriggerField = function(config){
36426     this.mimicing = false;
36427     Roo.form.TriggerField.superclass.constructor.call(this, config);
36428 };
36429
36430 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36431     /**
36432      * @cfg {String} triggerClass A CSS class to apply to the trigger
36433      */
36434     /**
36435      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36436      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36437      */
36438     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36439     /**
36440      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36441      */
36442     hideTrigger:false,
36443
36444     /** @cfg {Boolean} grow @hide */
36445     /** @cfg {Number} growMin @hide */
36446     /** @cfg {Number} growMax @hide */
36447
36448     /**
36449      * @hide 
36450      * @method
36451      */
36452     autoSize: Roo.emptyFn,
36453     // private
36454     monitorTab : true,
36455     // private
36456     deferHeight : true,
36457
36458     
36459     actionMode : 'wrap',
36460     // private
36461     onResize : function(w, h){
36462         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36463         if(typeof w == 'number'){
36464             var x = w - this.trigger.getWidth();
36465             this.el.setWidth(this.adjustWidth('input', x));
36466             this.trigger.setStyle('left', x+'px');
36467         }
36468     },
36469
36470     // private
36471     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36472
36473     // private
36474     getResizeEl : function(){
36475         return this.wrap;
36476     },
36477
36478     // private
36479     getPositionEl : function(){
36480         return this.wrap;
36481     },
36482
36483     // private
36484     alignErrorIcon : function(){
36485         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36486     },
36487
36488     // private
36489     onRender : function(ct, position){
36490         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36491         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36492         this.trigger = this.wrap.createChild(this.triggerConfig ||
36493                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36494         if(this.hideTrigger){
36495             this.trigger.setDisplayed(false);
36496         }
36497         this.initTrigger();
36498         if(!this.width){
36499             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36500         }
36501     },
36502
36503     // private
36504     initTrigger : function(){
36505         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36506         this.trigger.addClassOnOver('x-form-trigger-over');
36507         this.trigger.addClassOnClick('x-form-trigger-click');
36508     },
36509
36510     // private
36511     onDestroy : function(){
36512         if(this.trigger){
36513             this.trigger.removeAllListeners();
36514             this.trigger.remove();
36515         }
36516         if(this.wrap){
36517             this.wrap.remove();
36518         }
36519         Roo.form.TriggerField.superclass.onDestroy.call(this);
36520     },
36521
36522     // private
36523     onFocus : function(){
36524         Roo.form.TriggerField.superclass.onFocus.call(this);
36525         if(!this.mimicing){
36526             this.wrap.addClass('x-trigger-wrap-focus');
36527             this.mimicing = true;
36528             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36529             if(this.monitorTab){
36530                 this.el.on("keydown", this.checkTab, this);
36531             }
36532         }
36533     },
36534
36535     // private
36536     checkTab : function(e){
36537         if(e.getKey() == e.TAB){
36538             this.triggerBlur();
36539         }
36540     },
36541
36542     // private
36543     onBlur : function(){
36544         // do nothing
36545     },
36546
36547     // private
36548     mimicBlur : function(e, t){
36549         if(!this.wrap.contains(t) && this.validateBlur()){
36550             this.triggerBlur();
36551         }
36552     },
36553
36554     // private
36555     triggerBlur : function(){
36556         this.mimicing = false;
36557         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36558         if(this.monitorTab){
36559             this.el.un("keydown", this.checkTab, this);
36560         }
36561         this.wrap.removeClass('x-trigger-wrap-focus');
36562         Roo.form.TriggerField.superclass.onBlur.call(this);
36563     },
36564
36565     // private
36566     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36567     validateBlur : function(e, t){
36568         return true;
36569     },
36570
36571     // private
36572     onDisable : function(){
36573         Roo.form.TriggerField.superclass.onDisable.call(this);
36574         if(this.wrap){
36575             this.wrap.addClass('x-item-disabled');
36576         }
36577     },
36578
36579     // private
36580     onEnable : function(){
36581         Roo.form.TriggerField.superclass.onEnable.call(this);
36582         if(this.wrap){
36583             this.wrap.removeClass('x-item-disabled');
36584         }
36585     },
36586
36587     // private
36588     onShow : function(){
36589         var ae = this.getActionEl();
36590         
36591         if(ae){
36592             ae.dom.style.display = '';
36593             ae.dom.style.visibility = 'visible';
36594         }
36595     },
36596
36597     // private
36598     
36599     onHide : function(){
36600         var ae = this.getActionEl();
36601         ae.dom.style.display = 'none';
36602     },
36603
36604     /**
36605      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36606      * by an implementing function.
36607      * @method
36608      * @param {EventObject} e
36609      */
36610     onTriggerClick : Roo.emptyFn
36611 });
36612
36613 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36614 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36615 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36616 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36617     initComponent : function(){
36618         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36619
36620         this.triggerConfig = {
36621             tag:'span', cls:'x-form-twin-triggers', cn:[
36622             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36623             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36624         ]};
36625     },
36626
36627     getTrigger : function(index){
36628         return this.triggers[index];
36629     },
36630
36631     initTrigger : function(){
36632         var ts = this.trigger.select('.x-form-trigger', true);
36633         this.wrap.setStyle('overflow', 'hidden');
36634         var triggerField = this;
36635         ts.each(function(t, all, index){
36636             t.hide = function(){
36637                 var w = triggerField.wrap.getWidth();
36638                 this.dom.style.display = 'none';
36639                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36640             };
36641             t.show = function(){
36642                 var w = triggerField.wrap.getWidth();
36643                 this.dom.style.display = '';
36644                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36645             };
36646             var triggerIndex = 'Trigger'+(index+1);
36647
36648             if(this['hide'+triggerIndex]){
36649                 t.dom.style.display = 'none';
36650             }
36651             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36652             t.addClassOnOver('x-form-trigger-over');
36653             t.addClassOnClick('x-form-trigger-click');
36654         }, this);
36655         this.triggers = ts.elements;
36656     },
36657
36658     onTrigger1Click : Roo.emptyFn,
36659     onTrigger2Click : Roo.emptyFn
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670  
36671 /**
36672  * @class Roo.form.TextArea
36673  * @extends Roo.form.TextField
36674  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36675  * support for auto-sizing.
36676  * @constructor
36677  * Creates a new TextArea
36678  * @param {Object} config Configuration options
36679  */
36680 Roo.form.TextArea = function(config){
36681     Roo.form.TextArea.superclass.constructor.call(this, config);
36682     // these are provided exchanges for backwards compat
36683     // minHeight/maxHeight were replaced by growMin/growMax to be
36684     // compatible with TextField growing config values
36685     if(this.minHeight !== undefined){
36686         this.growMin = this.minHeight;
36687     }
36688     if(this.maxHeight !== undefined){
36689         this.growMax = this.maxHeight;
36690     }
36691 };
36692
36693 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36694     /**
36695      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36696      */
36697     growMin : 60,
36698     /**
36699      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36700      */
36701     growMax: 1000,
36702     /**
36703      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36704      * in the field (equivalent to setting overflow: hidden, defaults to false)
36705      */
36706     preventScrollbars: false,
36707     /**
36708      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36709      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36710      */
36711
36712     // private
36713     onRender : function(ct, position){
36714         if(!this.el){
36715             this.defaultAutoCreate = {
36716                 tag: "textarea",
36717                 style:"width:300px;height:60px;",
36718                 autocomplete: "off"
36719             };
36720         }
36721         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36722         if(this.grow){
36723             this.textSizeEl = Roo.DomHelper.append(document.body, {
36724                 tag: "pre", cls: "x-form-grow-sizer"
36725             });
36726             if(this.preventScrollbars){
36727                 this.el.setStyle("overflow", "hidden");
36728             }
36729             this.el.setHeight(this.growMin);
36730         }
36731     },
36732
36733     onDestroy : function(){
36734         if(this.textSizeEl){
36735             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36736         }
36737         Roo.form.TextArea.superclass.onDestroy.call(this);
36738     },
36739
36740     // private
36741     onKeyUp : function(e){
36742         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36743             this.autoSize();
36744         }
36745     },
36746
36747     /**
36748      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36749      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36750      */
36751     autoSize : function(){
36752         if(!this.grow || !this.textSizeEl){
36753             return;
36754         }
36755         var el = this.el;
36756         var v = el.dom.value;
36757         var ts = this.textSizeEl;
36758
36759         ts.innerHTML = '';
36760         ts.appendChild(document.createTextNode(v));
36761         v = ts.innerHTML;
36762
36763         Roo.fly(ts).setWidth(this.el.getWidth());
36764         if(v.length < 1){
36765             v = "&#160;&#160;";
36766         }else{
36767             if(Roo.isIE){
36768                 v = v.replace(/\n/g, '<p>&#160;</p>');
36769             }
36770             v += "&#160;\n&#160;";
36771         }
36772         ts.innerHTML = v;
36773         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36774         if(h != this.lastHeight){
36775             this.lastHeight = h;
36776             this.el.setHeight(h);
36777             this.fireEvent("autosize", this, h);
36778         }
36779     }
36780 });/*
36781  * Based on:
36782  * Ext JS Library 1.1.1
36783  * Copyright(c) 2006-2007, Ext JS, LLC.
36784  *
36785  * Originally Released Under LGPL - original licence link has changed is not relivant.
36786  *
36787  * Fork - LGPL
36788  * <script type="text/javascript">
36789  */
36790  
36791
36792 /**
36793  * @class Roo.form.NumberField
36794  * @extends Roo.form.TextField
36795  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36796  * @constructor
36797  * Creates a new NumberField
36798  * @param {Object} config Configuration options
36799  */
36800 Roo.form.NumberField = function(config){
36801     Roo.form.NumberField.superclass.constructor.call(this, config);
36802 };
36803
36804 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36805     /**
36806      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36807      */
36808     fieldClass: "x-form-field x-form-num-field",
36809     /**
36810      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36811      */
36812     allowDecimals : true,
36813     /**
36814      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36815      */
36816     decimalSeparator : ".",
36817     /**
36818      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36819      */
36820     decimalPrecision : 2,
36821     /**
36822      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36823      */
36824     allowNegative : true,
36825     /**
36826      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36827      */
36828     minValue : Number.NEGATIVE_INFINITY,
36829     /**
36830      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36831      */
36832     maxValue : Number.MAX_VALUE,
36833     /**
36834      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36835      */
36836     minText : "The minimum value for this field is {0}",
36837     /**
36838      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36839      */
36840     maxText : "The maximum value for this field is {0}",
36841     /**
36842      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36843      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36844      */
36845     nanText : "{0} is not a valid number",
36846
36847     // private
36848     initEvents : function(){
36849         Roo.form.NumberField.superclass.initEvents.call(this);
36850         var allowed = "0123456789";
36851         if(this.allowDecimals){
36852             allowed += this.decimalSeparator;
36853         }
36854         if(this.allowNegative){
36855             allowed += "-";
36856         }
36857         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36858         var keyPress = function(e){
36859             var k = e.getKey();
36860             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36861                 return;
36862             }
36863             var c = e.getCharCode();
36864             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36865                 e.stopEvent();
36866             }
36867         };
36868         this.el.on("keypress", keyPress, this);
36869     },
36870
36871     // private
36872     validateValue : function(value){
36873         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36874             return false;
36875         }
36876         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36877              return true;
36878         }
36879         var num = this.parseValue(value);
36880         if(isNaN(num)){
36881             this.markInvalid(String.format(this.nanText, value));
36882             return false;
36883         }
36884         if(num < this.minValue){
36885             this.markInvalid(String.format(this.minText, this.minValue));
36886             return false;
36887         }
36888         if(num > this.maxValue){
36889             this.markInvalid(String.format(this.maxText, this.maxValue));
36890             return false;
36891         }
36892         return true;
36893     },
36894
36895     getValue : function(){
36896         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36897     },
36898
36899     // private
36900     parseValue : function(value){
36901         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36902         return isNaN(value) ? '' : value;
36903     },
36904
36905     // private
36906     fixPrecision : function(value){
36907         var nan = isNaN(value);
36908         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36909             return nan ? '' : value;
36910         }
36911         return parseFloat(value).toFixed(this.decimalPrecision);
36912     },
36913
36914     setValue : function(v){
36915         v = this.fixPrecision(v);
36916         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36917     },
36918
36919     // private
36920     decimalPrecisionFcn : function(v){
36921         return Math.floor(v);
36922     },
36923
36924     beforeBlur : function(){
36925         var v = this.parseValue(this.getRawValue());
36926         if(v){
36927             this.setValue(v);
36928         }
36929     }
36930 });/*
36931  * Based on:
36932  * Ext JS Library 1.1.1
36933  * Copyright(c) 2006-2007, Ext JS, LLC.
36934  *
36935  * Originally Released Under LGPL - original licence link has changed is not relivant.
36936  *
36937  * Fork - LGPL
36938  * <script type="text/javascript">
36939  */
36940  
36941 /**
36942  * @class Roo.form.DateField
36943  * @extends Roo.form.TriggerField
36944  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36945 * @constructor
36946 * Create a new DateField
36947 * @param {Object} config
36948  */
36949 Roo.form.DateField = function(config){
36950     Roo.form.DateField.superclass.constructor.call(this, config);
36951     
36952       this.addEvents({
36953          
36954         /**
36955          * @event select
36956          * Fires when a date is selected
36957              * @param {Roo.form.DateField} combo This combo box
36958              * @param {Date} date The date selected
36959              */
36960         'select' : true
36961          
36962     });
36963     
36964     
36965     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36966     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36967     this.ddMatch = null;
36968     if(this.disabledDates){
36969         var dd = this.disabledDates;
36970         var re = "(?:";
36971         for(var i = 0; i < dd.length; i++){
36972             re += dd[i];
36973             if(i != dd.length-1) re += "|";
36974         }
36975         this.ddMatch = new RegExp(re + ")");
36976     }
36977 };
36978
36979 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36980     /**
36981      * @cfg {String} format
36982      * The default date format string which can be overriden for localization support.  The format must be
36983      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36984      */
36985     format : "m/d/y",
36986     /**
36987      * @cfg {String} altFormats
36988      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36989      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36990      */
36991     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36992     /**
36993      * @cfg {Array} disabledDays
36994      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36995      */
36996     disabledDays : null,
36997     /**
36998      * @cfg {String} disabledDaysText
36999      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37000      */
37001     disabledDaysText : "Disabled",
37002     /**
37003      * @cfg {Array} disabledDates
37004      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37005      * expression so they are very powerful. Some examples:
37006      * <ul>
37007      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37008      * <li>["03/08", "09/16"] would disable those days for every year</li>
37009      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37010      * <li>["03/../2006"] would disable every day in March 2006</li>
37011      * <li>["^03"] would disable every day in every March</li>
37012      * </ul>
37013      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37014      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37015      */
37016     disabledDates : null,
37017     /**
37018      * @cfg {String} disabledDatesText
37019      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37020      */
37021     disabledDatesText : "Disabled",
37022     /**
37023      * @cfg {Date/String} minValue
37024      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37025      * valid format (defaults to null).
37026      */
37027     minValue : null,
37028     /**
37029      * @cfg {Date/String} maxValue
37030      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37031      * valid format (defaults to null).
37032      */
37033     maxValue : null,
37034     /**
37035      * @cfg {String} minText
37036      * The error text to display when the date in the cell is before minValue (defaults to
37037      * 'The date in this field must be after {minValue}').
37038      */
37039     minText : "The date in this field must be equal to or after {0}",
37040     /**
37041      * @cfg {String} maxText
37042      * The error text to display when the date in the cell is after maxValue (defaults to
37043      * 'The date in this field must be before {maxValue}').
37044      */
37045     maxText : "The date in this field must be equal to or before {0}",
37046     /**
37047      * @cfg {String} invalidText
37048      * The error text to display when the date in the field is invalid (defaults to
37049      * '{value} is not a valid date - it must be in the format {format}').
37050      */
37051     invalidText : "{0} is not a valid date - it must be in the format {1}",
37052     /**
37053      * @cfg {String} triggerClass
37054      * An additional CSS class used to style the trigger button.  The trigger will always get the
37055      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37056      * which displays a calendar icon).
37057      */
37058     triggerClass : 'x-form-date-trigger',
37059     
37060
37061     /**
37062      * @cfg {bool} useIso
37063      * if enabled, then the date field will use a hidden field to store the 
37064      * real value as iso formated date. default (false)
37065      */ 
37066     useIso : false,
37067     /**
37068      * @cfg {String/Object} autoCreate
37069      * A DomHelper element spec, or true for a default element spec (defaults to
37070      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37071      */ 
37072     // private
37073     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37074     
37075     // private
37076     hiddenField: false,
37077     
37078     onRender : function(ct, position)
37079     {
37080         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37081         if (this.useIso) {
37082             this.el.dom.removeAttribute('name'); 
37083             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37084                     'before', true);
37085             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37086             // prevent input submission
37087             this.hiddenName = this.name;
37088         }
37089             
37090             
37091     },
37092     
37093     // private
37094     validateValue : function(value)
37095     {
37096         value = this.formatDate(value);
37097         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37098             return false;
37099         }
37100         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37101              return true;
37102         }
37103         var svalue = value;
37104         value = this.parseDate(value);
37105         if(!value){
37106             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37107             return false;
37108         }
37109         var time = value.getTime();
37110         if(this.minValue && time < this.minValue.getTime()){
37111             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37112             return false;
37113         }
37114         if(this.maxValue && time > this.maxValue.getTime()){
37115             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37116             return false;
37117         }
37118         if(this.disabledDays){
37119             var day = value.getDay();
37120             for(var i = 0; i < this.disabledDays.length; i++) {
37121                 if(day === this.disabledDays[i]){
37122                     this.markInvalid(this.disabledDaysText);
37123                     return false;
37124                 }
37125             }
37126         }
37127         var fvalue = this.formatDate(value);
37128         if(this.ddMatch && this.ddMatch.test(fvalue)){
37129             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37130             return false;
37131         }
37132         return true;
37133     },
37134
37135     // private
37136     // Provides logic to override the default TriggerField.validateBlur which just returns true
37137     validateBlur : function(){
37138         return !this.menu || !this.menu.isVisible();
37139     },
37140
37141     /**
37142      * Returns the current date value of the date field.
37143      * @return {Date} The date value
37144      */
37145     getValue : function(){
37146         
37147         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37148     },
37149
37150     /**
37151      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37152      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37153      * (the default format used is "m/d/y").
37154      * <br />Usage:
37155      * <pre><code>
37156 //All of these calls set the same date value (May 4, 2006)
37157
37158 //Pass a date object:
37159 var dt = new Date('5/4/06');
37160 dateField.setValue(dt);
37161
37162 //Pass a date string (default format):
37163 dateField.setValue('5/4/06');
37164
37165 //Pass a date string (custom format):
37166 dateField.format = 'Y-m-d';
37167 dateField.setValue('2006-5-4');
37168 </code></pre>
37169      * @param {String/Date} date The date or valid date string
37170      */
37171     setValue : function(date){
37172         if (this.hiddenField) {
37173             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37174         }
37175         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37176     },
37177
37178     // private
37179     parseDate : function(value){
37180         if(!value || value instanceof Date){
37181             return value;
37182         }
37183         var v = Date.parseDate(value, this.format);
37184         if(!v && this.altFormats){
37185             if(!this.altFormatsArray){
37186                 this.altFormatsArray = this.altFormats.split("|");
37187             }
37188             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37189                 v = Date.parseDate(value, this.altFormatsArray[i]);
37190             }
37191         }
37192         return v;
37193     },
37194
37195     // private
37196     formatDate : function(date, fmt){
37197         return (!date || !(date instanceof Date)) ?
37198                date : date.dateFormat(fmt || this.format);
37199     },
37200
37201     // private
37202     menuListeners : {
37203         select: function(m, d){
37204             this.setValue(d);
37205             this.fireEvent('select', this, d);
37206         },
37207         show : function(){ // retain focus styling
37208             this.onFocus();
37209         },
37210         hide : function(){
37211             this.focus.defer(10, this);
37212             var ml = this.menuListeners;
37213             this.menu.un("select", ml.select,  this);
37214             this.menu.un("show", ml.show,  this);
37215             this.menu.un("hide", ml.hide,  this);
37216         }
37217     },
37218
37219     // private
37220     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37221     onTriggerClick : function(){
37222         if(this.disabled){
37223             return;
37224         }
37225         if(this.menu == null){
37226             this.menu = new Roo.menu.DateMenu();
37227         }
37228         Roo.apply(this.menu.picker,  {
37229             showClear: this.allowBlank,
37230             minDate : this.minValue,
37231             maxDate : this.maxValue,
37232             disabledDatesRE : this.ddMatch,
37233             disabledDatesText : this.disabledDatesText,
37234             disabledDays : this.disabledDays,
37235             disabledDaysText : this.disabledDaysText,
37236             format : this.format,
37237             minText : String.format(this.minText, this.formatDate(this.minValue)),
37238             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37239         });
37240         this.menu.on(Roo.apply({}, this.menuListeners, {
37241             scope:this
37242         }));
37243         this.menu.picker.setValue(this.getValue() || new Date());
37244         this.menu.show(this.el, "tl-bl?");
37245     },
37246
37247     beforeBlur : function(){
37248         var v = this.parseDate(this.getRawValue());
37249         if(v){
37250             this.setValue(v);
37251         }
37252     }
37253
37254     /** @cfg {Boolean} grow @hide */
37255     /** @cfg {Number} growMin @hide */
37256     /** @cfg {Number} growMax @hide */
37257     /**
37258      * @hide
37259      * @method autoSize
37260      */
37261 });/*
37262  * Based on:
37263  * Ext JS Library 1.1.1
37264  * Copyright(c) 2006-2007, Ext JS, LLC.
37265  *
37266  * Originally Released Under LGPL - original licence link has changed is not relivant.
37267  *
37268  * Fork - LGPL
37269  * <script type="text/javascript">
37270  */
37271  
37272
37273 /**
37274  * @class Roo.form.ComboBox
37275  * @extends Roo.form.TriggerField
37276  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37277  * @constructor
37278  * Create a new ComboBox.
37279  * @param {Object} config Configuration options
37280  */
37281 Roo.form.ComboBox = function(config){
37282     Roo.form.ComboBox.superclass.constructor.call(this, config);
37283     this.addEvents({
37284         /**
37285          * @event expand
37286          * Fires when the dropdown list is expanded
37287              * @param {Roo.form.ComboBox} combo This combo box
37288              */
37289         'expand' : true,
37290         /**
37291          * @event collapse
37292          * Fires when the dropdown list is collapsed
37293              * @param {Roo.form.ComboBox} combo This combo box
37294              */
37295         'collapse' : true,
37296         /**
37297          * @event beforeselect
37298          * Fires before a list item is selected. Return false to cancel the selection.
37299              * @param {Roo.form.ComboBox} combo This combo box
37300              * @param {Roo.data.Record} record The data record returned from the underlying store
37301              * @param {Number} index The index of the selected item in the dropdown list
37302              */
37303         'beforeselect' : true,
37304         /**
37305          * @event select
37306          * Fires when a list item is selected
37307              * @param {Roo.form.ComboBox} combo This combo box
37308              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37309              * @param {Number} index The index of the selected item in the dropdown list
37310              */
37311         'select' : true,
37312         /**
37313          * @event beforequery
37314          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37315          * The event object passed has these properties:
37316              * @param {Roo.form.ComboBox} combo This combo box
37317              * @param {String} query The query
37318              * @param {Boolean} forceAll true to force "all" query
37319              * @param {Boolean} cancel true to cancel the query
37320              * @param {Object} e The query event object
37321              */
37322         'beforequery': true,
37323          /**
37324          * @event add
37325          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37326              * @param {Roo.form.ComboBox} combo This combo box
37327              */
37328         'add' : true,
37329         /**
37330          * @event edit
37331          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37332              * @param {Roo.form.ComboBox} combo This combo box
37333              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37334              */
37335         'edit' : true
37336         
37337         
37338     });
37339     if(this.transform){
37340         this.allowDomMove = false;
37341         var s = Roo.getDom(this.transform);
37342         if(!this.hiddenName){
37343             this.hiddenName = s.name;
37344         }
37345         if(!this.store){
37346             this.mode = 'local';
37347             var d = [], opts = s.options;
37348             for(var i = 0, len = opts.length;i < len; i++){
37349                 var o = opts[i];
37350                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37351                 if(o.selected) {
37352                     this.value = value;
37353                 }
37354                 d.push([value, o.text]);
37355             }
37356             this.store = new Roo.data.SimpleStore({
37357                 'id': 0,
37358                 fields: ['value', 'text'],
37359                 data : d
37360             });
37361             this.valueField = 'value';
37362             this.displayField = 'text';
37363         }
37364         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37365         if(!this.lazyRender){
37366             this.target = true;
37367             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37368             s.parentNode.removeChild(s); // remove it
37369             this.render(this.el.parentNode);
37370         }else{
37371             s.parentNode.removeChild(s); // remove it
37372         }
37373
37374     }
37375     if (this.store) {
37376         this.store = Roo.factory(this.store, Roo.data);
37377     }
37378     
37379     this.selectedIndex = -1;
37380     if(this.mode == 'local'){
37381         if(config.queryDelay === undefined){
37382             this.queryDelay = 10;
37383         }
37384         if(config.minChars === undefined){
37385             this.minChars = 0;
37386         }
37387     }
37388 };
37389
37390 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37391     /**
37392      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37393      */
37394     /**
37395      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37396      * rendering into an Roo.Editor, defaults to false)
37397      */
37398     /**
37399      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37400      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37401      */
37402     /**
37403      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37404      */
37405     /**
37406      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37407      * the dropdown list (defaults to undefined, with no header element)
37408      */
37409
37410      /**
37411      * @cfg {String/Roo.Template} tpl The template to use to render the output
37412      */
37413      
37414     // private
37415     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37416     /**
37417      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37418      */
37419     listWidth: undefined,
37420     /**
37421      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37422      * mode = 'remote' or 'text' if mode = 'local')
37423      */
37424     displayField: undefined,
37425     /**
37426      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37427      * mode = 'remote' or 'value' if mode = 'local'). 
37428      * Note: use of a valueField requires the user make a selection
37429      * in order for a value to be mapped.
37430      */
37431     valueField: undefined,
37432     
37433     
37434     /**
37435      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37436      * field's data value (defaults to the underlying DOM element's name)
37437      */
37438     hiddenName: undefined,
37439     /**
37440      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37441      */
37442     listClass: '',
37443     /**
37444      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37445      */
37446     selectedClass: 'x-combo-selected',
37447     /**
37448      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37449      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37450      * which displays a downward arrow icon).
37451      */
37452     triggerClass : 'x-form-arrow-trigger',
37453     /**
37454      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37455      */
37456     shadow:'sides',
37457     /**
37458      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37459      * anchor positions (defaults to 'tl-bl')
37460      */
37461     listAlign: 'tl-bl?',
37462     /**
37463      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37464      */
37465     maxHeight: 300,
37466     /**
37467      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37468      * query specified by the allQuery config option (defaults to 'query')
37469      */
37470     triggerAction: 'query',
37471     /**
37472      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37473      * (defaults to 4, does not apply if editable = false)
37474      */
37475     minChars : 4,
37476     /**
37477      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37478      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37479      */
37480     typeAhead: false,
37481     /**
37482      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37483      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37484      */
37485     queryDelay: 500,
37486     /**
37487      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37488      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37489      */
37490     pageSize: 0,
37491     /**
37492      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37493      * when editable = true (defaults to false)
37494      */
37495     selectOnFocus:false,
37496     /**
37497      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37498      */
37499     queryParam: 'query',
37500     /**
37501      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37502      * when mode = 'remote' (defaults to 'Loading...')
37503      */
37504     loadingText: 'Loading...',
37505     /**
37506      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37507      */
37508     resizable: false,
37509     /**
37510      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37511      */
37512     handleHeight : 8,
37513     /**
37514      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37515      * traditional select (defaults to true)
37516      */
37517     editable: true,
37518     /**
37519      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37520      */
37521     allQuery: '',
37522     /**
37523      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37524      */
37525     mode: 'remote',
37526     /**
37527      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37528      * listWidth has a higher value)
37529      */
37530     minListWidth : 70,
37531     /**
37532      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37533      * allow the user to set arbitrary text into the field (defaults to false)
37534      */
37535     forceSelection:false,
37536     /**
37537      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37538      * if typeAhead = true (defaults to 250)
37539      */
37540     typeAheadDelay : 250,
37541     /**
37542      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37543      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37544      */
37545     valueNotFoundText : undefined,
37546     /**
37547      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37548      */
37549     blockFocus : false,
37550     
37551     /**
37552      * @cfg {Boolean} disableClear Disable showing of clear button.
37553      */
37554     disableClear : false,
37555     /**
37556      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37557      */
37558     alwaysQuery : false,
37559     
37560     //private
37561     addicon : false,
37562     editicon: false,
37563     
37564     // element that contains real text value.. (when hidden is used..)
37565      
37566     // private
37567     onRender : function(ct, position){
37568         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37569         if(this.hiddenName){
37570             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37571                     'before', true);
37572             this.hiddenField.value =
37573                 this.hiddenValue !== undefined ? this.hiddenValue :
37574                 this.value !== undefined ? this.value : '';
37575
37576             // prevent input submission
37577             this.el.dom.removeAttribute('name');
37578              
37579              
37580         }
37581         if(Roo.isGecko){
37582             this.el.dom.setAttribute('autocomplete', 'off');
37583         }
37584
37585         var cls = 'x-combo-list';
37586
37587         this.list = new Roo.Layer({
37588             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37589         });
37590
37591         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37592         this.list.setWidth(lw);
37593         this.list.swallowEvent('mousewheel');
37594         this.assetHeight = 0;
37595
37596         if(this.title){
37597             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37598             this.assetHeight += this.header.getHeight();
37599         }
37600
37601         this.innerList = this.list.createChild({cls:cls+'-inner'});
37602         this.innerList.on('mouseover', this.onViewOver, this);
37603         this.innerList.on('mousemove', this.onViewMove, this);
37604         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37605         
37606         if(this.allowBlank && !this.pageSize && !this.disableClear){
37607             this.footer = this.list.createChild({cls:cls+'-ft'});
37608             this.pageTb = new Roo.Toolbar(this.footer);
37609            
37610         }
37611         if(this.pageSize){
37612             this.footer = this.list.createChild({cls:cls+'-ft'});
37613             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37614                     {pageSize: this.pageSize});
37615             
37616         }
37617         
37618         if (this.pageTb && this.allowBlank && !this.disableClear) {
37619             var _this = this;
37620             this.pageTb.add(new Roo.Toolbar.Fill(), {
37621                 cls: 'x-btn-icon x-btn-clear',
37622                 text: '&#160;',
37623                 handler: function()
37624                 {
37625                     _this.collapse();
37626                     _this.clearValue();
37627                     _this.onSelect(false, -1);
37628                 }
37629             });
37630         }
37631         if (this.footer) {
37632             this.assetHeight += this.footer.getHeight();
37633         }
37634         
37635
37636         if(!this.tpl){
37637             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37638         }
37639
37640         this.view = new Roo.View(this.innerList, this.tpl, {
37641             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37642         });
37643
37644         this.view.on('click', this.onViewClick, this);
37645
37646         this.store.on('beforeload', this.onBeforeLoad, this);
37647         this.store.on('load', this.onLoad, this);
37648         this.store.on('loadexception', this.onLoadException, this);
37649
37650         if(this.resizable){
37651             this.resizer = new Roo.Resizable(this.list,  {
37652                pinned:true, handles:'se'
37653             });
37654             this.resizer.on('resize', function(r, w, h){
37655                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37656                 this.listWidth = w;
37657                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37658                 this.restrictHeight();
37659             }, this);
37660             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37661         }
37662         if(!this.editable){
37663             this.editable = true;
37664             this.setEditable(false);
37665         }  
37666         
37667         
37668         if (typeof(this.events.add.listeners) != 'undefined') {
37669             
37670             this.addicon = this.wrap.createChild(
37671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37672        
37673             this.addicon.on('click', function(e) {
37674                 this.fireEvent('add', this);
37675             }, this);
37676         }
37677         if (typeof(this.events.edit.listeners) != 'undefined') {
37678             
37679             this.editicon = this.wrap.createChild(
37680                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37681             if (this.addicon) {
37682                 this.editicon.setStyle('margin-left', '40px');
37683             }
37684             this.editicon.on('click', function(e) {
37685                 
37686                 // we fire even  if inothing is selected..
37687                 this.fireEvent('edit', this, this.lastData );
37688                 
37689             }, this);
37690         }
37691         
37692         
37693         
37694     },
37695
37696     // private
37697     initEvents : function(){
37698         Roo.form.ComboBox.superclass.initEvents.call(this);
37699
37700         this.keyNav = new Roo.KeyNav(this.el, {
37701             "up" : function(e){
37702                 this.inKeyMode = true;
37703                 this.selectPrev();
37704             },
37705
37706             "down" : function(e){
37707                 if(!this.isExpanded()){
37708                     this.onTriggerClick();
37709                 }else{
37710                     this.inKeyMode = true;
37711                     this.selectNext();
37712                 }
37713             },
37714
37715             "enter" : function(e){
37716                 this.onViewClick();
37717                 //return true;
37718             },
37719
37720             "esc" : function(e){
37721                 this.collapse();
37722             },
37723
37724             "tab" : function(e){
37725                 this.onViewClick(false);
37726                 this.fireEvent("specialkey", this, e);
37727                 return true;
37728             },
37729
37730             scope : this,
37731
37732             doRelay : function(foo, bar, hname){
37733                 if(hname == 'down' || this.scope.isExpanded()){
37734                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37735                 }
37736                 return true;
37737             },
37738
37739             forceKeyDown: true
37740         });
37741         this.queryDelay = Math.max(this.queryDelay || 10,
37742                 this.mode == 'local' ? 10 : 250);
37743         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37744         if(this.typeAhead){
37745             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37746         }
37747         if(this.editable !== false){
37748             this.el.on("keyup", this.onKeyUp, this);
37749         }
37750         if(this.forceSelection){
37751             this.on('blur', this.doForce, this);
37752         }
37753     },
37754
37755     onDestroy : function(){
37756         if(this.view){
37757             this.view.setStore(null);
37758             this.view.el.removeAllListeners();
37759             this.view.el.remove();
37760             this.view.purgeListeners();
37761         }
37762         if(this.list){
37763             this.list.destroy();
37764         }
37765         if(this.store){
37766             this.store.un('beforeload', this.onBeforeLoad, this);
37767             this.store.un('load', this.onLoad, this);
37768             this.store.un('loadexception', this.onLoadException, this);
37769         }
37770         Roo.form.ComboBox.superclass.onDestroy.call(this);
37771     },
37772
37773     // private
37774     fireKey : function(e){
37775         if(e.isNavKeyPress() && !this.list.isVisible()){
37776             this.fireEvent("specialkey", this, e);
37777         }
37778     },
37779
37780     // private
37781     onResize: function(w, h){
37782         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37783         
37784         if(typeof w != 'number'){
37785             // we do not handle it!?!?
37786             return;
37787         }
37788         var tw = this.trigger.getWidth();
37789         tw += this.addicon ? this.addicon.getWidth() : 0;
37790         tw += this.editicon ? this.editicon.getWidth() : 0;
37791         var x = w - tw;
37792         this.el.setWidth( this.adjustWidth('input', x));
37793             
37794         this.trigger.setStyle('left', x+'px');
37795         
37796         if(this.list && this.listWidth === undefined){
37797             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37798             this.list.setWidth(lw);
37799             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37800         }
37801         
37802     
37803         
37804     },
37805
37806     /**
37807      * Allow or prevent the user from directly editing the field text.  If false is passed,
37808      * the user will only be able to select from the items defined in the dropdown list.  This method
37809      * is the runtime equivalent of setting the 'editable' config option at config time.
37810      * @param {Boolean} value True to allow the user to directly edit the field text
37811      */
37812     setEditable : function(value){
37813         if(value == this.editable){
37814             return;
37815         }
37816         this.editable = value;
37817         if(!value){
37818             this.el.dom.setAttribute('readOnly', true);
37819             this.el.on('mousedown', this.onTriggerClick,  this);
37820             this.el.addClass('x-combo-noedit');
37821         }else{
37822             this.el.dom.setAttribute('readOnly', false);
37823             this.el.un('mousedown', this.onTriggerClick,  this);
37824             this.el.removeClass('x-combo-noedit');
37825         }
37826     },
37827
37828     // private
37829     onBeforeLoad : function(){
37830         if(!this.hasFocus){
37831             return;
37832         }
37833         this.innerList.update(this.loadingText ?
37834                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37835         this.restrictHeight();
37836         this.selectedIndex = -1;
37837     },
37838
37839     // private
37840     onLoad : function(){
37841         if(!this.hasFocus){
37842             return;
37843         }
37844         if(this.store.getCount() > 0){
37845             this.expand();
37846             this.restrictHeight();
37847             if(this.lastQuery == this.allQuery){
37848                 if(this.editable){
37849                     this.el.dom.select();
37850                 }
37851                 if(!this.selectByValue(this.value, true)){
37852                     this.select(0, true);
37853                 }
37854             }else{
37855                 this.selectNext();
37856                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37857                     this.taTask.delay(this.typeAheadDelay);
37858                 }
37859             }
37860         }else{
37861             this.onEmptyResults();
37862         }
37863         //this.el.focus();
37864     },
37865     // private
37866     onLoadException : function()
37867     {
37868         this.collapse();
37869         Roo.log(this.store.reader.jsonData);
37870         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37871             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37872         }
37873         
37874         
37875     },
37876     // private
37877     onTypeAhead : function(){
37878         if(this.store.getCount() > 0){
37879             var r = this.store.getAt(0);
37880             var newValue = r.data[this.displayField];
37881             var len = newValue.length;
37882             var selStart = this.getRawValue().length;
37883             if(selStart != len){
37884                 this.setRawValue(newValue);
37885                 this.selectText(selStart, newValue.length);
37886             }
37887         }
37888     },
37889
37890     // private
37891     onSelect : function(record, index){
37892         if(this.fireEvent('beforeselect', this, record, index) !== false){
37893             this.setFromData(index > -1 ? record.data : false);
37894             this.collapse();
37895             this.fireEvent('select', this, record, index);
37896         }
37897     },
37898
37899     /**
37900      * Returns the currently selected field value or empty string if no value is set.
37901      * @return {String} value The selected value
37902      */
37903     getValue : function(){
37904         if(this.valueField){
37905             return typeof this.value != 'undefined' ? this.value : '';
37906         }else{
37907             return Roo.form.ComboBox.superclass.getValue.call(this);
37908         }
37909     },
37910
37911     /**
37912      * Clears any text/value currently set in the field
37913      */
37914     clearValue : function(){
37915         if(this.hiddenField){
37916             this.hiddenField.value = '';
37917         }
37918         this.value = '';
37919         this.setRawValue('');
37920         this.lastSelectionText = '';
37921         this.applyEmptyText();
37922     },
37923
37924     /**
37925      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37926      * will be displayed in the field.  If the value does not match the data value of an existing item,
37927      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37928      * Otherwise the field will be blank (although the value will still be set).
37929      * @param {String} value The value to match
37930      */
37931     setValue : function(v){
37932         var text = v;
37933         if(this.valueField){
37934             var r = this.findRecord(this.valueField, v);
37935             if(r){
37936                 text = r.data[this.displayField];
37937             }else if(this.valueNotFoundText !== undefined){
37938                 text = this.valueNotFoundText;
37939             }
37940         }
37941         this.lastSelectionText = text;
37942         if(this.hiddenField){
37943             this.hiddenField.value = v;
37944         }
37945         Roo.form.ComboBox.superclass.setValue.call(this, text);
37946         this.value = v;
37947     },
37948     /**
37949      * @property {Object} the last set data for the element
37950      */
37951     
37952     lastData : false,
37953     /**
37954      * Sets the value of the field based on a object which is related to the record format for the store.
37955      * @param {Object} value the value to set as. or false on reset?
37956      */
37957     setFromData : function(o){
37958         var dv = ''; // display value
37959         var vv = ''; // value value..
37960         this.lastData = o;
37961         if (this.displayField) {
37962             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37963         } else {
37964             // this is an error condition!!!
37965             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37966         }
37967         
37968         if(this.valueField){
37969             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37970         }
37971         if(this.hiddenField){
37972             this.hiddenField.value = vv;
37973             
37974             this.lastSelectionText = dv;
37975             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37976             this.value = vv;
37977             return;
37978         }
37979         // no hidden field.. - we store the value in 'value', but still display
37980         // display field!!!!
37981         this.lastSelectionText = dv;
37982         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37983         this.value = vv;
37984         
37985         
37986     },
37987     // private
37988     reset : function(){
37989         // overridden so that last data is reset..
37990         this.setValue(this.originalValue);
37991         this.clearInvalid();
37992         this.lastData = false;
37993     },
37994     // private
37995     findRecord : function(prop, value){
37996         var record;
37997         if(this.store.getCount() > 0){
37998             this.store.each(function(r){
37999                 if(r.data[prop] == value){
38000                     record = r;
38001                     return false;
38002                 }
38003                 return true;
38004             });
38005         }
38006         return record;
38007     },
38008     
38009     getName: function()
38010     {
38011         // returns hidden if it's set..
38012         if (!this.rendered) {return ''};
38013         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38014         
38015     },
38016     // private
38017     onViewMove : function(e, t){
38018         this.inKeyMode = false;
38019     },
38020
38021     // private
38022     onViewOver : function(e, t){
38023         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38024             return;
38025         }
38026         var item = this.view.findItemFromChild(t);
38027         if(item){
38028             var index = this.view.indexOf(item);
38029             this.select(index, false);
38030         }
38031     },
38032
38033     // private
38034     onViewClick : function(doFocus)
38035     {
38036         var index = this.view.getSelectedIndexes()[0];
38037         var r = this.store.getAt(index);
38038         if(r){
38039             this.onSelect(r, index);
38040         }
38041         if(doFocus !== false && !this.blockFocus){
38042             this.el.focus();
38043         }
38044     },
38045
38046     // private
38047     restrictHeight : function(){
38048         this.innerList.dom.style.height = '';
38049         var inner = this.innerList.dom;
38050         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38051         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38052         this.list.beginUpdate();
38053         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38054         this.list.alignTo(this.el, this.listAlign);
38055         this.list.endUpdate();
38056     },
38057
38058     // private
38059     onEmptyResults : function(){
38060         this.collapse();
38061     },
38062
38063     /**
38064      * Returns true if the dropdown list is expanded, else false.
38065      */
38066     isExpanded : function(){
38067         return this.list.isVisible();
38068     },
38069
38070     /**
38071      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38073      * @param {String} value The data value of the item to select
38074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38075      * selected item if it is not currently in view (defaults to true)
38076      * @return {Boolean} True if the value matched an item in the list, else false
38077      */
38078     selectByValue : function(v, scrollIntoView){
38079         if(v !== undefined && v !== null){
38080             var r = this.findRecord(this.valueField || this.displayField, v);
38081             if(r){
38082                 this.select(this.store.indexOf(r), scrollIntoView);
38083                 return true;
38084             }
38085         }
38086         return false;
38087     },
38088
38089     /**
38090      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38091      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38092      * @param {Number} index The zero-based index of the list item to select
38093      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38094      * selected item if it is not currently in view (defaults to true)
38095      */
38096     select : function(index, scrollIntoView){
38097         this.selectedIndex = index;
38098         this.view.select(index);
38099         if(scrollIntoView !== false){
38100             var el = this.view.getNode(index);
38101             if(el){
38102                 this.innerList.scrollChildIntoView(el, false);
38103             }
38104         }
38105     },
38106
38107     // private
38108     selectNext : function(){
38109         var ct = this.store.getCount();
38110         if(ct > 0){
38111             if(this.selectedIndex == -1){
38112                 this.select(0);
38113             }else if(this.selectedIndex < ct-1){
38114                 this.select(this.selectedIndex+1);
38115             }
38116         }
38117     },
38118
38119     // private
38120     selectPrev : function(){
38121         var ct = this.store.getCount();
38122         if(ct > 0){
38123             if(this.selectedIndex == -1){
38124                 this.select(0);
38125             }else if(this.selectedIndex != 0){
38126                 this.select(this.selectedIndex-1);
38127             }
38128         }
38129     },
38130
38131     // private
38132     onKeyUp : function(e){
38133         if(this.editable !== false && !e.isSpecialKey()){
38134             this.lastKey = e.getKey();
38135             this.dqTask.delay(this.queryDelay);
38136         }
38137     },
38138
38139     // private
38140     validateBlur : function(){
38141         return !this.list || !this.list.isVisible();   
38142     },
38143
38144     // private
38145     initQuery : function(){
38146         this.doQuery(this.getRawValue());
38147     },
38148
38149     // private
38150     doForce : function(){
38151         if(this.el.dom.value.length > 0){
38152             this.el.dom.value =
38153                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38154             this.applyEmptyText();
38155         }
38156     },
38157
38158     /**
38159      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38160      * query allowing the query action to be canceled if needed.
38161      * @param {String} query The SQL query to execute
38162      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38163      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38164      * saved in the current store (defaults to false)
38165      */
38166     doQuery : function(q, forceAll){
38167         if(q === undefined || q === null){
38168             q = '';
38169         }
38170         var qe = {
38171             query: q,
38172             forceAll: forceAll,
38173             combo: this,
38174             cancel:false
38175         };
38176         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38177             return false;
38178         }
38179         q = qe.query;
38180         forceAll = qe.forceAll;
38181         if(forceAll === true || (q.length >= this.minChars)){
38182             if(this.lastQuery != q || this.alwaysQuery){
38183                 this.lastQuery = q;
38184                 if(this.mode == 'local'){
38185                     this.selectedIndex = -1;
38186                     if(forceAll){
38187                         this.store.clearFilter();
38188                     }else{
38189                         this.store.filter(this.displayField, q);
38190                     }
38191                     this.onLoad();
38192                 }else{
38193                     this.store.baseParams[this.queryParam] = q;
38194                     this.store.load({
38195                         params: this.getParams(q)
38196                     });
38197                     this.expand();
38198                 }
38199             }else{
38200                 this.selectedIndex = -1;
38201                 this.onLoad();   
38202             }
38203         }
38204     },
38205
38206     // private
38207     getParams : function(q){
38208         var p = {};
38209         //p[this.queryParam] = q;
38210         if(this.pageSize){
38211             p.start = 0;
38212             p.limit = this.pageSize;
38213         }
38214         return p;
38215     },
38216
38217     /**
38218      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38219      */
38220     collapse : function(){
38221         if(!this.isExpanded()){
38222             return;
38223         }
38224         this.list.hide();
38225         Roo.get(document).un('mousedown', this.collapseIf, this);
38226         Roo.get(document).un('mousewheel', this.collapseIf, this);
38227         if (!this.editable) {
38228             Roo.get(document).un('keydown', this.listKeyPress, this);
38229         }
38230         this.fireEvent('collapse', this);
38231     },
38232
38233     // private
38234     collapseIf : function(e){
38235         if(!e.within(this.wrap) && !e.within(this.list)){
38236             this.collapse();
38237         }
38238     },
38239
38240     /**
38241      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38242      */
38243     expand : function(){
38244         if(this.isExpanded() || !this.hasFocus){
38245             return;
38246         }
38247         this.list.alignTo(this.el, this.listAlign);
38248         this.list.show();
38249         Roo.get(document).on('mousedown', this.collapseIf, this);
38250         Roo.get(document).on('mousewheel', this.collapseIf, this);
38251         if (!this.editable) {
38252             Roo.get(document).on('keydown', this.listKeyPress, this);
38253         }
38254         
38255         this.fireEvent('expand', this);
38256     },
38257
38258     // private
38259     // Implements the default empty TriggerField.onTriggerClick function
38260     onTriggerClick : function(){
38261         if(this.disabled){
38262             return;
38263         }
38264         if(this.isExpanded()){
38265             this.collapse();
38266             if (!this.blockFocus) {
38267                 this.el.focus();
38268             }
38269             
38270         }else {
38271             this.hasFocus = true;
38272             if(this.triggerAction == 'all') {
38273                 this.doQuery(this.allQuery, true);
38274             } else {
38275                 this.doQuery(this.getRawValue());
38276             }
38277             if (!this.blockFocus) {
38278                 this.el.focus();
38279             }
38280         }
38281     },
38282     listKeyPress : function(e)
38283     {
38284         //Roo.log('listkeypress');
38285         // scroll to first matching element based on key pres..
38286         if (e.isSpecialKey()) {
38287             return false;
38288         }
38289         var k = String.fromCharCode(e.getKey()).toUpperCase();
38290         //Roo.log(k);
38291         var match  = false;
38292         var csel = this.view.getSelectedNodes();
38293         var cselitem = false;
38294         if (csel.length) {
38295             var ix = this.view.indexOf(csel[0]);
38296             cselitem  = this.store.getAt(ix);
38297             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38298                 cselitem = false;
38299             }
38300             
38301         }
38302         
38303         this.store.each(function(v) { 
38304             if (cselitem) {
38305                 // start at existing selection.
38306                 if (cselitem.id == v.id) {
38307                     cselitem = false;
38308                 }
38309                 return;
38310             }
38311                 
38312             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38313                 match = this.store.indexOf(v);
38314                 return false;
38315             }
38316         }, this);
38317         
38318         if (match === false) {
38319             return true; // no more action?
38320         }
38321         // scroll to?
38322         this.view.select(match);
38323         var sn = Roo.get(this.view.getSelectedNodes()[0])
38324         sn.scrollIntoView(sn.dom.parentNode, false);
38325     }
38326
38327     /** 
38328     * @cfg {Boolean} grow 
38329     * @hide 
38330     */
38331     /** 
38332     * @cfg {Number} growMin 
38333     * @hide 
38334     */
38335     /** 
38336     * @cfg {Number} growMax 
38337     * @hide 
38338     */
38339     /**
38340      * @hide
38341      * @method autoSize
38342      */
38343 });/*
38344  * Based on:
38345  * Ext JS Library 1.1.1
38346  * Copyright(c) 2006-2007, Ext JS, LLC.
38347  *
38348  * Originally Released Under LGPL - original licence link has changed is not relivant.
38349  *
38350  * Fork - LGPL
38351  * <script type="text/javascript">
38352  */
38353 /**
38354  * @class Roo.form.Checkbox
38355  * @extends Roo.form.Field
38356  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38357  * @constructor
38358  * Creates a new Checkbox
38359  * @param {Object} config Configuration options
38360  */
38361 Roo.form.Checkbox = function(config){
38362     Roo.form.Checkbox.superclass.constructor.call(this, config);
38363     this.addEvents({
38364         /**
38365          * @event check
38366          * Fires when the checkbox is checked or unchecked.
38367              * @param {Roo.form.Checkbox} this This checkbox
38368              * @param {Boolean} checked The new checked value
38369              */
38370         check : true
38371     });
38372 };
38373
38374 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38375     /**
38376      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38377      */
38378     focusClass : undefined,
38379     /**
38380      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38381      */
38382     fieldClass: "x-form-field",
38383     /**
38384      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38385      */
38386     checked: false,
38387     /**
38388      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38389      * {tag: "input", type: "checkbox", autocomplete: "off"})
38390      */
38391     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38392     /**
38393      * @cfg {String} boxLabel The text that appears beside the checkbox
38394      */
38395     boxLabel : "",
38396     /**
38397      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38398      */  
38399     inputValue : '1',
38400     /**
38401      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38402      */
38403      valueOff: '0', // value when not checked..
38404
38405     actionMode : 'viewEl', 
38406     //
38407     // private
38408     itemCls : 'x-menu-check-item x-form-item',
38409     groupClass : 'x-menu-group-item',
38410     inputType : 'hidden',
38411     
38412     
38413     inSetChecked: false, // check that we are not calling self...
38414     
38415     inputElement: false, // real input element?
38416     basedOn: false, // ????
38417     
38418     isFormField: true, // not sure where this is needed!!!!
38419
38420     onResize : function(){
38421         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38422         if(!this.boxLabel){
38423             this.el.alignTo(this.wrap, 'c-c');
38424         }
38425     },
38426
38427     initEvents : function(){
38428         Roo.form.Checkbox.superclass.initEvents.call(this);
38429         this.el.on("click", this.onClick,  this);
38430         this.el.on("change", this.onClick,  this);
38431     },
38432
38433
38434     getResizeEl : function(){
38435         return this.wrap;
38436     },
38437
38438     getPositionEl : function(){
38439         return this.wrap;
38440     },
38441
38442     // private
38443     onRender : function(ct, position){
38444         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38445         /*
38446         if(this.inputValue !== undefined){
38447             this.el.dom.value = this.inputValue;
38448         }
38449         */
38450         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38451         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38452         var viewEl = this.wrap.createChild({ 
38453             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38454         this.viewEl = viewEl;   
38455         this.wrap.on('click', this.onClick,  this); 
38456         
38457         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38458         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38459         
38460         
38461         
38462         if(this.boxLabel){
38463             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38464         //    viewEl.on('click', this.onClick,  this); 
38465         }
38466         //if(this.checked){
38467             this.setChecked(this.checked);
38468         //}else{
38469             //this.checked = this.el.dom;
38470         //}
38471
38472     },
38473
38474     // private
38475     initValue : Roo.emptyFn,
38476
38477     /**
38478      * Returns the checked state of the checkbox.
38479      * @return {Boolean} True if checked, else false
38480      */
38481     getValue : function(){
38482         if(this.el){
38483             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38484         }
38485         return this.valueOff;
38486         
38487     },
38488
38489         // private
38490     onClick : function(){ 
38491         this.setChecked(!this.checked);
38492
38493         //if(this.el.dom.checked != this.checked){
38494         //    this.setValue(this.el.dom.checked);
38495        // }
38496     },
38497
38498     /**
38499      * Sets the checked state of the checkbox.
38500      * On is always based on a string comparison between inputValue and the param.
38501      * @param {Boolean/String} value - the value to set 
38502      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38503      */
38504     setValue : function(v,suppressEvent){
38505         
38506         
38507         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38508         //if(this.el && this.el.dom){
38509         //    this.el.dom.checked = this.checked;
38510         //    this.el.dom.defaultChecked = this.checked;
38511         //}
38512         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38513         //this.fireEvent("check", this, this.checked);
38514     },
38515     // private..
38516     setChecked : function(state,suppressEvent)
38517     {
38518         if (this.inSetChecked) {
38519             this.checked = state;
38520             return;
38521         }
38522         
38523     
38524         if(this.wrap){
38525             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38526         }
38527         this.checked = state;
38528         if(suppressEvent !== true){
38529             this.fireEvent('check', this, state);
38530         }
38531         this.inSetChecked = true;
38532         this.el.dom.value = state ? this.inputValue : this.valueOff;
38533         this.inSetChecked = false;
38534         
38535     },
38536     // handle setting of hidden value by some other method!!?!?
38537     setFromHidden: function()
38538     {
38539         if(!this.el){
38540             return;
38541         }
38542         //console.log("SET FROM HIDDEN");
38543         //alert('setFrom hidden');
38544         this.setValue(this.el.dom.value);
38545     },
38546     
38547     onDestroy : function()
38548     {
38549         if(this.viewEl){
38550             Roo.get(this.viewEl).remove();
38551         }
38552          
38553         Roo.form.Checkbox.superclass.onDestroy.call(this);
38554     }
38555
38556 });/*
38557  * Based on:
38558  * Ext JS Library 1.1.1
38559  * Copyright(c) 2006-2007, Ext JS, LLC.
38560  *
38561  * Originally Released Under LGPL - original licence link has changed is not relivant.
38562  *
38563  * Fork - LGPL
38564  * <script type="text/javascript">
38565  */
38566  
38567 /**
38568  * @class Roo.form.Radio
38569  * @extends Roo.form.Checkbox
38570  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38571  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38572  * @constructor
38573  * Creates a new Radio
38574  * @param {Object} config Configuration options
38575  */
38576 Roo.form.Radio = function(){
38577     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38578 };
38579 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38580     inputType: 'radio',
38581
38582     /**
38583      * If this radio is part of a group, it will return the selected value
38584      * @return {String}
38585      */
38586     getGroupValue : function(){
38587         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38588     }
38589 });//<script type="text/javascript">
38590
38591 /*
38592  * Ext JS Library 1.1.1
38593  * Copyright(c) 2006-2007, Ext JS, LLC.
38594  * licensing@extjs.com
38595  * 
38596  * http://www.extjs.com/license
38597  */
38598  
38599  /*
38600   * 
38601   * Known bugs:
38602   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38603   * - IE ? - no idea how much works there.
38604   * 
38605   * 
38606   * 
38607   */
38608  
38609
38610 /**
38611  * @class Ext.form.HtmlEditor
38612  * @extends Ext.form.Field
38613  * Provides a lightweight HTML Editor component.
38614  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38615  * 
38616  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38617  * supported by this editor.</b><br/><br/>
38618  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38619  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38620  */
38621 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38622       /**
38623      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38624      */
38625     toolbars : false,
38626     /**
38627      * @cfg {String} createLinkText The default text for the create link prompt
38628      */
38629     createLinkText : 'Please enter the URL for the link:',
38630     /**
38631      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38632      */
38633     defaultLinkValue : 'http:/'+'/',
38634    
38635      /**
38636      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38637      *                        Roo.resizable.
38638      */
38639     resizable : false,
38640      /**
38641      * @cfg {Number} height (in pixels)
38642      */   
38643     height: 300,
38644    /**
38645      * @cfg {Number} width (in pixels)
38646      */   
38647     width: 500,
38648     
38649     /**
38650      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38651      * 
38652      */
38653     stylesheets: false,
38654     
38655     // id of frame..
38656     frameId: false,
38657     
38658     // private properties
38659     validationEvent : false,
38660     deferHeight: true,
38661     initialized : false,
38662     activated : false,
38663     sourceEditMode : false,
38664     onFocus : Roo.emptyFn,
38665     iframePad:3,
38666     hideMode:'offsets',
38667     
38668     defaultAutoCreate : { // modified by initCompnoent..
38669         tag: "textarea",
38670         style:"width:500px;height:300px;",
38671         autocomplete: "off"
38672     },
38673
38674     // private
38675     initComponent : function(){
38676         this.addEvents({
38677             /**
38678              * @event initialize
38679              * Fires when the editor is fully initialized (including the iframe)
38680              * @param {HtmlEditor} this
38681              */
38682             initialize: true,
38683             /**
38684              * @event activate
38685              * Fires when the editor is first receives the focus. Any insertion must wait
38686              * until after this event.
38687              * @param {HtmlEditor} this
38688              */
38689             activate: true,
38690              /**
38691              * @event beforesync
38692              * Fires before the textarea is updated with content from the editor iframe. Return false
38693              * to cancel the sync.
38694              * @param {HtmlEditor} this
38695              * @param {String} html
38696              */
38697             beforesync: true,
38698              /**
38699              * @event beforepush
38700              * Fires before the iframe editor is updated with content from the textarea. Return false
38701              * to cancel the push.
38702              * @param {HtmlEditor} this
38703              * @param {String} html
38704              */
38705             beforepush: true,
38706              /**
38707              * @event sync
38708              * Fires when the textarea is updated with content from the editor iframe.
38709              * @param {HtmlEditor} this
38710              * @param {String} html
38711              */
38712             sync: true,
38713              /**
38714              * @event push
38715              * Fires when the iframe editor is updated with content from the textarea.
38716              * @param {HtmlEditor} this
38717              * @param {String} html
38718              */
38719             push: true,
38720              /**
38721              * @event editmodechange
38722              * Fires when the editor switches edit modes
38723              * @param {HtmlEditor} this
38724              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38725              */
38726             editmodechange: true,
38727             /**
38728              * @event editorevent
38729              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38730              * @param {HtmlEditor} this
38731              */
38732             editorevent: true
38733         });
38734         this.defaultAutoCreate =  {
38735             tag: "textarea",
38736             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38737             autocomplete: "off"
38738         };
38739     },
38740
38741     /**
38742      * Protected method that will not generally be called directly. It
38743      * is called when the editor creates its toolbar. Override this method if you need to
38744      * add custom toolbar buttons.
38745      * @param {HtmlEditor} editor
38746      */
38747     createToolbar : function(editor){
38748         if (!editor.toolbars || !editor.toolbars.length) {
38749             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38750         }
38751         
38752         for (var i =0 ; i < editor.toolbars.length;i++) {
38753             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38754             editor.toolbars[i].init(editor);
38755         }
38756          
38757         
38758     },
38759
38760     /**
38761      * Protected method that will not generally be called directly. It
38762      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38763      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38764      */
38765     getDocMarkup : function(){
38766         // body styles..
38767         var st = '';
38768         if (this.stylesheets === false) {
38769             
38770             Roo.get(document.head).select('style').each(function(node) {
38771                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38772             });
38773             
38774             Roo.get(document.head).select('link').each(function(node) { 
38775                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38776             });
38777             
38778         } else if (!this.stylesheets.length) {
38779                 // simple..
38780                 st = '<style type="text/css">' +
38781                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38782                    '</style>';
38783         } else {
38784             Roo.each(this.stylesheets, function(s) {
38785                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38786             });
38787             
38788         }
38789         
38790         return '<html><head>' + st  +
38791             //<style type="text/css">' +
38792             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38793             //'</style>' +
38794             ' </head><body></body></html>';
38795     },
38796
38797     // private
38798     onRender : function(ct, position)
38799     {
38800         var _t = this;
38801         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38802         this.el.dom.style.border = '0 none';
38803         this.el.dom.setAttribute('tabIndex', -1);
38804         this.el.addClass('x-hidden');
38805         if(Roo.isIE){ // fix IE 1px bogus margin
38806             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38807         }
38808         this.wrap = this.el.wrap({
38809             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38810         });
38811         
38812         if (this.resizable) {
38813             this.resizeEl = new Roo.Resizable(this.wrap, {
38814                 pinned : true,
38815                 wrap: true,
38816                 dynamic : true,
38817                 minHeight : this.height,
38818                 height: this.height,
38819                 handles : this.resizable,
38820                 width: this.width,
38821                 listeners : {
38822                     resize : function(r, w, h) {
38823                         _t.onResize(w,h); // -something
38824                     }
38825                 }
38826             });
38827             
38828         }
38829
38830         this.frameId = Roo.id();
38831         
38832         this.createToolbar(this);
38833         
38834       
38835         
38836         var iframe = this.wrap.createChild({
38837             tag: 'iframe',
38838             id: this.frameId,
38839             name: this.frameId,
38840             frameBorder : 'no',
38841             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38842         }, this.el
38843         );
38844         
38845        // console.log(iframe);
38846         //this.wrap.dom.appendChild(iframe);
38847
38848         this.iframe = iframe.dom;
38849
38850          this.assignDocWin();
38851         
38852         this.doc.designMode = 'on';
38853        
38854         this.doc.open();
38855         this.doc.write(this.getDocMarkup());
38856         this.doc.close();
38857
38858         
38859         var task = { // must defer to wait for browser to be ready
38860             run : function(){
38861                 //console.log("run task?" + this.doc.readyState);
38862                 this.assignDocWin();
38863                 if(this.doc.body || this.doc.readyState == 'complete'){
38864                     try {
38865                         this.doc.designMode="on";
38866                     } catch (e) {
38867                         return;
38868                     }
38869                     Roo.TaskMgr.stop(task);
38870                     this.initEditor.defer(10, this);
38871                 }
38872             },
38873             interval : 10,
38874             duration:10000,
38875             scope: this
38876         };
38877         Roo.TaskMgr.start(task);
38878
38879         if(!this.width){
38880             this.setSize(this.wrap.getSize());
38881         }
38882         if (this.resizeEl) {
38883             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38884             // should trigger onReize..
38885         }
38886     },
38887
38888     // private
38889     onResize : function(w, h)
38890     {
38891         //Roo.log('resize: ' +w + ',' + h );
38892         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38893         if(this.el && this.iframe){
38894             if(typeof w == 'number'){
38895                 var aw = w - this.wrap.getFrameWidth('lr');
38896                 this.el.setWidth(this.adjustWidth('textarea', aw));
38897                 this.iframe.style.width = aw + 'px';
38898             }
38899             if(typeof h == 'number'){
38900                 var tbh = 0;
38901                 for (var i =0; i < this.toolbars.length;i++) {
38902                     // fixme - ask toolbars for heights?
38903                     tbh += this.toolbars[i].tb.el.getHeight();
38904                     if (this.toolbars[i].footer) {
38905                         tbh += this.toolbars[i].footer.el.getHeight();
38906                     }
38907                 }
38908                 
38909                 
38910                 
38911                 
38912                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38913                 ah -= 5; // knock a few pixes off for look..
38914                 this.el.setHeight(this.adjustWidth('textarea', ah));
38915                 this.iframe.style.height = ah + 'px';
38916                 if(this.doc){
38917                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38918                 }
38919             }
38920         }
38921     },
38922
38923     /**
38924      * Toggles the editor between standard and source edit mode.
38925      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38926      */
38927     toggleSourceEdit : function(sourceEditMode){
38928         
38929         this.sourceEditMode = sourceEditMode === true;
38930         
38931         if(this.sourceEditMode){
38932           
38933             this.syncValue();
38934             this.iframe.className = 'x-hidden';
38935             this.el.removeClass('x-hidden');
38936             this.el.dom.removeAttribute('tabIndex');
38937             this.el.focus();
38938         }else{
38939              
38940             this.pushValue();
38941             this.iframe.className = '';
38942             this.el.addClass('x-hidden');
38943             this.el.dom.setAttribute('tabIndex', -1);
38944             this.deferFocus();
38945         }
38946         this.setSize(this.wrap.getSize());
38947         this.fireEvent('editmodechange', this, this.sourceEditMode);
38948     },
38949
38950     // private used internally
38951     createLink : function(){
38952         var url = prompt(this.createLinkText, this.defaultLinkValue);
38953         if(url && url != 'http:/'+'/'){
38954             this.relayCmd('createlink', url);
38955         }
38956     },
38957
38958     // private (for BoxComponent)
38959     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38960
38961     // private (for BoxComponent)
38962     getResizeEl : function(){
38963         return this.wrap;
38964     },
38965
38966     // private (for BoxComponent)
38967     getPositionEl : function(){
38968         return this.wrap;
38969     },
38970
38971     // private
38972     initEvents : function(){
38973         this.originalValue = this.getValue();
38974     },
38975
38976     /**
38977      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38978      * @method
38979      */
38980     markInvalid : Roo.emptyFn,
38981     /**
38982      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38983      * @method
38984      */
38985     clearInvalid : Roo.emptyFn,
38986
38987     setValue : function(v){
38988         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38989         this.pushValue();
38990     },
38991
38992     /**
38993      * Protected method that will not generally be called directly. If you need/want
38994      * custom HTML cleanup, this is the method you should override.
38995      * @param {String} html The HTML to be cleaned
38996      * return {String} The cleaned HTML
38997      */
38998     cleanHtml : function(html){
38999         html = String(html);
39000         if(html.length > 5){
39001             if(Roo.isSafari){ // strip safari nonsense
39002                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39003             }
39004         }
39005         if(html == '&nbsp;'){
39006             html = '';
39007         }
39008         return html;
39009     },
39010
39011     /**
39012      * Protected method that will not generally be called directly. Syncs the contents
39013      * of the editor iframe with the textarea.
39014      */
39015     syncValue : function(){
39016         if(this.initialized){
39017             var bd = (this.doc.body || this.doc.documentElement);
39018             //this.cleanUpPaste();
39019             var html = bd.innerHTML;
39020             if(Roo.isSafari){
39021                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39022                 var m = bs.match(/text-align:(.*?);/i);
39023                 if(m && m[1]){
39024                     html = '<div style="'+m[0]+'">' + html + '</div>';
39025                 }
39026             }
39027             html = this.cleanHtml(html);
39028             if(this.fireEvent('beforesync', this, html) !== false){
39029                 this.el.dom.value = html;
39030                 this.fireEvent('sync', this, html);
39031             }
39032         }
39033     },
39034
39035     /**
39036      * Protected method that will not generally be called directly. Pushes the value of the textarea
39037      * into the iframe editor.
39038      */
39039     pushValue : function(){
39040         if(this.initialized){
39041             var v = this.el.dom.value;
39042             if(v.length < 1){
39043                 v = '&#160;';
39044             }
39045             
39046             if(this.fireEvent('beforepush', this, v) !== false){
39047                 var d = (this.doc.body || this.doc.documentElement);
39048                 d.innerHTML = v;
39049                 this.cleanUpPaste();
39050                 this.el.dom.value = d.innerHTML;
39051                 this.fireEvent('push', this, v);
39052             }
39053         }
39054     },
39055
39056     // private
39057     deferFocus : function(){
39058         this.focus.defer(10, this);
39059     },
39060
39061     // doc'ed in Field
39062     focus : function(){
39063         if(this.win && !this.sourceEditMode){
39064             this.win.focus();
39065         }else{
39066             this.el.focus();
39067         }
39068     },
39069     
39070     assignDocWin: function()
39071     {
39072         var iframe = this.iframe;
39073         
39074          if(Roo.isIE){
39075             this.doc = iframe.contentWindow.document;
39076             this.win = iframe.contentWindow;
39077         } else {
39078             if (!Roo.get(this.frameId)) {
39079                 return;
39080             }
39081             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39082             this.win = Roo.get(this.frameId).dom.contentWindow;
39083         }
39084     },
39085     
39086     // private
39087     initEditor : function(){
39088         //console.log("INIT EDITOR");
39089         this.assignDocWin();
39090         
39091         
39092         
39093         this.doc.designMode="on";
39094         this.doc.open();
39095         this.doc.write(this.getDocMarkup());
39096         this.doc.close();
39097         
39098         var dbody = (this.doc.body || this.doc.documentElement);
39099         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39100         // this copies styles from the containing element into thsi one..
39101         // not sure why we need all of this..
39102         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39103         ss['background-attachment'] = 'fixed'; // w3c
39104         dbody.bgProperties = 'fixed'; // ie
39105         Roo.DomHelper.applyStyles(dbody, ss);
39106         Roo.EventManager.on(this.doc, {
39107             //'mousedown': this.onEditorEvent,
39108             'mouseup': this.onEditorEvent,
39109             'dblclick': this.onEditorEvent,
39110             'click': this.onEditorEvent,
39111             'keyup': this.onEditorEvent,
39112             buffer:100,
39113             scope: this
39114         });
39115         if(Roo.isGecko){
39116             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39117         }
39118         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39119             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39120         }
39121         this.initialized = true;
39122
39123         this.fireEvent('initialize', this);
39124         this.pushValue();
39125     },
39126
39127     // private
39128     onDestroy : function(){
39129         
39130         
39131         
39132         if(this.rendered){
39133             
39134             for (var i =0; i < this.toolbars.length;i++) {
39135                 // fixme - ask toolbars for heights?
39136                 this.toolbars[i].onDestroy();
39137             }
39138             
39139             this.wrap.dom.innerHTML = '';
39140             this.wrap.remove();
39141         }
39142     },
39143
39144     // private
39145     onFirstFocus : function(){
39146         
39147         this.assignDocWin();
39148         
39149         
39150         this.activated = true;
39151         for (var i =0; i < this.toolbars.length;i++) {
39152             this.toolbars[i].onFirstFocus();
39153         }
39154        
39155         if(Roo.isGecko){ // prevent silly gecko errors
39156             this.win.focus();
39157             var s = this.win.getSelection();
39158             if(!s.focusNode || s.focusNode.nodeType != 3){
39159                 var r = s.getRangeAt(0);
39160                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39161                 r.collapse(true);
39162                 this.deferFocus();
39163             }
39164             try{
39165                 this.execCmd('useCSS', true);
39166                 this.execCmd('styleWithCSS', false);
39167             }catch(e){}
39168         }
39169         this.fireEvent('activate', this);
39170     },
39171
39172     // private
39173     adjustFont: function(btn){
39174         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39175         //if(Roo.isSafari){ // safari
39176         //    adjust *= 2;
39177        // }
39178         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39179         if(Roo.isSafari){ // safari
39180             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39181             v =  (v < 10) ? 10 : v;
39182             v =  (v > 48) ? 48 : v;
39183             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39184             
39185         }
39186         
39187         
39188         v = Math.max(1, v+adjust);
39189         
39190         this.execCmd('FontSize', v  );
39191     },
39192
39193     onEditorEvent : function(e){
39194         this.fireEvent('editorevent', this, e);
39195       //  this.updateToolbar();
39196         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39197     },
39198
39199     insertTag : function(tg)
39200     {
39201         // could be a bit smarter... -> wrap the current selected tRoo..
39202         
39203         this.execCmd("formatblock",   tg);
39204         
39205     },
39206     
39207     insertText : function(txt)
39208     {
39209         
39210         
39211         range = this.createRange();
39212         range.deleteContents();
39213                //alert(Sender.getAttribute('label'));
39214                
39215         range.insertNode(this.doc.createTextNode(txt));
39216     } ,
39217     
39218     // private
39219     relayBtnCmd : function(btn){
39220         this.relayCmd(btn.cmd);
39221     },
39222
39223     /**
39224      * Executes a Midas editor command on the editor document and performs necessary focus and
39225      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39226      * @param {String} cmd The Midas command
39227      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39228      */
39229     relayCmd : function(cmd, value){
39230         this.win.focus();
39231         this.execCmd(cmd, value);
39232         this.fireEvent('editorevent', this);
39233         //this.updateToolbar();
39234         this.deferFocus();
39235     },
39236
39237     /**
39238      * Executes a Midas editor command directly on the editor document.
39239      * For visual commands, you should use {@link #relayCmd} instead.
39240      * <b>This should only be called after the editor is initialized.</b>
39241      * @param {String} cmd The Midas command
39242      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39243      */
39244     execCmd : function(cmd, value){
39245         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39246         this.syncValue();
39247     },
39248
39249    
39250     /**
39251      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39252      * to insert tRoo.
39253      * @param {String} text
39254      */
39255     insertAtCursor : function(text){
39256         if(!this.activated){
39257             return;
39258         }
39259         if(Roo.isIE){
39260             this.win.focus();
39261             var r = this.doc.selection.createRange();
39262             if(r){
39263                 r.collapse(true);
39264                 r.pasteHTML(text);
39265                 this.syncValue();
39266                 this.deferFocus();
39267             }
39268         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39269             this.win.focus();
39270             this.execCmd('InsertHTML', text);
39271             this.deferFocus();
39272         }
39273     },
39274  // private
39275     mozKeyPress : function(e){
39276         if(e.ctrlKey){
39277             var c = e.getCharCode(), cmd;
39278           
39279             if(c > 0){
39280                 c = String.fromCharCode(c).toLowerCase();
39281                 switch(c){
39282                     case 'b':
39283                         cmd = 'bold';
39284                     break;
39285                     case 'i':
39286                         cmd = 'italic';
39287                     break;
39288                     case 'u':
39289                         cmd = 'underline';
39290                         break;
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          Roo.log('cleanuppaste');
39594         this.cleanUpChildren(this.doc.body);
39595         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39596         if (clean != this.doc.body.innerHTML) {
39597             this.doc.body.innerHTML = clean;
39598         }
39599         
39600     },
39601     
39602     cleanWordChars : function(input) {
39603         var he = Roo.form.HtmlEditor;
39604     
39605         var output = input;
39606         Roo.each(he.swapCodes, function(sw) { 
39607         
39608             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39609             output = output.replace(swapper, sw[1]);
39610         });
39611         return output;
39612     },
39613     
39614     
39615     cleanUpChildren : function (n)
39616     {
39617         if (!n.childNodes.length) {
39618             return;
39619         }
39620         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39621            this.cleanUpChild(n.childNodes[i]);
39622         }
39623     },
39624     
39625     
39626         
39627     
39628     cleanUpChild : function (node)
39629     {
39630         //console.log(node);
39631         if (node.nodeName == "#text") {
39632             // clean up silly Windows -- stuff?
39633             return; 
39634         }
39635         if (node.nodeName == "#comment") {
39636             node.parentNode.removeChild(node);
39637             // clean up silly Windows -- stuff?
39638             return; 
39639         }
39640         
39641         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39642             // remove node.
39643             node.parentNode.removeChild(node);
39644             return;
39645             
39646         }
39647         
39648         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39649         
39650         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39651         
39652         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39653             remove_keep_children = true;
39654         }
39655         
39656         if (remove_keep_children) {
39657             this.cleanUpChildren(node);
39658             // inserts everything just before this node...
39659             while (node.childNodes.length) {
39660                 var cn = node.childNodes[0];
39661                 node.removeChild(cn);
39662                 node.parentNode.insertBefore(cn, node);
39663             }
39664             node.parentNode.removeChild(node);
39665             return;
39666         }
39667         
39668         if (!node.attributes || !node.attributes.length) {
39669             this.cleanUpChildren(node);
39670             return;
39671         }
39672         
39673         function cleanAttr(n,v)
39674         {
39675             
39676             if (v.match(/^\./) || v.match(/^\//)) {
39677                 return;
39678             }
39679             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39680                 return;
39681             }
39682             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39683             node.removeAttribute(n);
39684             
39685         }
39686         
39687         function cleanStyle(n,v)
39688         {
39689             if (v.match(/expression/)) { //XSS?? should we even bother..
39690                 node.removeAttribute(n);
39691                 return;
39692             }
39693             
39694             
39695             var parts = v.split(/;/);
39696             Roo.each(parts, function(p) {
39697                 p = p.replace(/\s+/g,'');
39698                 if (!p.length) {
39699                     return true;
39700                 }
39701                 var l = p.split(':').shift().replace(/\s+/g,'');
39702                 
39703                 // only allow 'c whitelisted system attributes'
39704                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39705                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39706                     node.removeAttribute(n);
39707                     return false;
39708                 }
39709                 return true;
39710             });
39711             
39712             
39713         }
39714         
39715         
39716         for (var i = node.attributes.length-1; i > -1 ; i--) {
39717             var a = node.attributes[i];
39718             //console.log(a);
39719             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39720                 node.removeAttribute(a.name);
39721                 return;
39722             }
39723             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39724                 cleanAttr(a.name,a.value); // fixme..
39725                 return;
39726             }
39727             if (a.name == 'style') {
39728                 cleanStyle(a.name,a.value);
39729             }
39730             /// clean up MS crap..
39731             // tecnically this should be a list of valid class'es..
39732             
39733             
39734             if (a.name == 'class') {
39735                 if (a.value.match(/^Mso/)) {
39736                     node.className = '';
39737                 }
39738                 
39739                 if (a.value.match(/body/)) {
39740                     node.className = '';
39741                 }
39742             }
39743             
39744             // style cleanup!?
39745             // class cleanup?
39746             
39747         }
39748         
39749         
39750         this.cleanUpChildren(node);
39751         
39752         
39753     }
39754     
39755     
39756     // hide stuff that is not compatible
39757     /**
39758      * @event blur
39759      * @hide
39760      */
39761     /**
39762      * @event change
39763      * @hide
39764      */
39765     /**
39766      * @event focus
39767      * @hide
39768      */
39769     /**
39770      * @event specialkey
39771      * @hide
39772      */
39773     /**
39774      * @cfg {String} fieldClass @hide
39775      */
39776     /**
39777      * @cfg {String} focusClass @hide
39778      */
39779     /**
39780      * @cfg {String} autoCreate @hide
39781      */
39782     /**
39783      * @cfg {String} inputType @hide
39784      */
39785     /**
39786      * @cfg {String} invalidClass @hide
39787      */
39788     /**
39789      * @cfg {String} invalidText @hide
39790      */
39791     /**
39792      * @cfg {String} msgFx @hide
39793      */
39794     /**
39795      * @cfg {String} validateOnBlur @hide
39796      */
39797 });
39798
39799 Roo.form.HtmlEditor.white = [
39800         'area', 'br', 'img', 'input', 'hr', 'wbr',
39801         
39802        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39803        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39804        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39805        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39806        'table',   'ul',         'xmp', 
39807        
39808        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39809       'thead',   'tr', 
39810      
39811       'dir', 'menu', 'ol', 'ul', 'dl',
39812        
39813       'embed',  'object'
39814 ];
39815
39816
39817 Roo.form.HtmlEditor.black = [
39818     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39819         'applet', // 
39820         'base',   'basefont', 'bgsound', 'blink',  'body', 
39821         'frame',  'frameset', 'head',    'html',   'ilayer', 
39822         'iframe', 'layer',  'link',     'meta',    'object',   
39823         'script', 'style' ,'title',  'xml' // clean later..
39824 ];
39825 Roo.form.HtmlEditor.clean = [
39826     'script', 'style', 'title', 'xml'
39827 ];
39828 Roo.form.HtmlEditor.remove = [
39829     'font'
39830 ];
39831 // attributes..
39832
39833 Roo.form.HtmlEditor.ablack = [
39834     'on'
39835 ];
39836     
39837 Roo.form.HtmlEditor.aclean = [ 
39838     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39839 ];
39840
39841 // protocols..
39842 Roo.form.HtmlEditor.pwhite= [
39843         'http',  'https',  'mailto'
39844 ];
39845
39846 // white listed style attributes.
39847 Roo.form.HtmlEditor.cwhite= [
39848         'text-align',
39849         'font-size'
39850 ];
39851
39852
39853 Roo.form.HtmlEditor.swapCodes   =[ 
39854     [    8211, "--" ], 
39855     [    8212, "--" ], 
39856     [    8216,  "'" ],  
39857     [    8217, "'" ],  
39858     [    8220, '"' ],  
39859     [    8221, '"' ],  
39860     [    8226, "*" ],  
39861     [    8230, "..." ]
39862 ]; 
39863
39864     // <script type="text/javascript">
39865 /*
39866  * Based on
39867  * Ext JS Library 1.1.1
39868  * Copyright(c) 2006-2007, Ext JS, LLC.
39869  *  
39870  
39871  */
39872
39873 /**
39874  * @class Roo.form.HtmlEditorToolbar1
39875  * Basic Toolbar
39876  * 
39877  * Usage:
39878  *
39879  new Roo.form.HtmlEditor({
39880     ....
39881     toolbars : [
39882         new Roo.form.HtmlEditorToolbar1({
39883             disable : { fonts: 1 , format: 1, ..., ... , ...],
39884             btns : [ .... ]
39885         })
39886     }
39887      
39888  * 
39889  * @cfg {Object} disable List of elements to disable..
39890  * @cfg {Array} btns List of additional buttons.
39891  * 
39892  * 
39893  * NEEDS Extra CSS? 
39894  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39895  */
39896  
39897 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39898 {
39899     
39900     Roo.apply(this, config);
39901     
39902     // default disabled, based on 'good practice'..
39903     this.disable = this.disable || {};
39904     Roo.applyIf(this.disable, {
39905         fontSize : true,
39906         colors : true,
39907         specialElements : true
39908     });
39909     
39910     
39911     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39912     // dont call parent... till later.
39913 }
39914
39915 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39916     
39917     tb: false,
39918     
39919     rendered: false,
39920     
39921     editor : false,
39922     /**
39923      * @cfg {Object} disable  List of toolbar elements to disable
39924          
39925      */
39926     disable : false,
39927       /**
39928      * @cfg {Array} fontFamilies An array of available font families
39929      */
39930     fontFamilies : [
39931         'Arial',
39932         'Courier New',
39933         'Tahoma',
39934         'Times New Roman',
39935         'Verdana'
39936     ],
39937     
39938     specialChars : [
39939            "&#169;",
39940           "&#174;",     
39941           "&#8482;",    
39942           "&#163;" ,    
39943          // "&#8212;",    
39944           "&#8230;",    
39945           "&#247;" ,    
39946         //  "&#225;" ,     ?? a acute?
39947            "&#8364;"    , //Euro
39948        //   "&#8220;"    ,
39949         //  "&#8221;"    ,
39950         //  "&#8226;"    ,
39951           "&#176;"  //   , // degrees
39952
39953          // "&#233;"     , // e ecute
39954          // "&#250;"     , // u ecute?
39955     ],
39956     
39957     specialElements : [
39958         {
39959             text: "Insert Table",
39960             xtype: 'MenuItem',
39961             xns : Roo.Menu,
39962             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39963                 
39964         },
39965         {    
39966             text: "Insert Image",
39967             xtype: 'MenuItem',
39968             xns : Roo.Menu,
39969             ihtml : '<img src="about:blank"/>'
39970             
39971         }
39972         
39973          
39974     ],
39975     
39976     
39977     inputElements : [ 
39978             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39979             "input:submit", "input:button", "select", "textarea", "label" ],
39980     formats : [
39981         ["p"] ,  
39982         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39983         ["pre"],[ "code"], 
39984         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39985     ],
39986      /**
39987      * @cfg {String} defaultFont default font to use.
39988      */
39989     defaultFont: 'tahoma',
39990    
39991     fontSelect : false,
39992     
39993     
39994     formatCombo : false,
39995     
39996     init : function(editor)
39997     {
39998         this.editor = editor;
39999         
40000         
40001         var fid = editor.frameId;
40002         var etb = this;
40003         function btn(id, toggle, handler){
40004             var xid = fid + '-'+ id ;
40005             return {
40006                 id : xid,
40007                 cmd : id,
40008                 cls : 'x-btn-icon x-edit-'+id,
40009                 enableToggle:toggle !== false,
40010                 scope: editor, // was editor...
40011                 handler:handler||editor.relayBtnCmd,
40012                 clickEvent:'mousedown',
40013                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40014                 tabIndex:-1
40015             };
40016         }
40017         
40018         
40019         
40020         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40021         this.tb = tb;
40022          // stop form submits
40023         tb.el.on('click', function(e){
40024             e.preventDefault(); // what does this do?
40025         });
40026
40027         if(!this.disable.font && !Roo.isSafari){
40028             /* why no safari for fonts
40029             editor.fontSelect = tb.el.createChild({
40030                 tag:'select',
40031                 tabIndex: -1,
40032                 cls:'x-font-select',
40033                 html: editor.createFontOptions()
40034             });
40035             editor.fontSelect.on('change', function(){
40036                 var font = editor.fontSelect.dom.value;
40037                 editor.relayCmd('fontname', font);
40038                 editor.deferFocus();
40039             }, editor);
40040             tb.add(
40041                 editor.fontSelect.dom,
40042                 '-'
40043             );
40044             */
40045         };
40046         if(!this.disable.formats){
40047             this.formatCombo = new Roo.form.ComboBox({
40048                 store: new Roo.data.SimpleStore({
40049                     id : 'tag',
40050                     fields: ['tag'],
40051                     data : this.formats // from states.js
40052                 }),
40053                 blockFocus : true,
40054                 //autoCreate : {tag: "div",  size: "20"},
40055                 displayField:'tag',
40056                 typeAhead: false,
40057                 mode: 'local',
40058                 editable : false,
40059                 triggerAction: 'all',
40060                 emptyText:'Add tag',
40061                 selectOnFocus:true,
40062                 width:135,
40063                 listeners : {
40064                     'select': function(c, r, i) {
40065                         editor.insertTag(r.get('tag'));
40066                         editor.focus();
40067                     }
40068                 }
40069
40070             });
40071             tb.addField(this.formatCombo);
40072             
40073         }
40074         
40075         if(!this.disable.format){
40076             tb.add(
40077                 btn('bold'),
40078                 btn('italic'),
40079                 btn('underline')
40080             );
40081         };
40082         if(!this.disable.fontSize){
40083             tb.add(
40084                 '-',
40085                 
40086                 
40087                 btn('increasefontsize', false, editor.adjustFont),
40088                 btn('decreasefontsize', false, editor.adjustFont)
40089             );
40090         };
40091         
40092         
40093         if(!this.disable.colors){
40094             tb.add(
40095                 '-', {
40096                     id:editor.frameId +'-forecolor',
40097                     cls:'x-btn-icon x-edit-forecolor',
40098                     clickEvent:'mousedown',
40099                     tooltip: this.buttonTips['forecolor'] || undefined,
40100                     tabIndex:-1,
40101                     menu : new Roo.menu.ColorMenu({
40102                         allowReselect: true,
40103                         focus: Roo.emptyFn,
40104                         value:'000000',
40105                         plain:true,
40106                         selectHandler: function(cp, color){
40107                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40108                             editor.deferFocus();
40109                         },
40110                         scope: editor,
40111                         clickEvent:'mousedown'
40112                     })
40113                 }, {
40114                     id:editor.frameId +'backcolor',
40115                     cls:'x-btn-icon x-edit-backcolor',
40116                     clickEvent:'mousedown',
40117                     tooltip: this.buttonTips['backcolor'] || undefined,
40118                     tabIndex:-1,
40119                     menu : new Roo.menu.ColorMenu({
40120                         focus: Roo.emptyFn,
40121                         value:'FFFFFF',
40122                         plain:true,
40123                         allowReselect: true,
40124                         selectHandler: function(cp, color){
40125                             if(Roo.isGecko){
40126                                 editor.execCmd('useCSS', false);
40127                                 editor.execCmd('hilitecolor', color);
40128                                 editor.execCmd('useCSS', true);
40129                                 editor.deferFocus();
40130                             }else{
40131                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40132                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40133                                 editor.deferFocus();
40134                             }
40135                         },
40136                         scope:editor,
40137                         clickEvent:'mousedown'
40138                     })
40139                 }
40140             );
40141         };
40142         // now add all the items...
40143         
40144
40145         if(!this.disable.alignments){
40146             tb.add(
40147                 '-',
40148                 btn('justifyleft'),
40149                 btn('justifycenter'),
40150                 btn('justifyright')
40151             );
40152         };
40153
40154         //if(!Roo.isSafari){
40155             if(!this.disable.links){
40156                 tb.add(
40157                     '-',
40158                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40159                 );
40160             };
40161
40162             if(!this.disable.lists){
40163                 tb.add(
40164                     '-',
40165                     btn('insertorderedlist'),
40166                     btn('insertunorderedlist')
40167                 );
40168             }
40169             if(!this.disable.sourceEdit){
40170                 tb.add(
40171                     '-',
40172                     btn('sourceedit', true, function(btn){
40173                         this.toggleSourceEdit(btn.pressed);
40174                     })
40175                 );
40176             }
40177         //}
40178         
40179         var smenu = { };
40180         // special menu.. - needs to be tidied up..
40181         if (!this.disable.special) {
40182             smenu = {
40183                 text: "&#169;",
40184                 cls: 'x-edit-none',
40185                 
40186                 menu : {
40187                     items : []
40188                 }
40189             };
40190             for (var i =0; i < this.specialChars.length; i++) {
40191                 smenu.menu.items.push({
40192                     
40193                     html: this.specialChars[i],
40194                     handler: function(a,b) {
40195                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40196                         
40197                     },
40198                     tabIndex:-1
40199                 });
40200             }
40201             
40202             
40203             tb.add(smenu);
40204             
40205             
40206         }
40207          
40208         if (!this.disable.specialElements) {
40209             var semenu = {
40210                 text: "Other;",
40211                 cls: 'x-edit-none',
40212                 menu : {
40213                     items : []
40214                 }
40215             };
40216             for (var i =0; i < this.specialElements.length; i++) {
40217                 semenu.menu.items.push(
40218                     Roo.apply({ 
40219                         handler: function(a,b) {
40220                             editor.insertAtCursor(this.ihtml);
40221                         }
40222                     }, this.specialElements[i])
40223                 );
40224                     
40225             }
40226             
40227             tb.add(semenu);
40228             
40229             
40230         }
40231          
40232         
40233         if (this.btns) {
40234             for(var i =0; i< this.btns.length;i++) {
40235                 var b = this.btns[i];
40236                 b.cls =  'x-edit-none';
40237                 b.scope = editor;
40238                 tb.add(b);
40239             }
40240         
40241         }
40242         
40243         
40244         
40245         // disable everything...
40246         
40247         this.tb.items.each(function(item){
40248            if(item.id != editor.frameId+ '-sourceedit'){
40249                 item.disable();
40250             }
40251         });
40252         this.rendered = true;
40253         
40254         // the all the btns;
40255         editor.on('editorevent', this.updateToolbar, this);
40256         // other toolbars need to implement this..
40257         //editor.on('editmodechange', this.updateToolbar, this);
40258     },
40259     
40260     
40261     
40262     /**
40263      * Protected method that will not generally be called directly. It triggers
40264      * a toolbar update by reading the markup state of the current selection in the editor.
40265      */
40266     updateToolbar: function(){
40267
40268         if(!this.editor.activated){
40269             this.editor.onFirstFocus();
40270             return;
40271         }
40272
40273         var btns = this.tb.items.map, 
40274             doc = this.editor.doc,
40275             frameId = this.editor.frameId;
40276
40277         if(!this.disable.font && !Roo.isSafari){
40278             /*
40279             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40280             if(name != this.fontSelect.dom.value){
40281                 this.fontSelect.dom.value = name;
40282             }
40283             */
40284         }
40285         if(!this.disable.format){
40286             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40287             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40288             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40289         }
40290         if(!this.disable.alignments){
40291             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40292             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40293             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40294         }
40295         if(!Roo.isSafari && !this.disable.lists){
40296             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40297             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40298         }
40299         
40300         var ans = this.editor.getAllAncestors();
40301         if (this.formatCombo) {
40302             
40303             
40304             var store = this.formatCombo.store;
40305             this.formatCombo.setValue("");
40306             for (var i =0; i < ans.length;i++) {
40307                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40308                     // select it..
40309                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40310                     break;
40311                 }
40312             }
40313         }
40314         
40315         
40316         
40317         // hides menus... - so this cant be on a menu...
40318         Roo.menu.MenuMgr.hideAll();
40319
40320         //this.editorsyncValue();
40321     },
40322    
40323     
40324     createFontOptions : function(){
40325         var buf = [], fs = this.fontFamilies, ff, lc;
40326         for(var i = 0, len = fs.length; i< len; i++){
40327             ff = fs[i];
40328             lc = ff.toLowerCase();
40329             buf.push(
40330                 '<option value="',lc,'" style="font-family:',ff,';"',
40331                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40332                     ff,
40333                 '</option>'
40334             );
40335         }
40336         return buf.join('');
40337     },
40338     
40339     toggleSourceEdit : function(sourceEditMode){
40340         if(sourceEditMode === undefined){
40341             sourceEditMode = !this.sourceEditMode;
40342         }
40343         this.sourceEditMode = sourceEditMode === true;
40344         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40345         // just toggle the button?
40346         if(btn.pressed !== this.editor.sourceEditMode){
40347             btn.toggle(this.editor.sourceEditMode);
40348             return;
40349         }
40350         
40351         if(this.sourceEditMode){
40352             this.tb.items.each(function(item){
40353                 if(item.cmd != 'sourceedit'){
40354                     item.disable();
40355                 }
40356             });
40357           
40358         }else{
40359             if(this.initialized){
40360                 this.tb.items.each(function(item){
40361                     item.enable();
40362                 });
40363             }
40364             
40365         }
40366         // tell the editor that it's been pressed..
40367         this.editor.toggleSourceEdit(sourceEditMode);
40368        
40369     },
40370      /**
40371      * Object collection of toolbar tooltips for the buttons in the editor. The key
40372      * is the command id associated with that button and the value is a valid QuickTips object.
40373      * For example:
40374 <pre><code>
40375 {
40376     bold : {
40377         title: 'Bold (Ctrl+B)',
40378         text: 'Make the selected text bold.',
40379         cls: 'x-html-editor-tip'
40380     },
40381     italic : {
40382         title: 'Italic (Ctrl+I)',
40383         text: 'Make the selected text italic.',
40384         cls: 'x-html-editor-tip'
40385     },
40386     ...
40387 </code></pre>
40388     * @type Object
40389      */
40390     buttonTips : {
40391         bold : {
40392             title: 'Bold (Ctrl+B)',
40393             text: 'Make the selected text bold.',
40394             cls: 'x-html-editor-tip'
40395         },
40396         italic : {
40397             title: 'Italic (Ctrl+I)',
40398             text: 'Make the selected text italic.',
40399             cls: 'x-html-editor-tip'
40400         },
40401         underline : {
40402             title: 'Underline (Ctrl+U)',
40403             text: 'Underline the selected text.',
40404             cls: 'x-html-editor-tip'
40405         },
40406         increasefontsize : {
40407             title: 'Grow Text',
40408             text: 'Increase the font size.',
40409             cls: 'x-html-editor-tip'
40410         },
40411         decreasefontsize : {
40412             title: 'Shrink Text',
40413             text: 'Decrease the font size.',
40414             cls: 'x-html-editor-tip'
40415         },
40416         backcolor : {
40417             title: 'Text Highlight Color',
40418             text: 'Change the background color of the selected text.',
40419             cls: 'x-html-editor-tip'
40420         },
40421         forecolor : {
40422             title: 'Font Color',
40423             text: 'Change the color of the selected text.',
40424             cls: 'x-html-editor-tip'
40425         },
40426         justifyleft : {
40427             title: 'Align Text Left',
40428             text: 'Align text to the left.',
40429             cls: 'x-html-editor-tip'
40430         },
40431         justifycenter : {
40432             title: 'Center Text',
40433             text: 'Center text in the editor.',
40434             cls: 'x-html-editor-tip'
40435         },
40436         justifyright : {
40437             title: 'Align Text Right',
40438             text: 'Align text to the right.',
40439             cls: 'x-html-editor-tip'
40440         },
40441         insertunorderedlist : {
40442             title: 'Bullet List',
40443             text: 'Start a bulleted list.',
40444             cls: 'x-html-editor-tip'
40445         },
40446         insertorderedlist : {
40447             title: 'Numbered List',
40448             text: 'Start a numbered list.',
40449             cls: 'x-html-editor-tip'
40450         },
40451         createlink : {
40452             title: 'Hyperlink',
40453             text: 'Make the selected text a hyperlink.',
40454             cls: 'x-html-editor-tip'
40455         },
40456         sourceedit : {
40457             title: 'Source Edit',
40458             text: 'Switch to source editing mode.',
40459             cls: 'x-html-editor-tip'
40460         }
40461     },
40462     // private
40463     onDestroy : function(){
40464         if(this.rendered){
40465             
40466             this.tb.items.each(function(item){
40467                 if(item.menu){
40468                     item.menu.removeAll();
40469                     if(item.menu.el){
40470                         item.menu.el.destroy();
40471                     }
40472                 }
40473                 item.destroy();
40474             });
40475              
40476         }
40477     },
40478     onFirstFocus: function() {
40479         this.tb.items.each(function(item){
40480            item.enable();
40481         });
40482     }
40483 });
40484
40485
40486
40487
40488 // <script type="text/javascript">
40489 /*
40490  * Based on
40491  * Ext JS Library 1.1.1
40492  * Copyright(c) 2006-2007, Ext JS, LLC.
40493  *  
40494  
40495  */
40496
40497  
40498 /**
40499  * @class Roo.form.HtmlEditor.ToolbarContext
40500  * Context Toolbar
40501  * 
40502  * Usage:
40503  *
40504  new Roo.form.HtmlEditor({
40505     ....
40506     toolbars : [
40507         { xtype: 'ToolbarStandard', styles : {} }
40508         { xtype: 'ToolbarContext', disable : {} }
40509     ]
40510 })
40511
40512      
40513  * 
40514  * @config : {Object} disable List of elements to disable.. (not done yet.)
40515  * @config : {Object} styles  Map of styles available.
40516  * 
40517  */
40518
40519 Roo.form.HtmlEditor.ToolbarContext = function(config)
40520 {
40521     
40522     Roo.apply(this, config);
40523     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40524     // dont call parent... till later.
40525     this.styles = this.styles || {};
40526 }
40527 Roo.form.HtmlEditor.ToolbarContext.types = {
40528     'IMG' : {
40529         width : {
40530             title: "Width",
40531             width: 40
40532         },
40533         height:  {
40534             title: "Height",
40535             width: 40
40536         },
40537         align: {
40538             title: "Align",
40539             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40540             width : 80
40541             
40542         },
40543         border: {
40544             title: "Border",
40545             width: 40
40546         },
40547         alt: {
40548             title: "Alt",
40549             width: 120
40550         },
40551         src : {
40552             title: "Src",
40553             width: 220
40554         }
40555         
40556     },
40557     'A' : {
40558         name : {
40559             title: "Name",
40560             width: 50
40561         },
40562         href:  {
40563             title: "Href",
40564             width: 220
40565         } // border?
40566         
40567     },
40568     'TABLE' : {
40569         rows : {
40570             title: "Rows",
40571             width: 20
40572         },
40573         cols : {
40574             title: "Cols",
40575             width: 20
40576         },
40577         width : {
40578             title: "Width",
40579             width: 40
40580         },
40581         height : {
40582             title: "Height",
40583             width: 40
40584         },
40585         border : {
40586             title: "Border",
40587             width: 20
40588         }
40589     },
40590     'TD' : {
40591         width : {
40592             title: "Width",
40593             width: 40
40594         },
40595         height : {
40596             title: "Height",
40597             width: 40
40598         },   
40599         align: {
40600             title: "Align",
40601             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40602             width: 80
40603         },
40604         valign: {
40605             title: "Valign",
40606             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40607             width: 80
40608         },
40609         colspan: {
40610             title: "Colspan",
40611             width: 20
40612             
40613         }
40614     },
40615     'INPUT' : {
40616         name : {
40617             title: "name",
40618             width: 120
40619         },
40620         value : {
40621             title: "Value",
40622             width: 120
40623         },
40624         width : {
40625             title: "Width",
40626             width: 40
40627         }
40628     },
40629     'LABEL' : {
40630         'for' : {
40631             title: "For",
40632             width: 120
40633         }
40634     },
40635     'TEXTAREA' : {
40636           name : {
40637             title: "name",
40638             width: 120
40639         },
40640         rows : {
40641             title: "Rows",
40642             width: 20
40643         },
40644         cols : {
40645             title: "Cols",
40646             width: 20
40647         }
40648     },
40649     'SELECT' : {
40650         name : {
40651             title: "name",
40652             width: 120
40653         },
40654         selectoptions : {
40655             title: "Options",
40656             width: 200
40657         }
40658     },
40659     
40660     // should we really allow this??
40661     // should this just be 
40662     'BODY' : {
40663         title : {
40664             title: "title",
40665             width: 200,
40666             disabled : true
40667         }
40668     },
40669     '*' : {
40670         // empty..
40671     }
40672 };
40673
40674
40675
40676 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40677     
40678     tb: false,
40679     
40680     rendered: false,
40681     
40682     editor : false,
40683     /**
40684      * @cfg {Object} disable  List of toolbar elements to disable
40685          
40686      */
40687     disable : false,
40688     /**
40689      * @cfg {Object} styles List of styles 
40690      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40691      *
40692      * These must be defined in the page, so they get rendered correctly..
40693      * .headline { }
40694      * TD.underline { }
40695      * 
40696      */
40697     styles : false,
40698     
40699     
40700     
40701     toolbars : false,
40702     
40703     init : function(editor)
40704     {
40705         this.editor = editor;
40706         
40707         
40708         var fid = editor.frameId;
40709         var etb = this;
40710         function btn(id, toggle, handler){
40711             var xid = fid + '-'+ id ;
40712             return {
40713                 id : xid,
40714                 cmd : id,
40715                 cls : 'x-btn-icon x-edit-'+id,
40716                 enableToggle:toggle !== false,
40717                 scope: editor, // was editor...
40718                 handler:handler||editor.relayBtnCmd,
40719                 clickEvent:'mousedown',
40720                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40721                 tabIndex:-1
40722             };
40723         }
40724         // create a new element.
40725         var wdiv = editor.wrap.createChild({
40726                 tag: 'div'
40727             }, editor.wrap.dom.firstChild.nextSibling, true);
40728         
40729         // can we do this more than once??
40730         
40731          // stop form submits
40732       
40733  
40734         // disable everything...
40735         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40736         this.toolbars = {};
40737            
40738         for (var i in  ty) {
40739           
40740             this.toolbars[i] = this.buildToolbar(ty[i],i);
40741         }
40742         this.tb = this.toolbars.BODY;
40743         this.tb.el.show();
40744         this.buildFooter();
40745         this.footer.show();
40746          
40747         this.rendered = true;
40748         
40749         // the all the btns;
40750         editor.on('editorevent', this.updateToolbar, this);
40751         // other toolbars need to implement this..
40752         //editor.on('editmodechange', this.updateToolbar, this);
40753     },
40754     
40755     
40756     
40757     /**
40758      * Protected method that will not generally be called directly. It triggers
40759      * a toolbar update by reading the markup state of the current selection in the editor.
40760      */
40761     updateToolbar: function(ignore_a,ignore_b,sel){
40762
40763         
40764         if(!this.editor.activated){
40765              this.editor.onFirstFocus();
40766             return;
40767         }
40768         var updateFooter = sel ? false : true;
40769         
40770         
40771         var ans = this.editor.getAllAncestors();
40772         
40773         // pick
40774         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40775         
40776         if (!sel) { 
40777             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40778             sel = sel ? sel : this.editor.doc.body;
40779             sel = sel.tagName.length ? sel : this.editor.doc.body;
40780             
40781         }
40782         // pick a menu that exists..
40783         var tn = sel.tagName.toUpperCase();
40784         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40785         
40786         tn = sel.tagName.toUpperCase();
40787         
40788         var lastSel = this.tb.selectedNode
40789         
40790         this.tb.selectedNode = sel;
40791         
40792         // if current menu does not match..
40793         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40794                 
40795             this.tb.el.hide();
40796             ///console.log("show: " + tn);
40797             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40798             this.tb.el.show();
40799             // update name
40800             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40801             
40802             
40803             // update attributes
40804             if (this.tb.fields) {
40805                 this.tb.fields.each(function(e) {
40806                    e.setValue(sel.getAttribute(e.name));
40807                 });
40808             }
40809             
40810             // update styles
40811             var st = this.tb.fields.item(0);
40812             st.store.removeAll();
40813             var cn = sel.className.split(/\s+/);
40814             
40815             var avs = [];
40816             if (this.styles['*']) {
40817                 
40818                 Roo.each(this.styles['*'], function(v) {
40819                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40820                 });
40821             }
40822             if (this.styles[tn]) { 
40823                 Roo.each(this.styles[tn], function(v) {
40824                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40825                 });
40826             }
40827             
40828             st.store.loadData(avs);
40829             st.collapse();
40830             st.setValue(cn);
40831             
40832             // flag our selected Node.
40833             this.tb.selectedNode = sel;
40834            
40835            
40836             Roo.menu.MenuMgr.hideAll();
40837
40838         }
40839         
40840         if (!updateFooter) {
40841             return;
40842         }
40843         // update the footer
40844         //
40845         var html = '';
40846         
40847         this.footerEls = ans.reverse();
40848         Roo.each(this.footerEls, function(a,i) {
40849             if (!a) { return; }
40850             html += html.length ? ' &gt; '  :  '';
40851             
40852             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40853             
40854         });
40855        
40856         // 
40857         var sz = this.footDisp.up('td').getSize();
40858         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40859         this.footDisp.dom.style.marginLeft = '5px';
40860         
40861         this.footDisp.dom.style.overflow = 'hidden';
40862         
40863         this.footDisp.dom.innerHTML = html;
40864             
40865         //this.editorsyncValue();
40866     },
40867    
40868        
40869     // private
40870     onDestroy : function(){
40871         if(this.rendered){
40872             
40873             this.tb.items.each(function(item){
40874                 if(item.menu){
40875                     item.menu.removeAll();
40876                     if(item.menu.el){
40877                         item.menu.el.destroy();
40878                     }
40879                 }
40880                 item.destroy();
40881             });
40882              
40883         }
40884     },
40885     onFirstFocus: function() {
40886         // need to do this for all the toolbars..
40887         this.tb.items.each(function(item){
40888            item.enable();
40889         });
40890     },
40891     buildToolbar: function(tlist, nm)
40892     {
40893         var editor = this.editor;
40894          // create a new element.
40895         var wdiv = editor.wrap.createChild({
40896                 tag: 'div'
40897             }, editor.wrap.dom.firstChild.nextSibling, true);
40898         
40899        
40900         var tb = new Roo.Toolbar(wdiv);
40901         // add the name..
40902         
40903         tb.add(nm+ ":&nbsp;");
40904         
40905         // styles...
40906         if (this.styles) {
40907             
40908             // this needs a multi-select checkbox...
40909             tb.addField( new Roo.form.ComboBox({
40910                 store: new Roo.data.SimpleStore({
40911                     id : 'val',
40912                     fields: ['val', 'selected'],
40913                     data : [] 
40914                 }),
40915                 name : 'className',
40916                 displayField:'val',
40917                 typeAhead: false,
40918                 mode: 'local',
40919                 editable : false,
40920                 triggerAction: 'all',
40921                 emptyText:'Select Style',
40922                 selectOnFocus:true,
40923                 width: 130,
40924                 listeners : {
40925                     'select': function(c, r, i) {
40926                         // initial support only for on class per el..
40927                         tb.selectedNode.className =  r ? r.get('val') : '';
40928                     }
40929                 }
40930     
40931             }));
40932         }
40933             
40934         
40935         
40936         for (var i in tlist) {
40937             
40938             var item = tlist[i];
40939             tb.add(item.title + ":&nbsp;");
40940             
40941             
40942             
40943             
40944             if (item.opts) {
40945                 // opts == pulldown..
40946                 tb.addField( new Roo.form.ComboBox({
40947                     store: new Roo.data.SimpleStore({
40948                         id : 'val',
40949                         fields: ['val'],
40950                         data : item.opts  
40951                     }),
40952                     name : i,
40953                     displayField:'val',
40954                     typeAhead: false,
40955                     mode: 'local',
40956                     editable : false,
40957                     triggerAction: 'all',
40958                     emptyText:'Select',
40959                     selectOnFocus:true,
40960                     width: item.width ? item.width  : 130,
40961                     listeners : {
40962                         'select': function(c, r, i) {
40963                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40964                         }
40965                     }
40966
40967                 }));
40968                 continue;
40969                     
40970                  
40971                 
40972                 tb.addField( new Roo.form.TextField({
40973                     name: i,
40974                     width: 100,
40975                     //allowBlank:false,
40976                     value: ''
40977                 }));
40978                 continue;
40979             }
40980             tb.addField( new Roo.form.TextField({
40981                 name: i,
40982                 width: item.width,
40983                 //allowBlank:true,
40984                 value: '',
40985                 listeners: {
40986                     'change' : function(f, nv, ov) {
40987                         tb.selectedNode.setAttribute(f.name, nv);
40988                     }
40989                 }
40990             }));
40991              
40992         }
40993         tb.el.on('click', function(e){
40994             e.preventDefault(); // what does this do?
40995         });
40996         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40997         tb.el.hide();
40998         tb.name = nm;
40999         // dont need to disable them... as they will get hidden
41000         return tb;
41001          
41002         
41003     },
41004     buildFooter : function()
41005     {
41006         
41007         var fel = this.editor.wrap.createChild();
41008         this.footer = new Roo.Toolbar(fel);
41009         // toolbar has scrolly on left / right?
41010         var footDisp= new Roo.Toolbar.Fill();
41011         var _t = this;
41012         this.footer.add(
41013             {
41014                 text : '&lt;',
41015                 xtype: 'Button',
41016                 handler : function() {
41017                     _t.footDisp.scrollTo('left',0,true)
41018                 }
41019             }
41020         );
41021         this.footer.add( footDisp );
41022         this.footer.add( 
41023             {
41024                 text : '&gt;',
41025                 xtype: 'Button',
41026                 handler : function() {
41027                     // no animation..
41028                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41029                 }
41030             }
41031         );
41032         var fel = Roo.get(footDisp.el);
41033         fel.addClass('x-editor-context');
41034         this.footDispWrap = fel; 
41035         this.footDispWrap.overflow  = 'hidden';
41036         
41037         this.footDisp = fel.createChild();
41038         this.footDispWrap.on('click', this.onContextClick, this)
41039         
41040         
41041     },
41042     onContextClick : function (ev,dom)
41043     {
41044         ev.preventDefault();
41045         var  cn = dom.className;
41046         Roo.log(cn);
41047         if (!cn.match(/x-ed-loc-/)) {
41048             return;
41049         }
41050         var n = cn.split('-').pop();
41051         var ans = this.footerEls;
41052         var sel = ans[n];
41053         
41054          // pick
41055         var range = this.editor.createRange();
41056         
41057         range.selectNodeContents(sel);
41058         //range.selectNode(sel);
41059         
41060         
41061         var selection = this.editor.getSelection();
41062         selection.removeAllRanges();
41063         selection.addRange(range);
41064         
41065         
41066         
41067         this.updateToolbar(null, null, sel);
41068         
41069         
41070     }
41071     
41072     
41073     
41074     
41075     
41076 });
41077
41078
41079
41080
41081
41082 /*
41083  * Based on:
41084  * Ext JS Library 1.1.1
41085  * Copyright(c) 2006-2007, Ext JS, LLC.
41086  *
41087  * Originally Released Under LGPL - original licence link has changed is not relivant.
41088  *
41089  * Fork - LGPL
41090  * <script type="text/javascript">
41091  */
41092  
41093 /**
41094  * @class Roo.form.BasicForm
41095  * @extends Roo.util.Observable
41096  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41097  * @constructor
41098  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41099  * @param {Object} config Configuration options
41100  */
41101 Roo.form.BasicForm = function(el, config){
41102     this.allItems = [];
41103     this.childForms = [];
41104     Roo.apply(this, config);
41105     /*
41106      * The Roo.form.Field items in this form.
41107      * @type MixedCollection
41108      */
41109      
41110      
41111     this.items = new Roo.util.MixedCollection(false, function(o){
41112         return o.id || (o.id = Roo.id());
41113     });
41114     this.addEvents({
41115         /**
41116          * @event beforeaction
41117          * Fires before any action is performed. Return false to cancel the action.
41118          * @param {Form} this
41119          * @param {Action} action The action to be performed
41120          */
41121         beforeaction: true,
41122         /**
41123          * @event actionfailed
41124          * Fires when an action fails.
41125          * @param {Form} this
41126          * @param {Action} action The action that failed
41127          */
41128         actionfailed : true,
41129         /**
41130          * @event actioncomplete
41131          * Fires when an action is completed.
41132          * @param {Form} this
41133          * @param {Action} action The action that completed
41134          */
41135         actioncomplete : true
41136     });
41137     if(el){
41138         this.initEl(el);
41139     }
41140     Roo.form.BasicForm.superclass.constructor.call(this);
41141 };
41142
41143 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41144     /**
41145      * @cfg {String} method
41146      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41147      */
41148     /**
41149      * @cfg {DataReader} reader
41150      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41151      * This is optional as there is built-in support for processing JSON.
41152      */
41153     /**
41154      * @cfg {DataReader} errorReader
41155      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41156      * This is completely optional as there is built-in support for processing JSON.
41157      */
41158     /**
41159      * @cfg {String} url
41160      * The URL to use for form actions if one isn't supplied in the action options.
41161      */
41162     /**
41163      * @cfg {Boolean} fileUpload
41164      * Set to true if this form is a file upload.
41165      */
41166      
41167     /**
41168      * @cfg {Object} baseParams
41169      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41170      */
41171      /**
41172      
41173     /**
41174      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41175      */
41176     timeout: 30,
41177
41178     // private
41179     activeAction : null,
41180
41181     /**
41182      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41183      * or setValues() data instead of when the form was first created.
41184      */
41185     trackResetOnLoad : false,
41186     
41187     
41188     /**
41189      * childForms - used for multi-tab forms
41190      * @type {Array}
41191      */
41192     childForms : false,
41193     
41194     /**
41195      * allItems - full list of fields.
41196      * @type {Array}
41197      */
41198     allItems : false,
41199     
41200     /**
41201      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41202      * element by passing it or its id or mask the form itself by passing in true.
41203      * @type Mixed
41204      */
41205     waitMsgTarget : false,
41206
41207     // private
41208     initEl : function(el){
41209         this.el = Roo.get(el);
41210         this.id = this.el.id || Roo.id();
41211         this.el.on('submit', this.onSubmit, this);
41212         this.el.addClass('x-form');
41213     },
41214
41215     // private
41216     onSubmit : function(e){
41217         e.stopEvent();
41218     },
41219
41220     /**
41221      * Returns true if client-side validation on the form is successful.
41222      * @return Boolean
41223      */
41224     isValid : function(){
41225         var valid = true;
41226         this.items.each(function(f){
41227            if(!f.validate()){
41228                valid = false;
41229            }
41230         });
41231         return valid;
41232     },
41233
41234     /**
41235      * Returns true if any fields in this form have changed since their original load.
41236      * @return Boolean
41237      */
41238     isDirty : function(){
41239         var dirty = false;
41240         this.items.each(function(f){
41241            if(f.isDirty()){
41242                dirty = true;
41243                return false;
41244            }
41245         });
41246         return dirty;
41247     },
41248
41249     /**
41250      * Performs a predefined action (submit or load) or custom actions you define on this form.
41251      * @param {String} actionName The name of the action type
41252      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41253      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41254      * accept other config options):
41255      * <pre>
41256 Property          Type             Description
41257 ----------------  ---------------  ----------------------------------------------------------------------------------
41258 url               String           The url for the action (defaults to the form's url)
41259 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41260 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41261 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41262                                    validate the form on the client (defaults to false)
41263      * </pre>
41264      * @return {BasicForm} this
41265      */
41266     doAction : function(action, options){
41267         if(typeof action == 'string'){
41268             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41269         }
41270         if(this.fireEvent('beforeaction', this, action) !== false){
41271             this.beforeAction(action);
41272             action.run.defer(100, action);
41273         }
41274         return this;
41275     },
41276
41277     /**
41278      * Shortcut to do a submit action.
41279      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41280      * @return {BasicForm} this
41281      */
41282     submit : function(options){
41283         this.doAction('submit', options);
41284         return this;
41285     },
41286
41287     /**
41288      * Shortcut to do a load action.
41289      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41290      * @return {BasicForm} this
41291      */
41292     load : function(options){
41293         this.doAction('load', options);
41294         return this;
41295     },
41296
41297     /**
41298      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41299      * @param {Record} record The record to edit
41300      * @return {BasicForm} this
41301      */
41302     updateRecord : function(record){
41303         record.beginEdit();
41304         var fs = record.fields;
41305         fs.each(function(f){
41306             var field = this.findField(f.name);
41307             if(field){
41308                 record.set(f.name, field.getValue());
41309             }
41310         }, this);
41311         record.endEdit();
41312         return this;
41313     },
41314
41315     /**
41316      * Loads an Roo.data.Record into this form.
41317      * @param {Record} record The record to load
41318      * @return {BasicForm} this
41319      */
41320     loadRecord : function(record){
41321         this.setValues(record.data);
41322         return this;
41323     },
41324
41325     // private
41326     beforeAction : function(action){
41327         var o = action.options;
41328         
41329        
41330         if(this.waitMsgTarget === true){
41331             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41332         }else if(this.waitMsgTarget){
41333             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41334             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41335         }else {
41336             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41337         }
41338          
41339     },
41340
41341     // private
41342     afterAction : function(action, success){
41343         this.activeAction = null;
41344         var o = action.options;
41345         
41346         if(this.waitMsgTarget === true){
41347             this.el.unmask();
41348         }else if(this.waitMsgTarget){
41349             this.waitMsgTarget.unmask();
41350         }else{
41351             Roo.MessageBox.updateProgress(1);
41352             Roo.MessageBox.hide();
41353         }
41354          
41355         if(success){
41356             if(o.reset){
41357                 this.reset();
41358             }
41359             Roo.callback(o.success, o.scope, [this, action]);
41360             this.fireEvent('actioncomplete', this, action);
41361             
41362         }else{
41363             Roo.callback(o.failure, o.scope, [this, action]);
41364             // show an error message if no failed handler is set..
41365             if (!this.hasListener('actionfailed')) {
41366                 Roo.MessageBox.alert("Error",
41367                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41368                         action.result.errorMsg :
41369                         "Saving Failed, please check your entries"
41370                 );
41371             }
41372             
41373             this.fireEvent('actionfailed', this, action);
41374         }
41375         
41376     },
41377
41378     /**
41379      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41380      * @param {String} id The value to search for
41381      * @return Field
41382      */
41383     findField : function(id){
41384         var field = this.items.get(id);
41385         if(!field){
41386             this.items.each(function(f){
41387                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41388                     field = f;
41389                     return false;
41390                 }
41391             });
41392         }
41393         return field || null;
41394     },
41395
41396     /**
41397      * Add a secondary form to this one, 
41398      * Used to provide tabbed forms. One form is primary, with hidden values 
41399      * which mirror the elements from the other forms.
41400      * 
41401      * @param {Roo.form.Form} form to add.
41402      * 
41403      */
41404     addForm : function(form)
41405     {
41406        
41407         if (this.childForms.indexOf(form) > -1) {
41408             // already added..
41409             return;
41410         }
41411         this.childForms.push(form);
41412         var n = '';
41413         Roo.each(form.allItems, function (fe) {
41414             
41415             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41416             if (this.findField(n)) { // already added..
41417                 return;
41418             }
41419             var add = new Roo.form.Hidden({
41420                 name : n
41421             });
41422             add.render(this.el);
41423             
41424             this.add( add );
41425         }, this);
41426         
41427     },
41428     /**
41429      * Mark fields in this form invalid in bulk.
41430      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41431      * @return {BasicForm} this
41432      */
41433     markInvalid : function(errors){
41434         if(errors instanceof Array){
41435             for(var i = 0, len = errors.length; i < len; i++){
41436                 var fieldError = errors[i];
41437                 var f = this.findField(fieldError.id);
41438                 if(f){
41439                     f.markInvalid(fieldError.msg);
41440                 }
41441             }
41442         }else{
41443             var field, id;
41444             for(id in errors){
41445                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41446                     field.markInvalid(errors[id]);
41447                 }
41448             }
41449         }
41450         Roo.each(this.childForms || [], function (f) {
41451             f.markInvalid(errors);
41452         });
41453         
41454         return this;
41455     },
41456
41457     /**
41458      * Set values for fields in this form in bulk.
41459      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41460      * @return {BasicForm} this
41461      */
41462     setValues : function(values){
41463         if(values instanceof Array){ // array of objects
41464             for(var i = 0, len = values.length; i < len; i++){
41465                 var v = values[i];
41466                 var f = this.findField(v.id);
41467                 if(f){
41468                     f.setValue(v.value);
41469                     if(this.trackResetOnLoad){
41470                         f.originalValue = f.getValue();
41471                     }
41472                 }
41473             }
41474         }else{ // object hash
41475             var field, id;
41476             for(id in values){
41477                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41478                     
41479                     if (field.setFromData && 
41480                         field.valueField && 
41481                         field.displayField &&
41482                         // combos' with local stores can 
41483                         // be queried via setValue()
41484                         // to set their value..
41485                         (field.store && !field.store.isLocal)
41486                         ) {
41487                         // it's a combo
41488                         var sd = { };
41489                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41490                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41491                         field.setFromData(sd);
41492                         
41493                     } else {
41494                         field.setValue(values[id]);
41495                     }
41496                     
41497                     
41498                     if(this.trackResetOnLoad){
41499                         field.originalValue = field.getValue();
41500                     }
41501                 }
41502             }
41503         }
41504          
41505         Roo.each(this.childForms || [], function (f) {
41506             f.setValues(values);
41507         });
41508                 
41509         return this;
41510     },
41511
41512     /**
41513      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41514      * they are returned as an array.
41515      * @param {Boolean} asString
41516      * @return {Object}
41517      */
41518     getValues : function(asString){
41519         if (this.childForms) {
41520             // copy values from the child forms
41521             Roo.each(this.childForms, function (f) {
41522                 this.setValues(f.getValues());
41523             }, this);
41524         }
41525         
41526         
41527         
41528         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41529         if(asString === true){
41530             return fs;
41531         }
41532         return Roo.urlDecode(fs);
41533     },
41534     
41535     /**
41536      * Returns the fields in this form as an object with key/value pairs. 
41537      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41538      * @return {Object}
41539      */
41540     getFieldValues : function(with_hidden)
41541     {
41542         if (this.childForms) {
41543             // copy values from the child forms
41544             // should this call getFieldValues - probably not as we do not currently copy
41545             // hidden fields when we generate..
41546             Roo.each(this.childForms, function (f) {
41547                 this.setValues(f.getValues());
41548             }, this);
41549         }
41550         
41551         var ret = {};
41552         this.items.each(function(f){
41553             if (!f.getName()) {
41554                 return;
41555             }
41556             var v = f.getValue();
41557             // not sure if this supported any more..
41558             if ((typeof(v) == 'object') && f.getRawValue) {
41559                 v = f.getRawValue() ; // dates..
41560             }
41561             // combo boxes where name != hiddenName...
41562             if (f.name != f.getName()) {
41563                 ret[f.name] = f.getRawValue();
41564             }
41565             ret[f.getName()] = v;
41566         });
41567         
41568         return ret;
41569     },
41570
41571     /**
41572      * Clears all invalid messages in this form.
41573      * @return {BasicForm} this
41574      */
41575     clearInvalid : function(){
41576         this.items.each(function(f){
41577            f.clearInvalid();
41578         });
41579         
41580         Roo.each(this.childForms || [], function (f) {
41581             f.clearInvalid();
41582         });
41583         
41584         
41585         return this;
41586     },
41587
41588     /**
41589      * Resets this form.
41590      * @return {BasicForm} this
41591      */
41592     reset : function(){
41593         this.items.each(function(f){
41594             f.reset();
41595         });
41596         
41597         Roo.each(this.childForms || [], function (f) {
41598             f.reset();
41599         });
41600        
41601         
41602         return this;
41603     },
41604
41605     /**
41606      * Add Roo.form components to this form.
41607      * @param {Field} field1
41608      * @param {Field} field2 (optional)
41609      * @param {Field} etc (optional)
41610      * @return {BasicForm} this
41611      */
41612     add : function(){
41613         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41614         return this;
41615     },
41616
41617
41618     /**
41619      * Removes a field from the items collection (does NOT remove its markup).
41620      * @param {Field} field
41621      * @return {BasicForm} this
41622      */
41623     remove : function(field){
41624         this.items.remove(field);
41625         return this;
41626     },
41627
41628     /**
41629      * Looks at the fields in this form, checks them for an id attribute,
41630      * and calls applyTo on the existing dom element with that id.
41631      * @return {BasicForm} this
41632      */
41633     render : function(){
41634         this.items.each(function(f){
41635             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41636                 f.applyTo(f.id);
41637             }
41638         });
41639         return this;
41640     },
41641
41642     /**
41643      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41644      * @param {Object} values
41645      * @return {BasicForm} this
41646      */
41647     applyToFields : function(o){
41648         this.items.each(function(f){
41649            Roo.apply(f, o);
41650         });
41651         return this;
41652     },
41653
41654     /**
41655      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41656      * @param {Object} values
41657      * @return {BasicForm} this
41658      */
41659     applyIfToFields : function(o){
41660         this.items.each(function(f){
41661            Roo.applyIf(f, o);
41662         });
41663         return this;
41664     }
41665 });
41666
41667 // back compat
41668 Roo.BasicForm = Roo.form.BasicForm;/*
41669  * Based on:
41670  * Ext JS Library 1.1.1
41671  * Copyright(c) 2006-2007, Ext JS, LLC.
41672  *
41673  * Originally Released Under LGPL - original licence link has changed is not relivant.
41674  *
41675  * Fork - LGPL
41676  * <script type="text/javascript">
41677  */
41678
41679 /**
41680  * @class Roo.form.Form
41681  * @extends Roo.form.BasicForm
41682  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41683  * @constructor
41684  * @param {Object} config Configuration options
41685  */
41686 Roo.form.Form = function(config){
41687     var xitems =  [];
41688     if (config.items) {
41689         xitems = config.items;
41690         delete config.items;
41691     }
41692    
41693     
41694     Roo.form.Form.superclass.constructor.call(this, null, config);
41695     this.url = this.url || this.action;
41696     if(!this.root){
41697         this.root = new Roo.form.Layout(Roo.applyIf({
41698             id: Roo.id()
41699         }, config));
41700     }
41701     this.active = this.root;
41702     /**
41703      * Array of all the buttons that have been added to this form via {@link addButton}
41704      * @type Array
41705      */
41706     this.buttons = [];
41707     this.allItems = [];
41708     this.addEvents({
41709         /**
41710          * @event clientvalidation
41711          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41712          * @param {Form} this
41713          * @param {Boolean} valid true if the form has passed client-side validation
41714          */
41715         clientvalidation: true,
41716         /**
41717          * @event rendered
41718          * Fires when the form is rendered
41719          * @param {Roo.form.Form} form
41720          */
41721         rendered : true
41722     });
41723     
41724     if (this.progressUrl) {
41725             // push a hidden field onto the list of fields..
41726             this.addxtype( {
41727                     xns: Roo.form, 
41728                     xtype : 'Hidden', 
41729                     name : 'UPLOAD_IDENTIFIER' 
41730             });
41731         }
41732         
41733     
41734     Roo.each(xitems, this.addxtype, this);
41735     
41736     
41737     
41738 };
41739
41740 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41741     /**
41742      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41743      */
41744     /**
41745      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41746      */
41747     /**
41748      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41749      */
41750     buttonAlign:'center',
41751
41752     /**
41753      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41754      */
41755     minButtonWidth:75,
41756
41757     /**
41758      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41759      * This property cascades to child containers if not set.
41760      */
41761     labelAlign:'left',
41762
41763     /**
41764      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41765      * fires a looping event with that state. This is required to bind buttons to the valid
41766      * state using the config value formBind:true on the button.
41767      */
41768     monitorValid : false,
41769
41770     /**
41771      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41772      */
41773     monitorPoll : 200,
41774     
41775     /**
41776      * @cfg {String} progressUrl - Url to return progress data 
41777      */
41778     
41779     progressUrl : false,
41780   
41781     /**
41782      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41783      * fields are added and the column is closed. If no fields are passed the column remains open
41784      * until end() is called.
41785      * @param {Object} config The config to pass to the column
41786      * @param {Field} field1 (optional)
41787      * @param {Field} field2 (optional)
41788      * @param {Field} etc (optional)
41789      * @return Column The column container object
41790      */
41791     column : function(c){
41792         var col = new Roo.form.Column(c);
41793         this.start(col);
41794         if(arguments.length > 1){ // duplicate code required because of Opera
41795             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41796             this.end();
41797         }
41798         return col;
41799     },
41800
41801     /**
41802      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41803      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41804      * until end() is called.
41805      * @param {Object} config The config to pass to the fieldset
41806      * @param {Field} field1 (optional)
41807      * @param {Field} field2 (optional)
41808      * @param {Field} etc (optional)
41809      * @return FieldSet The fieldset container object
41810      */
41811     fieldset : function(c){
41812         var fs = new Roo.form.FieldSet(c);
41813         this.start(fs);
41814         if(arguments.length > 1){ // duplicate code required because of Opera
41815             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41816             this.end();
41817         }
41818         return fs;
41819     },
41820
41821     /**
41822      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41823      * fields are added and the container is closed. If no fields are passed the container remains open
41824      * until end() is called.
41825      * @param {Object} config The config to pass to the Layout
41826      * @param {Field} field1 (optional)
41827      * @param {Field} field2 (optional)
41828      * @param {Field} etc (optional)
41829      * @return Layout The container object
41830      */
41831     container : function(c){
41832         var l = new Roo.form.Layout(c);
41833         this.start(l);
41834         if(arguments.length > 1){ // duplicate code required because of Opera
41835             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41836             this.end();
41837         }
41838         return l;
41839     },
41840
41841     /**
41842      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41843      * @param {Object} container A Roo.form.Layout or subclass of Layout
41844      * @return {Form} this
41845      */
41846     start : function(c){
41847         // cascade label info
41848         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41849         this.active.stack.push(c);
41850         c.ownerCt = this.active;
41851         this.active = c;
41852         return this;
41853     },
41854
41855     /**
41856      * Closes the current open container
41857      * @return {Form} this
41858      */
41859     end : function(){
41860         if(this.active == this.root){
41861             return this;
41862         }
41863         this.active = this.active.ownerCt;
41864         return this;
41865     },
41866
41867     /**
41868      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41869      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41870      * as the label of the field.
41871      * @param {Field} field1
41872      * @param {Field} field2 (optional)
41873      * @param {Field} etc. (optional)
41874      * @return {Form} this
41875      */
41876     add : function(){
41877         this.active.stack.push.apply(this.active.stack, arguments);
41878         this.allItems.push.apply(this.allItems,arguments);
41879         var r = [];
41880         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41881             if(a[i].isFormField){
41882                 r.push(a[i]);
41883             }
41884         }
41885         if(r.length > 0){
41886             Roo.form.Form.superclass.add.apply(this, r);
41887         }
41888         return this;
41889     },
41890     
41891
41892     
41893     
41894     
41895      /**
41896      * Find any element that has been added to a form, using it's ID or name
41897      * This can include framesets, columns etc. along with regular fields..
41898      * @param {String} id - id or name to find.
41899      
41900      * @return {Element} e - or false if nothing found.
41901      */
41902     findbyId : function(id)
41903     {
41904         var ret = false;
41905         if (!id) {
41906             return ret;
41907         }
41908         Roo.each(this.allItems, function(f){
41909             if (f.id == id || f.name == id ){
41910                 ret = f;
41911                 return false;
41912             }
41913         });
41914         return ret;
41915     },
41916
41917     
41918     
41919     /**
41920      * Render this form into the passed container. This should only be called once!
41921      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41922      * @return {Form} this
41923      */
41924     render : function(ct)
41925     {
41926         
41927         
41928         
41929         ct = Roo.get(ct);
41930         var o = this.autoCreate || {
41931             tag: 'form',
41932             method : this.method || 'POST',
41933             id : this.id || Roo.id()
41934         };
41935         this.initEl(ct.createChild(o));
41936
41937         this.root.render(this.el);
41938         
41939        
41940              
41941         this.items.each(function(f){
41942             f.render('x-form-el-'+f.id);
41943         });
41944
41945         if(this.buttons.length > 0){
41946             // tables are required to maintain order and for correct IE layout
41947             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41948                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41949                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41950             }}, null, true);
41951             var tr = tb.getElementsByTagName('tr')[0];
41952             for(var i = 0, len = this.buttons.length; i < len; i++) {
41953                 var b = this.buttons[i];
41954                 var td = document.createElement('td');
41955                 td.className = 'x-form-btn-td';
41956                 b.render(tr.appendChild(td));
41957             }
41958         }
41959         if(this.monitorValid){ // initialize after render
41960             this.startMonitoring();
41961         }
41962         this.fireEvent('rendered', this);
41963         return this;
41964     },
41965
41966     /**
41967      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41968      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41969      * object or a valid Roo.DomHelper element config
41970      * @param {Function} handler The function called when the button is clicked
41971      * @param {Object} scope (optional) The scope of the handler function
41972      * @return {Roo.Button}
41973      */
41974     addButton : function(config, handler, scope){
41975         var bc = {
41976             handler: handler,
41977             scope: scope,
41978             minWidth: this.minButtonWidth,
41979             hideParent:true
41980         };
41981         if(typeof config == "string"){
41982             bc.text = config;
41983         }else{
41984             Roo.apply(bc, config);
41985         }
41986         var btn = new Roo.Button(null, bc);
41987         this.buttons.push(btn);
41988         return btn;
41989     },
41990
41991      /**
41992      * Adds a series of form elements (using the xtype property as the factory method.
41993      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41994      * @param {Object} config 
41995      */
41996     
41997     addxtype : function()
41998     {
41999         var ar = Array.prototype.slice.call(arguments, 0);
42000         var ret = false;
42001         for(var i = 0; i < ar.length; i++) {
42002             if (!ar[i]) {
42003                 continue; // skip -- if this happends something invalid got sent, we 
42004                 // should ignore it, as basically that interface element will not show up
42005                 // and that should be pretty obvious!!
42006             }
42007             
42008             if (Roo.form[ar[i].xtype]) {
42009                 ar[i].form = this;
42010                 var fe = Roo.factory(ar[i], Roo.form);
42011                 if (!ret) {
42012                     ret = fe;
42013                 }
42014                 fe.form = this;
42015                 if (fe.store) {
42016                     fe.store.form = this;
42017                 }
42018                 if (fe.isLayout) {  
42019                          
42020                     this.start(fe);
42021                     this.allItems.push(fe);
42022                     if (fe.items && fe.addxtype) {
42023                         fe.addxtype.apply(fe, fe.items);
42024                         delete fe.items;
42025                     }
42026                      this.end();
42027                     continue;
42028                 }
42029                 
42030                 
42031                  
42032                 this.add(fe);
42033               //  console.log('adding ' + ar[i].xtype);
42034             }
42035             if (ar[i].xtype == 'Button') {  
42036                 //console.log('adding button');
42037                 //console.log(ar[i]);
42038                 this.addButton(ar[i]);
42039                 this.allItems.push(fe);
42040                 continue;
42041             }
42042             
42043             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42044                 alert('end is not supported on xtype any more, use items');
42045             //    this.end();
42046             //    //console.log('adding end');
42047             }
42048             
42049         }
42050         return ret;
42051     },
42052     
42053     /**
42054      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42055      * option "monitorValid"
42056      */
42057     startMonitoring : function(){
42058         if(!this.bound){
42059             this.bound = true;
42060             Roo.TaskMgr.start({
42061                 run : this.bindHandler,
42062                 interval : this.monitorPoll || 200,
42063                 scope: this
42064             });
42065         }
42066     },
42067
42068     /**
42069      * Stops monitoring of the valid state of this form
42070      */
42071     stopMonitoring : function(){
42072         this.bound = false;
42073     },
42074
42075     // private
42076     bindHandler : function(){
42077         if(!this.bound){
42078             return false; // stops binding
42079         }
42080         var valid = true;
42081         this.items.each(function(f){
42082             if(!f.isValid(true)){
42083                 valid = false;
42084                 return false;
42085             }
42086         });
42087         for(var i = 0, len = this.buttons.length; i < len; i++){
42088             var btn = this.buttons[i];
42089             if(btn.formBind === true && btn.disabled === valid){
42090                 btn.setDisabled(!valid);
42091             }
42092         }
42093         this.fireEvent('clientvalidation', this, valid);
42094     }
42095     
42096     
42097     
42098     
42099     
42100     
42101     
42102     
42103 });
42104
42105
42106 // back compat
42107 Roo.Form = Roo.form.Form;
42108 /*
42109  * Based on:
42110  * Ext JS Library 1.1.1
42111  * Copyright(c) 2006-2007, Ext JS, LLC.
42112  *
42113  * Originally Released Under LGPL - original licence link has changed is not relivant.
42114  *
42115  * Fork - LGPL
42116  * <script type="text/javascript">
42117  */
42118  
42119  /**
42120  * @class Roo.form.Action
42121  * Internal Class used to handle form actions
42122  * @constructor
42123  * @param {Roo.form.BasicForm} el The form element or its id
42124  * @param {Object} config Configuration options
42125  */
42126  
42127  
42128 // define the action interface
42129 Roo.form.Action = function(form, options){
42130     this.form = form;
42131     this.options = options || {};
42132 };
42133 /**
42134  * Client Validation Failed
42135  * @const 
42136  */
42137 Roo.form.Action.CLIENT_INVALID = 'client';
42138 /**
42139  * Server Validation Failed
42140  * @const 
42141  */
42142  Roo.form.Action.SERVER_INVALID = 'server';
42143  /**
42144  * Connect to Server Failed
42145  * @const 
42146  */
42147 Roo.form.Action.CONNECT_FAILURE = 'connect';
42148 /**
42149  * Reading Data from Server Failed
42150  * @const 
42151  */
42152 Roo.form.Action.LOAD_FAILURE = 'load';
42153
42154 Roo.form.Action.prototype = {
42155     type : 'default',
42156     failureType : undefined,
42157     response : undefined,
42158     result : undefined,
42159
42160     // interface method
42161     run : function(options){
42162
42163     },
42164
42165     // interface method
42166     success : function(response){
42167
42168     },
42169
42170     // interface method
42171     handleResponse : function(response){
42172
42173     },
42174
42175     // default connection failure
42176     failure : function(response){
42177         
42178         this.response = response;
42179         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42180         this.form.afterAction(this, false);
42181     },
42182
42183     processResponse : function(response){
42184         this.response = response;
42185         if(!response.responseText){
42186             return true;
42187         }
42188         this.result = this.handleResponse(response);
42189         return this.result;
42190     },
42191
42192     // utility functions used internally
42193     getUrl : function(appendParams){
42194         var url = this.options.url || this.form.url || this.form.el.dom.action;
42195         if(appendParams){
42196             var p = this.getParams();
42197             if(p){
42198                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42199             }
42200         }
42201         return url;
42202     },
42203
42204     getMethod : function(){
42205         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42206     },
42207
42208     getParams : function(){
42209         var bp = this.form.baseParams;
42210         var p = this.options.params;
42211         if(p){
42212             if(typeof p == "object"){
42213                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42214             }else if(typeof p == 'string' && bp){
42215                 p += '&' + Roo.urlEncode(bp);
42216             }
42217         }else if(bp){
42218             p = Roo.urlEncode(bp);
42219         }
42220         return p;
42221     },
42222
42223     createCallback : function(){
42224         return {
42225             success: this.success,
42226             failure: this.failure,
42227             scope: this,
42228             timeout: (this.form.timeout*1000),
42229             upload: this.form.fileUpload ? this.success : undefined
42230         };
42231     }
42232 };
42233
42234 Roo.form.Action.Submit = function(form, options){
42235     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42236 };
42237
42238 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42239     type : 'submit',
42240
42241     haveProgress : false,
42242     uploadComplete : false,
42243     
42244     // uploadProgress indicator.
42245     uploadProgress : function()
42246     {
42247         if (!this.form.progressUrl) {
42248             return;
42249         }
42250         
42251         if (!this.haveProgress) {
42252             Roo.MessageBox.progress("Uploading", "Uploading");
42253         }
42254         if (this.uploadComplete) {
42255            Roo.MessageBox.hide();
42256            return;
42257         }
42258         
42259         this.haveProgress = true;
42260    
42261         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42262         
42263         var c = new Roo.data.Connection();
42264         c.request({
42265             url : this.form.progressUrl,
42266             params: {
42267                 id : uid
42268             },
42269             method: 'GET',
42270             success : function(req){
42271                //console.log(data);
42272                 var rdata = false;
42273                 var edata;
42274                 try  {
42275                    rdata = Roo.decode(req.responseText)
42276                 } catch (e) {
42277                     Roo.log("Invalid data from server..");
42278                     Roo.log(edata);
42279                     return;
42280                 }
42281                 if (!rdata || !rdata.success) {
42282                     Roo.log(rdata);
42283                     return;
42284                 }
42285                 var data = rdata.data;
42286                 
42287                 if (this.uploadComplete) {
42288                    Roo.MessageBox.hide();
42289                    return;
42290                 }
42291                    
42292                 if (data){
42293                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42294                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42295                     );
42296                 }
42297                 this.uploadProgress.defer(2000,this);
42298             },
42299        
42300             failure: function(data) {
42301                 Roo.log('progress url failed ');
42302                 Roo.log(data);
42303             },
42304             scope : this
42305         });
42306            
42307     },
42308     
42309     
42310     run : function()
42311     {
42312         // run get Values on the form, so it syncs any secondary forms.
42313         this.form.getValues();
42314         
42315         var o = this.options;
42316         var method = this.getMethod();
42317         var isPost = method == 'POST';
42318         if(o.clientValidation === false || this.form.isValid()){
42319             
42320             if (this.form.progressUrl) {
42321                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42322                     (new Date() * 1) + '' + Math.random());
42323                     
42324             } 
42325             
42326             
42327             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42328                 form:this.form.el.dom,
42329                 url:this.getUrl(!isPost),
42330                 method: method,
42331                 params:isPost ? this.getParams() : null,
42332                 isUpload: this.form.fileUpload
42333             }));
42334             
42335             this.uploadProgress();
42336
42337         }else if (o.clientValidation !== false){ // client validation failed
42338             this.failureType = Roo.form.Action.CLIENT_INVALID;
42339             this.form.afterAction(this, false);
42340         }
42341     },
42342
42343     success : function(response)
42344     {
42345         this.uploadComplete= true;
42346         if (this.haveProgress) {
42347             Roo.MessageBox.hide();
42348         }
42349         
42350         
42351         var result = this.processResponse(response);
42352         if(result === true || result.success){
42353             this.form.afterAction(this, true);
42354             return;
42355         }
42356         if(result.errors){
42357             this.form.markInvalid(result.errors);
42358             this.failureType = Roo.form.Action.SERVER_INVALID;
42359         }
42360         this.form.afterAction(this, false);
42361     },
42362     failure : function(response)
42363     {
42364         this.uploadComplete= true;
42365         if (this.haveProgress) {
42366             Roo.MessageBox.hide();
42367         }
42368         
42369         this.response = response;
42370         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42371         this.form.afterAction(this, false);
42372     },
42373     
42374     handleResponse : function(response){
42375         if(this.form.errorReader){
42376             var rs = this.form.errorReader.read(response);
42377             var errors = [];
42378             if(rs.records){
42379                 for(var i = 0, len = rs.records.length; i < len; i++) {
42380                     var r = rs.records[i];
42381                     errors[i] = r.data;
42382                 }
42383             }
42384             if(errors.length < 1){
42385                 errors = null;
42386             }
42387             return {
42388                 success : rs.success,
42389                 errors : errors
42390             };
42391         }
42392         var ret = false;
42393         try {
42394             ret = Roo.decode(response.responseText);
42395         } catch (e) {
42396             ret = {
42397                 success: false,
42398                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42399                 errors : []
42400             };
42401         }
42402         return ret;
42403         
42404     }
42405 });
42406
42407
42408 Roo.form.Action.Load = function(form, options){
42409     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42410     this.reader = this.form.reader;
42411 };
42412
42413 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42414     type : 'load',
42415
42416     run : function(){
42417         
42418         Roo.Ajax.request(Roo.apply(
42419                 this.createCallback(), {
42420                     method:this.getMethod(),
42421                     url:this.getUrl(false),
42422                     params:this.getParams()
42423         }));
42424     },
42425
42426     success : function(response){
42427         
42428         var result = this.processResponse(response);
42429         if(result === true || !result.success || !result.data){
42430             this.failureType = Roo.form.Action.LOAD_FAILURE;
42431             this.form.afterAction(this, false);
42432             return;
42433         }
42434         this.form.clearInvalid();
42435         this.form.setValues(result.data);
42436         this.form.afterAction(this, true);
42437     },
42438
42439     handleResponse : function(response){
42440         if(this.form.reader){
42441             var rs = this.form.reader.read(response);
42442             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42443             return {
42444                 success : rs.success,
42445                 data : data
42446             };
42447         }
42448         return Roo.decode(response.responseText);
42449     }
42450 });
42451
42452 Roo.form.Action.ACTION_TYPES = {
42453     'load' : Roo.form.Action.Load,
42454     'submit' : Roo.form.Action.Submit
42455 };/*
42456  * Based on:
42457  * Ext JS Library 1.1.1
42458  * Copyright(c) 2006-2007, Ext JS, LLC.
42459  *
42460  * Originally Released Under LGPL - original licence link has changed is not relivant.
42461  *
42462  * Fork - LGPL
42463  * <script type="text/javascript">
42464  */
42465  
42466 /**
42467  * @class Roo.form.Layout
42468  * @extends Roo.Component
42469  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42470  * @constructor
42471  * @param {Object} config Configuration options
42472  */
42473 Roo.form.Layout = function(config){
42474     var xitems = [];
42475     if (config.items) {
42476         xitems = config.items;
42477         delete config.items;
42478     }
42479     Roo.form.Layout.superclass.constructor.call(this, config);
42480     this.stack = [];
42481     Roo.each(xitems, this.addxtype, this);
42482      
42483 };
42484
42485 Roo.extend(Roo.form.Layout, Roo.Component, {
42486     /**
42487      * @cfg {String/Object} autoCreate
42488      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42489      */
42490     /**
42491      * @cfg {String/Object/Function} style
42492      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42493      * a function which returns such a specification.
42494      */
42495     /**
42496      * @cfg {String} labelAlign
42497      * Valid values are "left," "top" and "right" (defaults to "left")
42498      */
42499     /**
42500      * @cfg {Number} labelWidth
42501      * Fixed width in pixels of all field labels (defaults to undefined)
42502      */
42503     /**
42504      * @cfg {Boolean} clear
42505      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42506      */
42507     clear : true,
42508     /**
42509      * @cfg {String} labelSeparator
42510      * The separator to use after field labels (defaults to ':')
42511      */
42512     labelSeparator : ':',
42513     /**
42514      * @cfg {Boolean} hideLabels
42515      * True to suppress the display of field labels in this layout (defaults to false)
42516      */
42517     hideLabels : false,
42518
42519     // private
42520     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42521     
42522     isLayout : true,
42523     
42524     // private
42525     onRender : function(ct, position){
42526         if(this.el){ // from markup
42527             this.el = Roo.get(this.el);
42528         }else {  // generate
42529             var cfg = this.getAutoCreate();
42530             this.el = ct.createChild(cfg, position);
42531         }
42532         if(this.style){
42533             this.el.applyStyles(this.style);
42534         }
42535         if(this.labelAlign){
42536             this.el.addClass('x-form-label-'+this.labelAlign);
42537         }
42538         if(this.hideLabels){
42539             this.labelStyle = "display:none";
42540             this.elementStyle = "padding-left:0;";
42541         }else{
42542             if(typeof this.labelWidth == 'number'){
42543                 this.labelStyle = "width:"+this.labelWidth+"px;";
42544                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42545             }
42546             if(this.labelAlign == 'top'){
42547                 this.labelStyle = "width:auto;";
42548                 this.elementStyle = "padding-left:0;";
42549             }
42550         }
42551         var stack = this.stack;
42552         var slen = stack.length;
42553         if(slen > 0){
42554             if(!this.fieldTpl){
42555                 var t = new Roo.Template(
42556                     '<div class="x-form-item {5}">',
42557                         '<label for="{0}" style="{2}">{1}{4}</label>',
42558                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42559                         '</div>',
42560                     '</div><div class="x-form-clear-left"></div>'
42561                 );
42562                 t.disableFormats = true;
42563                 t.compile();
42564                 Roo.form.Layout.prototype.fieldTpl = t;
42565             }
42566             for(var i = 0; i < slen; i++) {
42567                 if(stack[i].isFormField){
42568                     this.renderField(stack[i]);
42569                 }else{
42570                     this.renderComponent(stack[i]);
42571                 }
42572             }
42573         }
42574         if(this.clear){
42575             this.el.createChild({cls:'x-form-clear'});
42576         }
42577     },
42578
42579     // private
42580     renderField : function(f){
42581         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42582                f.id, //0
42583                f.fieldLabel, //1
42584                f.labelStyle||this.labelStyle||'', //2
42585                this.elementStyle||'', //3
42586                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42587                f.itemCls||this.itemCls||''  //5
42588        ], true).getPrevSibling());
42589     },
42590
42591     // private
42592     renderComponent : function(c){
42593         c.render(c.isLayout ? this.el : this.el.createChild());    
42594     },
42595     /**
42596      * Adds a object form elements (using the xtype property as the factory method.)
42597      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42598      * @param {Object} config 
42599      */
42600     addxtype : function(o)
42601     {
42602         // create the lement.
42603         o.form = this.form;
42604         var fe = Roo.factory(o, Roo.form);
42605         this.form.allItems.push(fe);
42606         this.stack.push(fe);
42607         
42608         if (fe.isFormField) {
42609             this.form.items.add(fe);
42610         }
42611          
42612         return fe;
42613     }
42614 });
42615
42616 /**
42617  * @class Roo.form.Column
42618  * @extends Roo.form.Layout
42619  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42620  * @constructor
42621  * @param {Object} config Configuration options
42622  */
42623 Roo.form.Column = function(config){
42624     Roo.form.Column.superclass.constructor.call(this, config);
42625 };
42626
42627 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42628     /**
42629      * @cfg {Number/String} width
42630      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42631      */
42632     /**
42633      * @cfg {String/Object} autoCreate
42634      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42635      */
42636
42637     // private
42638     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42639
42640     // private
42641     onRender : function(ct, position){
42642         Roo.form.Column.superclass.onRender.call(this, ct, position);
42643         if(this.width){
42644             this.el.setWidth(this.width);
42645         }
42646     }
42647 });
42648
42649
42650 /**
42651  * @class Roo.form.Row
42652  * @extends Roo.form.Layout
42653  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42654  * @constructor
42655  * @param {Object} config Configuration options
42656  */
42657
42658  
42659 Roo.form.Row = function(config){
42660     Roo.form.Row.superclass.constructor.call(this, config);
42661 };
42662  
42663 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42664       /**
42665      * @cfg {Number/String} width
42666      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42667      */
42668     /**
42669      * @cfg {Number/String} height
42670      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42671      */
42672     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42673     
42674     padWidth : 20,
42675     // private
42676     onRender : function(ct, position){
42677         //console.log('row render');
42678         if(!this.rowTpl){
42679             var t = new Roo.Template(
42680                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42681                     '<label for="{0}" style="{2}">{1}{4}</label>',
42682                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42683                     '</div>',
42684                 '</div>'
42685             );
42686             t.disableFormats = true;
42687             t.compile();
42688             Roo.form.Layout.prototype.rowTpl = t;
42689         }
42690         this.fieldTpl = this.rowTpl;
42691         
42692         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42693         var labelWidth = 100;
42694         
42695         if ((this.labelAlign != 'top')) {
42696             if (typeof this.labelWidth == 'number') {
42697                 labelWidth = this.labelWidth
42698             }
42699             this.padWidth =  20 + labelWidth;
42700             
42701         }
42702         
42703         Roo.form.Column.superclass.onRender.call(this, ct, position);
42704         if(this.width){
42705             this.el.setWidth(this.width);
42706         }
42707         if(this.height){
42708             this.el.setHeight(this.height);
42709         }
42710     },
42711     
42712     // private
42713     renderField : function(f){
42714         f.fieldEl = this.fieldTpl.append(this.el, [
42715                f.id, f.fieldLabel,
42716                f.labelStyle||this.labelStyle||'',
42717                this.elementStyle||'',
42718                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42719                f.itemCls||this.itemCls||'',
42720                f.width ? f.width + this.padWidth : 160 + this.padWidth
42721        ],true);
42722     }
42723 });
42724  
42725
42726 /**
42727  * @class Roo.form.FieldSet
42728  * @extends Roo.form.Layout
42729  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42730  * @constructor
42731  * @param {Object} config Configuration options
42732  */
42733 Roo.form.FieldSet = function(config){
42734     Roo.form.FieldSet.superclass.constructor.call(this, config);
42735 };
42736
42737 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42738     /**
42739      * @cfg {String} legend
42740      * The text to display as the legend for the FieldSet (defaults to '')
42741      */
42742     /**
42743      * @cfg {String/Object} autoCreate
42744      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42745      */
42746
42747     // private
42748     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42749
42750     // private
42751     onRender : function(ct, position){
42752         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42753         if(this.legend){
42754             this.setLegend(this.legend);
42755         }
42756     },
42757
42758     // private
42759     setLegend : function(text){
42760         if(this.rendered){
42761             this.el.child('legend').update(text);
42762         }
42763     }
42764 });/*
42765  * Based on:
42766  * Ext JS Library 1.1.1
42767  * Copyright(c) 2006-2007, Ext JS, LLC.
42768  *
42769  * Originally Released Under LGPL - original licence link has changed is not relivant.
42770  *
42771  * Fork - LGPL
42772  * <script type="text/javascript">
42773  */
42774 /**
42775  * @class Roo.form.VTypes
42776  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42777  * @singleton
42778  */
42779 Roo.form.VTypes = function(){
42780     // closure these in so they are only created once.
42781     var alpha = /^[a-zA-Z_]+$/;
42782     var alphanum = /^[a-zA-Z0-9_]+$/;
42783     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42784     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42785
42786     // All these messages and functions are configurable
42787     return {
42788         /**
42789          * The function used to validate email addresses
42790          * @param {String} value The email address
42791          */
42792         'email' : function(v){
42793             return email.test(v);
42794         },
42795         /**
42796          * The error text to display when the email validation function returns false
42797          * @type String
42798          */
42799         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42800         /**
42801          * The keystroke filter mask to be applied on email input
42802          * @type RegExp
42803          */
42804         'emailMask' : /[a-z0-9_\.\-@]/i,
42805
42806         /**
42807          * The function used to validate URLs
42808          * @param {String} value The URL
42809          */
42810         'url' : function(v){
42811             return url.test(v);
42812         },
42813         /**
42814          * The error text to display when the url validation function returns false
42815          * @type String
42816          */
42817         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42818         
42819         /**
42820          * The function used to validate alpha values
42821          * @param {String} value The value
42822          */
42823         'alpha' : function(v){
42824             return alpha.test(v);
42825         },
42826         /**
42827          * The error text to display when the alpha validation function returns false
42828          * @type String
42829          */
42830         'alphaText' : 'This field should only contain letters and _',
42831         /**
42832          * The keystroke filter mask to be applied on alpha input
42833          * @type RegExp
42834          */
42835         'alphaMask' : /[a-z_]/i,
42836
42837         /**
42838          * The function used to validate alphanumeric values
42839          * @param {String} value The value
42840          */
42841         'alphanum' : function(v){
42842             return alphanum.test(v);
42843         },
42844         /**
42845          * The error text to display when the alphanumeric validation function returns false
42846          * @type String
42847          */
42848         'alphanumText' : 'This field should only contain letters, numbers and _',
42849         /**
42850          * The keystroke filter mask to be applied on alphanumeric input
42851          * @type RegExp
42852          */
42853         'alphanumMask' : /[a-z0-9_]/i
42854     };
42855 }();//<script type="text/javascript">
42856
42857 /**
42858  * @class Roo.form.FCKeditor
42859  * @extends Roo.form.TextArea
42860  * Wrapper around the FCKEditor http://www.fckeditor.net
42861  * @constructor
42862  * Creates a new FCKeditor
42863  * @param {Object} config Configuration options
42864  */
42865 Roo.form.FCKeditor = function(config){
42866     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42867     this.addEvents({
42868          /**
42869          * @event editorinit
42870          * Fired when the editor is initialized - you can add extra handlers here..
42871          * @param {FCKeditor} this
42872          * @param {Object} the FCK object.
42873          */
42874         editorinit : true
42875     });
42876     
42877     
42878 };
42879 Roo.form.FCKeditor.editors = { };
42880 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42881 {
42882     //defaultAutoCreate : {
42883     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42884     //},
42885     // private
42886     /**
42887      * @cfg {Object} fck options - see fck manual for details.
42888      */
42889     fckconfig : false,
42890     
42891     /**
42892      * @cfg {Object} fck toolbar set (Basic or Default)
42893      */
42894     toolbarSet : 'Basic',
42895     /**
42896      * @cfg {Object} fck BasePath
42897      */ 
42898     basePath : '/fckeditor/',
42899     
42900     
42901     frame : false,
42902     
42903     value : '',
42904     
42905    
42906     onRender : function(ct, position)
42907     {
42908         if(!this.el){
42909             this.defaultAutoCreate = {
42910                 tag: "textarea",
42911                 style:"width:300px;height:60px;",
42912                 autocomplete: "off"
42913             };
42914         }
42915         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42916         /*
42917         if(this.grow){
42918             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42919             if(this.preventScrollbars){
42920                 this.el.setStyle("overflow", "hidden");
42921             }
42922             this.el.setHeight(this.growMin);
42923         }
42924         */
42925         //console.log('onrender' + this.getId() );
42926         Roo.form.FCKeditor.editors[this.getId()] = this;
42927          
42928
42929         this.replaceTextarea() ;
42930         
42931     },
42932     
42933     getEditor : function() {
42934         return this.fckEditor;
42935     },
42936     /**
42937      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42938      * @param {Mixed} value The value to set
42939      */
42940     
42941     
42942     setValue : function(value)
42943     {
42944         //console.log('setValue: ' + value);
42945         
42946         if(typeof(value) == 'undefined') { // not sure why this is happending...
42947             return;
42948         }
42949         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42950         
42951         //if(!this.el || !this.getEditor()) {
42952         //    this.value = value;
42953             //this.setValue.defer(100,this,[value]);    
42954         //    return;
42955         //} 
42956         
42957         if(!this.getEditor()) {
42958             return;
42959         }
42960         
42961         this.getEditor().SetData(value);
42962         
42963         //
42964
42965     },
42966
42967     /**
42968      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42969      * @return {Mixed} value The field value
42970      */
42971     getValue : function()
42972     {
42973         
42974         if (this.frame && this.frame.dom.style.display == 'none') {
42975             return Roo.form.FCKeditor.superclass.getValue.call(this);
42976         }
42977         
42978         if(!this.el || !this.getEditor()) {
42979            
42980            // this.getValue.defer(100,this); 
42981             return this.value;
42982         }
42983        
42984         
42985         var value=this.getEditor().GetData();
42986         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42987         return Roo.form.FCKeditor.superclass.getValue.call(this);
42988         
42989
42990     },
42991
42992     /**
42993      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42994      * @return {Mixed} value The field value
42995      */
42996     getRawValue : function()
42997     {
42998         if (this.frame && this.frame.dom.style.display == 'none') {
42999             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43000         }
43001         
43002         if(!this.el || !this.getEditor()) {
43003             //this.getRawValue.defer(100,this); 
43004             return this.value;
43005             return;
43006         }
43007         
43008         
43009         
43010         var value=this.getEditor().GetData();
43011         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43012         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43013          
43014     },
43015     
43016     setSize : function(w,h) {
43017         
43018         
43019         
43020         //if (this.frame && this.frame.dom.style.display == 'none') {
43021         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43022         //    return;
43023         //}
43024         //if(!this.el || !this.getEditor()) {
43025         //    this.setSize.defer(100,this, [w,h]); 
43026         //    return;
43027         //}
43028         
43029         
43030         
43031         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43032         
43033         this.frame.dom.setAttribute('width', w);
43034         this.frame.dom.setAttribute('height', h);
43035         this.frame.setSize(w,h);
43036         
43037     },
43038     
43039     toggleSourceEdit : function(value) {
43040         
43041       
43042          
43043         this.el.dom.style.display = value ? '' : 'none';
43044         this.frame.dom.style.display = value ?  'none' : '';
43045         
43046     },
43047     
43048     
43049     focus: function(tag)
43050     {
43051         if (this.frame.dom.style.display == 'none') {
43052             return Roo.form.FCKeditor.superclass.focus.call(this);
43053         }
43054         if(!this.el || !this.getEditor()) {
43055             this.focus.defer(100,this, [tag]); 
43056             return;
43057         }
43058         
43059         
43060         
43061         
43062         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43063         this.getEditor().Focus();
43064         if (tgs.length) {
43065             if (!this.getEditor().Selection.GetSelection()) {
43066                 this.focus.defer(100,this, [tag]); 
43067                 return;
43068             }
43069             
43070             
43071             var r = this.getEditor().EditorDocument.createRange();
43072             r.setStart(tgs[0],0);
43073             r.setEnd(tgs[0],0);
43074             this.getEditor().Selection.GetSelection().removeAllRanges();
43075             this.getEditor().Selection.GetSelection().addRange(r);
43076             this.getEditor().Focus();
43077         }
43078         
43079     },
43080     
43081     
43082     
43083     replaceTextarea : function()
43084     {
43085         if ( document.getElementById( this.getId() + '___Frame' ) )
43086             return ;
43087         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43088         //{
43089             // We must check the elements firstly using the Id and then the name.
43090         var oTextarea = document.getElementById( this.getId() );
43091         
43092         var colElementsByName = document.getElementsByName( this.getId() ) ;
43093          
43094         oTextarea.style.display = 'none' ;
43095
43096         if ( oTextarea.tabIndex ) {            
43097             this.TabIndex = oTextarea.tabIndex ;
43098         }
43099         
43100         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43101         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43102         this.frame = Roo.get(this.getId() + '___Frame')
43103     },
43104     
43105     _getConfigHtml : function()
43106     {
43107         var sConfig = '' ;
43108
43109         for ( var o in this.fckconfig ) {
43110             sConfig += sConfig.length > 0  ? '&amp;' : '';
43111             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43112         }
43113
43114         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43115     },
43116     
43117     
43118     _getIFrameHtml : function()
43119     {
43120         var sFile = 'fckeditor.html' ;
43121         /* no idea what this is about..
43122         try
43123         {
43124             if ( (/fcksource=true/i).test( window.top.location.search ) )
43125                 sFile = 'fckeditor.original.html' ;
43126         }
43127         catch (e) { 
43128         */
43129
43130         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43131         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43132         
43133         
43134         var html = '<iframe id="' + this.getId() +
43135             '___Frame" src="' + sLink +
43136             '" width="' + this.width +
43137             '" height="' + this.height + '"' +
43138             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43139             ' frameborder="0" scrolling="no"></iframe>' ;
43140
43141         return html ;
43142     },
43143     
43144     _insertHtmlBefore : function( html, element )
43145     {
43146         if ( element.insertAdjacentHTML )       {
43147             // IE
43148             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43149         } else { // Gecko
43150             var oRange = document.createRange() ;
43151             oRange.setStartBefore( element ) ;
43152             var oFragment = oRange.createContextualFragment( html );
43153             element.parentNode.insertBefore( oFragment, element ) ;
43154         }
43155     }
43156     
43157     
43158   
43159     
43160     
43161     
43162     
43163
43164 });
43165
43166 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43167
43168 function FCKeditor_OnComplete(editorInstance){
43169     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43170     f.fckEditor = editorInstance;
43171     //console.log("loaded");
43172     f.fireEvent('editorinit', f, editorInstance);
43173
43174   
43175
43176  
43177
43178
43179
43180
43181
43182
43183
43184
43185
43186
43187
43188
43189
43190
43191
43192 //<script type="text/javascript">
43193 /**
43194  * @class Roo.form.GridField
43195  * @extends Roo.form.Field
43196  * Embed a grid (or editable grid into a form)
43197  * STATUS ALPHA
43198  * 
43199  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43200  * it needs 
43201  * xgrid.store = Roo.data.Store
43202  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43203  * xgrid.store.reader = Roo.data.JsonReader 
43204  * 
43205  * 
43206  * @constructor
43207  * Creates a new GridField
43208  * @param {Object} config Configuration options
43209  */
43210 Roo.form.GridField = function(config){
43211     Roo.form.GridField.superclass.constructor.call(this, config);
43212      
43213 };
43214
43215 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43216     /**
43217      * @cfg {Number} width  - used to restrict width of grid..
43218      */
43219     width : 100,
43220     /**
43221      * @cfg {Number} height - used to restrict height of grid..
43222      */
43223     height : 50,
43224      /**
43225      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43226          * 
43227          *}
43228      */
43229     xgrid : false, 
43230     /**
43231      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43232      * {tag: "input", type: "checkbox", autocomplete: "off"})
43233      */
43234    // defaultAutoCreate : { tag: 'div' },
43235     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43236     /**
43237      * @cfg {String} addTitle Text to include for adding a title.
43238      */
43239     addTitle : false,
43240     //
43241     onResize : function(){
43242         Roo.form.Field.superclass.onResize.apply(this, arguments);
43243     },
43244
43245     initEvents : function(){
43246         // Roo.form.Checkbox.superclass.initEvents.call(this);
43247         // has no events...
43248        
43249     },
43250
43251
43252     getResizeEl : function(){
43253         return this.wrap;
43254     },
43255
43256     getPositionEl : function(){
43257         return this.wrap;
43258     },
43259
43260     // private
43261     onRender : function(ct, position){
43262         
43263         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43264         var style = this.style;
43265         delete this.style;
43266         
43267         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43268         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43269         this.viewEl = this.wrap.createChild({ tag: 'div' });
43270         if (style) {
43271             this.viewEl.applyStyles(style);
43272         }
43273         if (this.width) {
43274             this.viewEl.setWidth(this.width);
43275         }
43276         if (this.height) {
43277             this.viewEl.setHeight(this.height);
43278         }
43279         //if(this.inputValue !== undefined){
43280         //this.setValue(this.value);
43281         
43282         
43283         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43284         
43285         
43286         this.grid.render();
43287         this.grid.getDataSource().on('remove', this.refreshValue, this);
43288         this.grid.getDataSource().on('update', this.refreshValue, this);
43289         this.grid.on('afteredit', this.refreshValue, this);
43290  
43291     },
43292      
43293     
43294     /**
43295      * Sets the value of the item. 
43296      * @param {String} either an object  or a string..
43297      */
43298     setValue : function(v){
43299         //this.value = v;
43300         v = v || []; // empty set..
43301         // this does not seem smart - it really only affects memoryproxy grids..
43302         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43303             var ds = this.grid.getDataSource();
43304             // assumes a json reader..
43305             var data = {}
43306             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43307             ds.loadData( data);
43308         }
43309         // clear selection so it does not get stale.
43310         if (this.grid.sm) { 
43311             this.grid.sm.clearSelections();
43312         }
43313         
43314         Roo.form.GridField.superclass.setValue.call(this, v);
43315         this.refreshValue();
43316         // should load data in the grid really....
43317     },
43318     
43319     // private
43320     refreshValue: function() {
43321          var val = [];
43322         this.grid.getDataSource().each(function(r) {
43323             val.push(r.data);
43324         });
43325         this.el.dom.value = Roo.encode(val);
43326     }
43327     
43328      
43329     
43330     
43331 });/*
43332  * Based on:
43333  * Ext JS Library 1.1.1
43334  * Copyright(c) 2006-2007, Ext JS, LLC.
43335  *
43336  * Originally Released Under LGPL - original licence link has changed is not relivant.
43337  *
43338  * Fork - LGPL
43339  * <script type="text/javascript">
43340  */
43341 /**
43342  * @class Roo.form.DisplayField
43343  * @extends Roo.form.Field
43344  * A generic Field to display non-editable data.
43345  * @constructor
43346  * Creates a new Display Field item.
43347  * @param {Object} config Configuration options
43348  */
43349 Roo.form.DisplayField = function(config){
43350     Roo.form.DisplayField.superclass.constructor.call(this, config);
43351     
43352 };
43353
43354 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43355     inputType:      'hidden',
43356     allowBlank:     true,
43357     readOnly:         true,
43358     
43359  
43360     /**
43361      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43362      */
43363     focusClass : undefined,
43364     /**
43365      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43366      */
43367     fieldClass: 'x-form-field',
43368     
43369      /**
43370      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43371      */
43372     valueRenderer: undefined,
43373     
43374     width: 100,
43375     /**
43376      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43377      * {tag: "input", type: "checkbox", autocomplete: "off"})
43378      */
43379      
43380  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43381
43382     onResize : function(){
43383         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43384         
43385     },
43386
43387     initEvents : function(){
43388         // Roo.form.Checkbox.superclass.initEvents.call(this);
43389         // has no events...
43390        
43391     },
43392
43393
43394     getResizeEl : function(){
43395         return this.wrap;
43396     },
43397
43398     getPositionEl : function(){
43399         return this.wrap;
43400     },
43401
43402     // private
43403     onRender : function(ct, position){
43404         
43405         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43406         //if(this.inputValue !== undefined){
43407         this.wrap = this.el.wrap();
43408         
43409         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43410         
43411         if (this.bodyStyle) {
43412             this.viewEl.applyStyles(this.bodyStyle);
43413         }
43414         //this.viewEl.setStyle('padding', '2px');
43415         
43416         this.setValue(this.value);
43417         
43418     },
43419 /*
43420     // private
43421     initValue : Roo.emptyFn,
43422
43423   */
43424
43425         // private
43426     onClick : function(){
43427         
43428     },
43429
43430     /**
43431      * Sets the checked state of the checkbox.
43432      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43433      */
43434     setValue : function(v){
43435         this.value = v;
43436         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43437         // this might be called before we have a dom element..
43438         if (!this.viewEl) {
43439             return;
43440         }
43441         this.viewEl.dom.innerHTML = html;
43442         Roo.form.DisplayField.superclass.setValue.call(this, v);
43443
43444     }
43445 });/*
43446  * 
43447  * Licence- LGPL
43448  * 
43449  */
43450
43451 /**
43452  * @class Roo.form.DayPicker
43453  * @extends Roo.form.Field
43454  * A Day picker show [M] [T] [W] ....
43455  * @constructor
43456  * Creates a new Day Picker
43457  * @param {Object} config Configuration options
43458  */
43459 Roo.form.DayPicker= function(config){
43460     Roo.form.DayPicker.superclass.constructor.call(this, config);
43461      
43462 };
43463
43464 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43465     /**
43466      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43467      */
43468     focusClass : undefined,
43469     /**
43470      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43471      */
43472     fieldClass: "x-form-field",
43473    
43474     /**
43475      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43476      * {tag: "input", type: "checkbox", autocomplete: "off"})
43477      */
43478     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43479     
43480    
43481     actionMode : 'viewEl', 
43482     //
43483     // private
43484  
43485     inputType : 'hidden',
43486     
43487      
43488     inputElement: false, // real input element?
43489     basedOn: false, // ????
43490     
43491     isFormField: true, // not sure where this is needed!!!!
43492
43493     onResize : function(){
43494         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43495         if(!this.boxLabel){
43496             this.el.alignTo(this.wrap, 'c-c');
43497         }
43498     },
43499
43500     initEvents : function(){
43501         Roo.form.Checkbox.superclass.initEvents.call(this);
43502         this.el.on("click", this.onClick,  this);
43503         this.el.on("change", this.onClick,  this);
43504     },
43505
43506
43507     getResizeEl : function(){
43508         return this.wrap;
43509     },
43510
43511     getPositionEl : function(){
43512         return this.wrap;
43513     },
43514
43515     
43516     // private
43517     onRender : function(ct, position){
43518         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43519        
43520         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43521         
43522         var r1 = '<table><tr>';
43523         var r2 = '<tr class="x-form-daypick-icons">';
43524         for (var i=0; i < 7; i++) {
43525             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43526             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43527         }
43528         
43529         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43530         viewEl.select('img').on('click', this.onClick, this);
43531         this.viewEl = viewEl;   
43532         
43533         
43534         // this will not work on Chrome!!!
43535         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43536         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43537         
43538         
43539           
43540
43541     },
43542
43543     // private
43544     initValue : Roo.emptyFn,
43545
43546     /**
43547      * Returns the checked state of the checkbox.
43548      * @return {Boolean} True if checked, else false
43549      */
43550     getValue : function(){
43551         return this.el.dom.value;
43552         
43553     },
43554
43555         // private
43556     onClick : function(e){ 
43557         //this.setChecked(!this.checked);
43558         Roo.get(e.target).toggleClass('x-menu-item-checked');
43559         this.refreshValue();
43560         //if(this.el.dom.checked != this.checked){
43561         //    this.setValue(this.el.dom.checked);
43562        // }
43563     },
43564     
43565     // private
43566     refreshValue : function()
43567     {
43568         var val = '';
43569         this.viewEl.select('img',true).each(function(e,i,n)  {
43570             val += e.is(".x-menu-item-checked") ? String(n) : '';
43571         });
43572         this.setValue(val, true);
43573     },
43574
43575     /**
43576      * Sets the checked state of the checkbox.
43577      * On is always based on a string comparison between inputValue and the param.
43578      * @param {Boolean/String} value - the value to set 
43579      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43580      */
43581     setValue : function(v,suppressEvent){
43582         if (!this.el.dom) {
43583             return;
43584         }
43585         var old = this.el.dom.value ;
43586         this.el.dom.value = v;
43587         if (suppressEvent) {
43588             return ;
43589         }
43590          
43591         // update display..
43592         this.viewEl.select('img',true).each(function(e,i,n)  {
43593             
43594             var on = e.is(".x-menu-item-checked");
43595             var newv = v.indexOf(String(n)) > -1;
43596             if (on != newv) {
43597                 e.toggleClass('x-menu-item-checked');
43598             }
43599             
43600         });
43601         
43602         
43603         this.fireEvent('change', this, v, old);
43604         
43605         
43606     },
43607    
43608     // handle setting of hidden value by some other method!!?!?
43609     setFromHidden: function()
43610     {
43611         if(!this.el){
43612             return;
43613         }
43614         //console.log("SET FROM HIDDEN");
43615         //alert('setFrom hidden');
43616         this.setValue(this.el.dom.value);
43617     },
43618     
43619     onDestroy : function()
43620     {
43621         if(this.viewEl){
43622             Roo.get(this.viewEl).remove();
43623         }
43624          
43625         Roo.form.DayPicker.superclass.onDestroy.call(this);
43626     }
43627
43628 });/*
43629  * RooJS Library 1.1.1
43630  * Copyright(c) 2008-2011  Alan Knowles
43631  *
43632  * License - LGPL
43633  */
43634  
43635
43636 /**
43637  * @class Roo.form.ComboCheck
43638  * @extends Roo.form.ComboBox
43639  * A combobox for multiple select items.
43640  *
43641  * FIXME - could do with a reset button..
43642  * 
43643  * @constructor
43644  * Create a new ComboCheck
43645  * @param {Object} config Configuration options
43646  */
43647 Roo.form.ComboCheck = function(config){
43648     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43649     // should verify some data...
43650     // like
43651     // hiddenName = required..
43652     // displayField = required
43653     // valudField == required
43654     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43655     var _t = this;
43656     Roo.each(req, function(e) {
43657         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43658             throw "Roo.form.ComboCheck : missing value for: " + e;
43659         }
43660     });
43661     
43662     
43663 };
43664
43665 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43666      
43667      
43668     editable : false,
43669      
43670     selectedClass: 'x-menu-item-checked', 
43671     
43672     // private
43673     onRender : function(ct, position){
43674         var _t = this;
43675         
43676         
43677         
43678         if(!this.tpl){
43679             var cls = 'x-combo-list';
43680
43681             
43682             this.tpl =  new Roo.Template({
43683                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43684                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43685                    '<span>{' + this.displayField + '}</span>' +
43686                     '</div>' 
43687                 
43688             });
43689         }
43690  
43691         
43692         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43693         this.view.singleSelect = false;
43694         this.view.multiSelect = true;
43695         this.view.toggleSelect = true;
43696         this.pageTb.add(new Roo.Toolbar.Fill(), {
43697             
43698             text: 'Done',
43699             handler: function()
43700             {
43701                 _t.collapse();
43702             }
43703         });
43704     },
43705     
43706     onViewOver : function(e, t){
43707         // do nothing...
43708         return;
43709         
43710     },
43711     
43712     onViewClick : function(doFocus,index){
43713         return;
43714         
43715     },
43716     select: function () {
43717         //Roo.log("SELECT CALLED");
43718     },
43719      
43720     selectByValue : function(xv, scrollIntoView){
43721         var ar = this.getValueArray();
43722         var sels = [];
43723         
43724         Roo.each(ar, function(v) {
43725             if(v === undefined || v === null){
43726                 return;
43727             }
43728             var r = this.findRecord(this.valueField, v);
43729             if(r){
43730                 sels.push(this.store.indexOf(r))
43731                 
43732             }
43733         },this);
43734         this.view.select(sels);
43735         return false;
43736     },
43737     
43738     
43739     
43740     onSelect : function(record, index){
43741        // Roo.log("onselect Called");
43742        // this is only called by the clear button now..
43743         this.view.clearSelections();
43744         this.setValue('[]');
43745         if (this.value != this.valueBefore) {
43746             this.fireEvent('change', this, this.value, this.valueBefore);
43747         }
43748     },
43749     getValueArray : function()
43750     {
43751         var ar = [] ;
43752         
43753         try {
43754             //Roo.log(this.value);
43755             if (typeof(this.value) == 'undefined') {
43756                 return [];
43757             }
43758             var ar = Roo.decode(this.value);
43759             return  ar instanceof Array ? ar : []; //?? valid?
43760             
43761         } catch(e) {
43762             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43763             return [];
43764         }
43765          
43766     },
43767     expand : function ()
43768     {
43769         Roo.form.ComboCheck.superclass.expand.call(this);
43770         this.valueBefore = this.value;
43771         
43772
43773     },
43774     
43775     collapse : function(){
43776         Roo.form.ComboCheck.superclass.collapse.call(this);
43777         var sl = this.view.getSelectedIndexes();
43778         var st = this.store;
43779         var nv = [];
43780         var tv = [];
43781         var r;
43782         Roo.each(sl, function(i) {
43783             r = st.getAt(i);
43784             nv.push(r.get(this.valueField));
43785         },this);
43786         this.setValue(Roo.encode(nv));
43787         if (this.value != this.valueBefore) {
43788
43789             this.fireEvent('change', this, this.value, this.valueBefore);
43790         }
43791         
43792     },
43793     
43794     setValue : function(v){
43795         // Roo.log(v);
43796         this.value = v;
43797         
43798         var vals = this.getValueArray();
43799         var tv = [];
43800         Roo.each(vals, function(k) {
43801             var r = this.findRecord(this.valueField, k);
43802             if(r){
43803                 tv.push(r.data[this.displayField]);
43804             }else if(this.valueNotFoundText !== undefined){
43805                 tv.push( this.valueNotFoundText );
43806             }
43807         },this);
43808        // Roo.log(tv);
43809         
43810         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43811         this.hiddenField.value = v;
43812         this.value = v;
43813     }
43814     
43815 });//<script type="text/javasscript">
43816  
43817
43818 /**
43819  * @class Roo.DDView
43820  * A DnD enabled version of Roo.View.
43821  * @param {Element/String} container The Element in which to create the View.
43822  * @param {String} tpl The template string used to create the markup for each element of the View
43823  * @param {Object} config The configuration properties. These include all the config options of
43824  * {@link Roo.View} plus some specific to this class.<br>
43825  * <p>
43826  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43827  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43828  * <p>
43829  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43830 .x-view-drag-insert-above {
43831         border-top:1px dotted #3366cc;
43832 }
43833 .x-view-drag-insert-below {
43834         border-bottom:1px dotted #3366cc;
43835 }
43836 </code></pre>
43837  * 
43838  */
43839  
43840 Roo.DDView = function(container, tpl, config) {
43841     Roo.DDView.superclass.constructor.apply(this, arguments);
43842     this.getEl().setStyle("outline", "0px none");
43843     this.getEl().unselectable();
43844     if (this.dragGroup) {
43845                 this.setDraggable(this.dragGroup.split(","));
43846     }
43847     if (this.dropGroup) {
43848                 this.setDroppable(this.dropGroup.split(","));
43849     }
43850     if (this.deletable) {
43851         this.setDeletable();
43852     }
43853     this.isDirtyFlag = false;
43854         this.addEvents({
43855                 "drop" : true
43856         });
43857 };
43858
43859 Roo.extend(Roo.DDView, Roo.View, {
43860 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43861 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43862 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43863 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43864
43865         isFormField: true,
43866
43867         reset: Roo.emptyFn,
43868         
43869         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43870
43871         validate: function() {
43872                 return true;
43873         },
43874         
43875         destroy: function() {
43876                 this.purgeListeners();
43877                 this.getEl.removeAllListeners();
43878                 this.getEl().remove();
43879                 if (this.dragZone) {
43880                         if (this.dragZone.destroy) {
43881                                 this.dragZone.destroy();
43882                         }
43883                 }
43884                 if (this.dropZone) {
43885                         if (this.dropZone.destroy) {
43886                                 this.dropZone.destroy();
43887                         }
43888                 }
43889         },
43890
43891 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43892         getName: function() {
43893                 return this.name;
43894         },
43895
43896 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43897         setValue: function(v) {
43898                 if (!this.store) {
43899                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43900                 }
43901                 var data = {};
43902                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43903                 this.store.proxy = new Roo.data.MemoryProxy(data);
43904                 this.store.load();
43905         },
43906
43907 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43908         getValue: function() {
43909                 var result = '(';
43910                 this.store.each(function(rec) {
43911                         result += rec.id + ',';
43912                 });
43913                 return result.substr(0, result.length - 1) + ')';
43914         },
43915         
43916         getIds: function() {
43917                 var i = 0, result = new Array(this.store.getCount());
43918                 this.store.each(function(rec) {
43919                         result[i++] = rec.id;
43920                 });
43921                 return result;
43922         },
43923         
43924         isDirty: function() {
43925                 return this.isDirtyFlag;
43926         },
43927
43928 /**
43929  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43930  *      whole Element becomes the target, and this causes the drop gesture to append.
43931  */
43932     getTargetFromEvent : function(e) {
43933                 var target = e.getTarget();
43934                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43935                 target = target.parentNode;
43936                 }
43937                 if (!target) {
43938                         target = this.el.dom.lastChild || this.el.dom;
43939                 }
43940                 return target;
43941     },
43942
43943 /**
43944  *      Create the drag data which consists of an object which has the property "ddel" as
43945  *      the drag proxy element. 
43946  */
43947     getDragData : function(e) {
43948         var target = this.findItemFromChild(e.getTarget());
43949                 if(target) {
43950                         this.handleSelection(e);
43951                         var selNodes = this.getSelectedNodes();
43952             var dragData = {
43953                 source: this,
43954                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43955                 nodes: selNodes,
43956                 records: []
43957                         };
43958                         var selectedIndices = this.getSelectedIndexes();
43959                         for (var i = 0; i < selectedIndices.length; i++) {
43960                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43961                         }
43962                         if (selNodes.length == 1) {
43963                                 dragData.ddel = target.cloneNode(true); // the div element
43964                         } else {
43965                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43966                                 div.className = 'multi-proxy';
43967                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43968                                         div.appendChild(selNodes[i].cloneNode(true));
43969                                 }
43970                                 dragData.ddel = div;
43971                         }
43972             //console.log(dragData)
43973             //console.log(dragData.ddel.innerHTML)
43974                         return dragData;
43975                 }
43976         //console.log('nodragData')
43977                 return false;
43978     },
43979     
43980 /**     Specify to which ddGroup items in this DDView may be dragged. */
43981     setDraggable: function(ddGroup) {
43982         if (ddGroup instanceof Array) {
43983                 Roo.each(ddGroup, this.setDraggable, this);
43984                 return;
43985         }
43986         if (this.dragZone) {
43987                 this.dragZone.addToGroup(ddGroup);
43988         } else {
43989                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43990                                 containerScroll: true,
43991                                 ddGroup: ddGroup 
43992
43993                         });
43994 //                      Draggability implies selection. DragZone's mousedown selects the element.
43995                         if (!this.multiSelect) { this.singleSelect = true; }
43996
43997 //                      Wire the DragZone's handlers up to methods in *this*
43998                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43999                 }
44000     },
44001
44002 /**     Specify from which ddGroup this DDView accepts drops. */
44003     setDroppable: function(ddGroup) {
44004         if (ddGroup instanceof Array) {
44005                 Roo.each(ddGroup, this.setDroppable, this);
44006                 return;
44007         }
44008         if (this.dropZone) {
44009                 this.dropZone.addToGroup(ddGroup);
44010         } else {
44011                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44012                                 containerScroll: true,
44013                                 ddGroup: ddGroup
44014                         });
44015
44016 //                      Wire the DropZone's handlers up to methods in *this*
44017                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44018                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44019                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44020                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44021                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44022                 }
44023     },
44024
44025 /**     Decide whether to drop above or below a View node. */
44026     getDropPoint : function(e, n, dd){
44027         if (n == this.el.dom) { return "above"; }
44028                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44029                 var c = t + (b - t) / 2;
44030                 var y = Roo.lib.Event.getPageY(e);
44031                 if(y <= c) {
44032                         return "above";
44033                 }else{
44034                         return "below";
44035                 }
44036     },
44037
44038     onNodeEnter : function(n, dd, e, data){
44039                 return false;
44040     },
44041     
44042     onNodeOver : function(n, dd, e, data){
44043                 var pt = this.getDropPoint(e, n, dd);
44044                 // set the insert point style on the target node
44045                 var dragElClass = this.dropNotAllowed;
44046                 if (pt) {
44047                         var targetElClass;
44048                         if (pt == "above"){
44049                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44050                                 targetElClass = "x-view-drag-insert-above";
44051                         } else {
44052                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44053                                 targetElClass = "x-view-drag-insert-below";
44054                         }
44055                         if (this.lastInsertClass != targetElClass){
44056                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44057                                 this.lastInsertClass = targetElClass;
44058                         }
44059                 }
44060                 return dragElClass;
44061         },
44062
44063     onNodeOut : function(n, dd, e, data){
44064                 this.removeDropIndicators(n);
44065     },
44066
44067     onNodeDrop : function(n, dd, e, data){
44068         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44069                 return false;
44070         }
44071         var pt = this.getDropPoint(e, n, dd);
44072                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44073                 if (pt == "below") { insertAt++; }
44074                 for (var i = 0; i < data.records.length; i++) {
44075                         var r = data.records[i];
44076                         var dup = this.store.getById(r.id);
44077                         if (dup && (dd != this.dragZone)) {
44078                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44079                         } else {
44080                                 if (data.copy) {
44081                                         this.store.insert(insertAt++, r.copy());
44082                                 } else {
44083                                         data.source.isDirtyFlag = true;
44084                                         r.store.remove(r);
44085                                         this.store.insert(insertAt++, r);
44086                                 }
44087                                 this.isDirtyFlag = true;
44088                         }
44089                 }
44090                 this.dragZone.cachedTarget = null;
44091                 return true;
44092     },
44093
44094     removeDropIndicators : function(n){
44095                 if(n){
44096                         Roo.fly(n).removeClass([
44097                                 "x-view-drag-insert-above",
44098                                 "x-view-drag-insert-below"]);
44099                         this.lastInsertClass = "_noclass";
44100                 }
44101     },
44102
44103 /**
44104  *      Utility method. Add a delete option to the DDView's context menu.
44105  *      @param {String} imageUrl The URL of the "delete" icon image.
44106  */
44107         setDeletable: function(imageUrl) {
44108                 if (!this.singleSelect && !this.multiSelect) {
44109                         this.singleSelect = true;
44110                 }
44111                 var c = this.getContextMenu();
44112                 this.contextMenu.on("itemclick", function(item) {
44113                         switch (item.id) {
44114                                 case "delete":
44115                                         this.remove(this.getSelectedIndexes());
44116                                         break;
44117                         }
44118                 }, this);
44119                 this.contextMenu.add({
44120                         icon: imageUrl,
44121                         id: "delete",
44122                         text: 'Delete'
44123                 });
44124         },
44125         
44126 /**     Return the context menu for this DDView. */
44127         getContextMenu: function() {
44128                 if (!this.contextMenu) {
44129 //                      Create the View's context menu
44130                         this.contextMenu = new Roo.menu.Menu({
44131                                 id: this.id + "-contextmenu"
44132                         });
44133                         this.el.on("contextmenu", this.showContextMenu, this);
44134                 }
44135                 return this.contextMenu;
44136         },
44137         
44138         disableContextMenu: function() {
44139                 if (this.contextMenu) {
44140                         this.el.un("contextmenu", this.showContextMenu, this);
44141                 }
44142         },
44143
44144         showContextMenu: function(e, item) {
44145         item = this.findItemFromChild(e.getTarget());
44146                 if (item) {
44147                         e.stopEvent();
44148                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44149                         this.contextMenu.showAt(e.getXY());
44150             }
44151     },
44152
44153 /**
44154  *      Remove {@link Roo.data.Record}s at the specified indices.
44155  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44156  */
44157     remove: function(selectedIndices) {
44158                 selectedIndices = [].concat(selectedIndices);
44159                 for (var i = 0; i < selectedIndices.length; i++) {
44160                         var rec = this.store.getAt(selectedIndices[i]);
44161                         this.store.remove(rec);
44162                 }
44163     },
44164
44165 /**
44166  *      Double click fires the event, but also, if this is draggable, and there is only one other
44167  *      related DropZone, it transfers the selected node.
44168  */
44169     onDblClick : function(e){
44170         var item = this.findItemFromChild(e.getTarget());
44171         if(item){
44172             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44173                 return false;
44174             }
44175             if (this.dragGroup) {
44176                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44177                     while (targets.indexOf(this.dropZone) > -1) {
44178                             targets.remove(this.dropZone);
44179                                 }
44180                     if (targets.length == 1) {
44181                                         this.dragZone.cachedTarget = null;
44182                         var el = Roo.get(targets[0].getEl());
44183                         var box = el.getBox(true);
44184                         targets[0].onNodeDrop(el.dom, {
44185                                 target: el.dom,
44186                                 xy: [box.x, box.y + box.height - 1]
44187                         }, null, this.getDragData(e));
44188                     }
44189                 }
44190         }
44191     },
44192     
44193     handleSelection: function(e) {
44194                 this.dragZone.cachedTarget = null;
44195         var item = this.findItemFromChild(e.getTarget());
44196         if (!item) {
44197                 this.clearSelections(true);
44198                 return;
44199         }
44200                 if (item && (this.multiSelect || this.singleSelect)){
44201                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44202                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44203                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44204                                 this.unselect(item);
44205                         } else {
44206                                 this.select(item, this.multiSelect && e.ctrlKey);
44207                                 this.lastSelection = item;
44208                         }
44209                 }
44210     },
44211
44212     onItemClick : function(item, index, e){
44213                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44214                         return false;
44215                 }
44216                 return true;
44217     },
44218
44219     unselect : function(nodeInfo, suppressEvent){
44220                 var node = this.getNode(nodeInfo);
44221                 if(node && this.isSelected(node)){
44222                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44223                                 Roo.fly(node).removeClass(this.selectedClass);
44224                                 this.selections.remove(node);
44225                                 if(!suppressEvent){
44226                                         this.fireEvent("selectionchange", this, this.selections);
44227                                 }
44228                         }
44229                 }
44230     }
44231 });
44232 /*
44233  * Based on:
44234  * Ext JS Library 1.1.1
44235  * Copyright(c) 2006-2007, Ext JS, LLC.
44236  *
44237  * Originally Released Under LGPL - original licence link has changed is not relivant.
44238  *
44239  * Fork - LGPL
44240  * <script type="text/javascript">
44241  */
44242  
44243 /**
44244  * @class Roo.LayoutManager
44245  * @extends Roo.util.Observable
44246  * Base class for layout managers.
44247  */
44248 Roo.LayoutManager = function(container, config){
44249     Roo.LayoutManager.superclass.constructor.call(this);
44250     this.el = Roo.get(container);
44251     // ie scrollbar fix
44252     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44253         document.body.scroll = "no";
44254     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44255         this.el.position('relative');
44256     }
44257     this.id = this.el.id;
44258     this.el.addClass("x-layout-container");
44259     /** false to disable window resize monitoring @type Boolean */
44260     this.monitorWindowResize = true;
44261     this.regions = {};
44262     this.addEvents({
44263         /**
44264          * @event layout
44265          * Fires when a layout is performed. 
44266          * @param {Roo.LayoutManager} this
44267          */
44268         "layout" : true,
44269         /**
44270          * @event regionresized
44271          * Fires when the user resizes a region. 
44272          * @param {Roo.LayoutRegion} region The resized region
44273          * @param {Number} newSize The new size (width for east/west, height for north/south)
44274          */
44275         "regionresized" : true,
44276         /**
44277          * @event regioncollapsed
44278          * Fires when a region is collapsed. 
44279          * @param {Roo.LayoutRegion} region The collapsed region
44280          */
44281         "regioncollapsed" : true,
44282         /**
44283          * @event regionexpanded
44284          * Fires when a region is expanded.  
44285          * @param {Roo.LayoutRegion} region The expanded region
44286          */
44287         "regionexpanded" : true
44288     });
44289     this.updating = false;
44290     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44291 };
44292
44293 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44294     /**
44295      * Returns true if this layout is currently being updated
44296      * @return {Boolean}
44297      */
44298     isUpdating : function(){
44299         return this.updating; 
44300     },
44301     
44302     /**
44303      * Suspend the LayoutManager from doing auto-layouts while
44304      * making multiple add or remove calls
44305      */
44306     beginUpdate : function(){
44307         this.updating = true;    
44308     },
44309     
44310     /**
44311      * Restore auto-layouts and optionally disable the manager from performing a layout
44312      * @param {Boolean} noLayout true to disable a layout update 
44313      */
44314     endUpdate : function(noLayout){
44315         this.updating = false;
44316         if(!noLayout){
44317             this.layout();
44318         }    
44319     },
44320     
44321     layout: function(){
44322         
44323     },
44324     
44325     onRegionResized : function(region, newSize){
44326         this.fireEvent("regionresized", region, newSize);
44327         this.layout();
44328     },
44329     
44330     onRegionCollapsed : function(region){
44331         this.fireEvent("regioncollapsed", region);
44332     },
44333     
44334     onRegionExpanded : function(region){
44335         this.fireEvent("regionexpanded", region);
44336     },
44337         
44338     /**
44339      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44340      * performs box-model adjustments.
44341      * @return {Object} The size as an object {width: (the width), height: (the height)}
44342      */
44343     getViewSize : function(){
44344         var size;
44345         if(this.el.dom != document.body){
44346             size = this.el.getSize();
44347         }else{
44348             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44349         }
44350         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44351         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44352         return size;
44353     },
44354     
44355     /**
44356      * Returns the Element this layout is bound to.
44357      * @return {Roo.Element}
44358      */
44359     getEl : function(){
44360         return this.el;
44361     },
44362     
44363     /**
44364      * Returns the specified region.
44365      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44366      * @return {Roo.LayoutRegion}
44367      */
44368     getRegion : function(target){
44369         return this.regions[target.toLowerCase()];
44370     },
44371     
44372     onWindowResize : function(){
44373         if(this.monitorWindowResize){
44374             this.layout();
44375         }
44376     }
44377 });/*
44378  * Based on:
44379  * Ext JS Library 1.1.1
44380  * Copyright(c) 2006-2007, Ext JS, LLC.
44381  *
44382  * Originally Released Under LGPL - original licence link has changed is not relivant.
44383  *
44384  * Fork - LGPL
44385  * <script type="text/javascript">
44386  */
44387 /**
44388  * @class Roo.BorderLayout
44389  * @extends Roo.LayoutManager
44390  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44391  * please see: <br><br>
44392  * <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>
44393  * <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>
44394  * Example:
44395  <pre><code>
44396  var layout = new Roo.BorderLayout(document.body, {
44397     north: {
44398         initialSize: 25,
44399         titlebar: false
44400     },
44401     west: {
44402         split:true,
44403         initialSize: 200,
44404         minSize: 175,
44405         maxSize: 400,
44406         titlebar: true,
44407         collapsible: true
44408     },
44409     east: {
44410         split:true,
44411         initialSize: 202,
44412         minSize: 175,
44413         maxSize: 400,
44414         titlebar: true,
44415         collapsible: true
44416     },
44417     south: {
44418         split:true,
44419         initialSize: 100,
44420         minSize: 100,
44421         maxSize: 200,
44422         titlebar: true,
44423         collapsible: true
44424     },
44425     center: {
44426         titlebar: true,
44427         autoScroll:true,
44428         resizeTabs: true,
44429         minTabWidth: 50,
44430         preferredTabWidth: 150
44431     }
44432 });
44433
44434 // shorthand
44435 var CP = Roo.ContentPanel;
44436
44437 layout.beginUpdate();
44438 layout.add("north", new CP("north", "North"));
44439 layout.add("south", new CP("south", {title: "South", closable: true}));
44440 layout.add("west", new CP("west", {title: "West"}));
44441 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44442 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44443 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44444 layout.getRegion("center").showPanel("center1");
44445 layout.endUpdate();
44446 </code></pre>
44447
44448 <b>The container the layout is rendered into can be either the body element or any other element.
44449 If it is not the body element, the container needs to either be an absolute positioned element,
44450 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44451 the container size if it is not the body element.</b>
44452
44453 * @constructor
44454 * Create a new BorderLayout
44455 * @param {String/HTMLElement/Element} container The container this layout is bound to
44456 * @param {Object} config Configuration options
44457  */
44458 Roo.BorderLayout = function(container, config){
44459     config = config || {};
44460     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44461     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44462     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44463         var target = this.factory.validRegions[i];
44464         if(config[target]){
44465             this.addRegion(target, config[target]);
44466         }
44467     }
44468 };
44469
44470 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44471     /**
44472      * Creates and adds a new region if it doesn't already exist.
44473      * @param {String} target The target region key (north, south, east, west or center).
44474      * @param {Object} config The regions config object
44475      * @return {BorderLayoutRegion} The new region
44476      */
44477     addRegion : function(target, config){
44478         if(!this.regions[target]){
44479             var r = this.factory.create(target, this, config);
44480             this.bindRegion(target, r);
44481         }
44482         return this.regions[target];
44483     },
44484
44485     // private (kinda)
44486     bindRegion : function(name, r){
44487         this.regions[name] = r;
44488         r.on("visibilitychange", this.layout, this);
44489         r.on("paneladded", this.layout, this);
44490         r.on("panelremoved", this.layout, this);
44491         r.on("invalidated", this.layout, this);
44492         r.on("resized", this.onRegionResized, this);
44493         r.on("collapsed", this.onRegionCollapsed, this);
44494         r.on("expanded", this.onRegionExpanded, this);
44495     },
44496
44497     /**
44498      * Performs a layout update.
44499      */
44500     layout : function(){
44501         if(this.updating) return;
44502         var size = this.getViewSize();
44503         var w = size.width;
44504         var h = size.height;
44505         var centerW = w;
44506         var centerH = h;
44507         var centerY = 0;
44508         var centerX = 0;
44509         //var x = 0, y = 0;
44510
44511         var rs = this.regions;
44512         var north = rs["north"];
44513         var south = rs["south"]; 
44514         var west = rs["west"];
44515         var east = rs["east"];
44516         var center = rs["center"];
44517         //if(this.hideOnLayout){ // not supported anymore
44518             //c.el.setStyle("display", "none");
44519         //}
44520         if(north && north.isVisible()){
44521             var b = north.getBox();
44522             var m = north.getMargins();
44523             b.width = w - (m.left+m.right);
44524             b.x = m.left;
44525             b.y = m.top;
44526             centerY = b.height + b.y + m.bottom;
44527             centerH -= centerY;
44528             north.updateBox(this.safeBox(b));
44529         }
44530         if(south && south.isVisible()){
44531             var b = south.getBox();
44532             var m = south.getMargins();
44533             b.width = w - (m.left+m.right);
44534             b.x = m.left;
44535             var totalHeight = (b.height + m.top + m.bottom);
44536             b.y = h - totalHeight + m.top;
44537             centerH -= totalHeight;
44538             south.updateBox(this.safeBox(b));
44539         }
44540         if(west && west.isVisible()){
44541             var b = west.getBox();
44542             var m = west.getMargins();
44543             b.height = centerH - (m.top+m.bottom);
44544             b.x = m.left;
44545             b.y = centerY + m.top;
44546             var totalWidth = (b.width + m.left + m.right);
44547             centerX += totalWidth;
44548             centerW -= totalWidth;
44549             west.updateBox(this.safeBox(b));
44550         }
44551         if(east && east.isVisible()){
44552             var b = east.getBox();
44553             var m = east.getMargins();
44554             b.height = centerH - (m.top+m.bottom);
44555             var totalWidth = (b.width + m.left + m.right);
44556             b.x = w - totalWidth + m.left;
44557             b.y = centerY + m.top;
44558             centerW -= totalWidth;
44559             east.updateBox(this.safeBox(b));
44560         }
44561         if(center){
44562             var m = center.getMargins();
44563             var centerBox = {
44564                 x: centerX + m.left,
44565                 y: centerY + m.top,
44566                 width: centerW - (m.left+m.right),
44567                 height: centerH - (m.top+m.bottom)
44568             };
44569             //if(this.hideOnLayout){
44570                 //center.el.setStyle("display", "block");
44571             //}
44572             center.updateBox(this.safeBox(centerBox));
44573         }
44574         this.el.repaint();
44575         this.fireEvent("layout", this);
44576     },
44577
44578     // private
44579     safeBox : function(box){
44580         box.width = Math.max(0, box.width);
44581         box.height = Math.max(0, box.height);
44582         return box;
44583     },
44584
44585     /**
44586      * Adds a ContentPanel (or subclass) to this layout.
44587      * @param {String} target The target region key (north, south, east, west or center).
44588      * @param {Roo.ContentPanel} panel The panel to add
44589      * @return {Roo.ContentPanel} The added panel
44590      */
44591     add : function(target, panel){
44592          
44593         target = target.toLowerCase();
44594         return this.regions[target].add(panel);
44595     },
44596
44597     /**
44598      * Remove a ContentPanel (or subclass) to this layout.
44599      * @param {String} target The target region key (north, south, east, west or center).
44600      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44601      * @return {Roo.ContentPanel} The removed panel
44602      */
44603     remove : function(target, panel){
44604         target = target.toLowerCase();
44605         return this.regions[target].remove(panel);
44606     },
44607
44608     /**
44609      * Searches all regions for a panel with the specified id
44610      * @param {String} panelId
44611      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44612      */
44613     findPanel : function(panelId){
44614         var rs = this.regions;
44615         for(var target in rs){
44616             if(typeof rs[target] != "function"){
44617                 var p = rs[target].getPanel(panelId);
44618                 if(p){
44619                     return p;
44620                 }
44621             }
44622         }
44623         return null;
44624     },
44625
44626     /**
44627      * Searches all regions for a panel with the specified id and activates (shows) it.
44628      * @param {String/ContentPanel} panelId The panels id or the panel itself
44629      * @return {Roo.ContentPanel} The shown panel or null
44630      */
44631     showPanel : function(panelId) {
44632       var rs = this.regions;
44633       for(var target in rs){
44634          var r = rs[target];
44635          if(typeof r != "function"){
44636             if(r.hasPanel(panelId)){
44637                return r.showPanel(panelId);
44638             }
44639          }
44640       }
44641       return null;
44642    },
44643
44644    /**
44645      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44646      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44647      */
44648     restoreState : function(provider){
44649         if(!provider){
44650             provider = Roo.state.Manager;
44651         }
44652         var sm = new Roo.LayoutStateManager();
44653         sm.init(this, provider);
44654     },
44655
44656     /**
44657      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44658      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44659      * a valid ContentPanel config object.  Example:
44660      * <pre><code>
44661 // Create the main layout
44662 var layout = new Roo.BorderLayout('main-ct', {
44663     west: {
44664         split:true,
44665         minSize: 175,
44666         titlebar: true
44667     },
44668     center: {
44669         title:'Components'
44670     }
44671 }, 'main-ct');
44672
44673 // Create and add multiple ContentPanels at once via configs
44674 layout.batchAdd({
44675    west: {
44676        id: 'source-files',
44677        autoCreate:true,
44678        title:'Ext Source Files',
44679        autoScroll:true,
44680        fitToFrame:true
44681    },
44682    center : {
44683        el: cview,
44684        autoScroll:true,
44685        fitToFrame:true,
44686        toolbar: tb,
44687        resizeEl:'cbody'
44688    }
44689 });
44690 </code></pre>
44691      * @param {Object} regions An object containing ContentPanel configs by region name
44692      */
44693     batchAdd : function(regions){
44694         this.beginUpdate();
44695         for(var rname in regions){
44696             var lr = this.regions[rname];
44697             if(lr){
44698                 this.addTypedPanels(lr, regions[rname]);
44699             }
44700         }
44701         this.endUpdate();
44702     },
44703
44704     // private
44705     addTypedPanels : function(lr, ps){
44706         if(typeof ps == 'string'){
44707             lr.add(new Roo.ContentPanel(ps));
44708         }
44709         else if(ps instanceof Array){
44710             for(var i =0, len = ps.length; i < len; i++){
44711                 this.addTypedPanels(lr, ps[i]);
44712             }
44713         }
44714         else if(!ps.events){ // raw config?
44715             var el = ps.el;
44716             delete ps.el; // prevent conflict
44717             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44718         }
44719         else {  // panel object assumed!
44720             lr.add(ps);
44721         }
44722     },
44723     /**
44724      * Adds a xtype elements to the layout.
44725      * <pre><code>
44726
44727 layout.addxtype({
44728        xtype : 'ContentPanel',
44729        region: 'west',
44730        items: [ .... ]
44731    }
44732 );
44733
44734 layout.addxtype({
44735         xtype : 'NestedLayoutPanel',
44736         region: 'west',
44737         layout: {
44738            center: { },
44739            west: { }   
44740         },
44741         items : [ ... list of content panels or nested layout panels.. ]
44742    }
44743 );
44744 </code></pre>
44745      * @param {Object} cfg Xtype definition of item to add.
44746      */
44747     addxtype : function(cfg)
44748     {
44749         // basically accepts a pannel...
44750         // can accept a layout region..!?!?
44751         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44752         
44753         if (!cfg.xtype.match(/Panel$/)) {
44754             return false;
44755         }
44756         var ret = false;
44757         
44758         if (typeof(cfg.region) == 'undefined') {
44759             Roo.log("Failed to add Panel, region was not set");
44760             Roo.log(cfg);
44761             return false;
44762         }
44763         var region = cfg.region;
44764         delete cfg.region;
44765         
44766           
44767         var xitems = [];
44768         if (cfg.items) {
44769             xitems = cfg.items;
44770             delete cfg.items;
44771         }
44772         var nb = false;
44773         
44774         switch(cfg.xtype) 
44775         {
44776             case 'ContentPanel':  // ContentPanel (el, cfg)
44777             case 'ScrollPanel':  // ContentPanel (el, cfg)
44778                 if(cfg.autoCreate) {
44779                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44780                 } else {
44781                     var el = this.el.createChild();
44782                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44783                 }
44784                 
44785                 this.add(region, ret);
44786                 break;
44787             
44788             
44789             case 'TreePanel': // our new panel!
44790                 cfg.el = this.el.createChild();
44791                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44792                 this.add(region, ret);
44793                 break;
44794             
44795             case 'NestedLayoutPanel': 
44796                 // create a new Layout (which is  a Border Layout...
44797                 var el = this.el.createChild();
44798                 var clayout = cfg.layout;
44799                 delete cfg.layout;
44800                 clayout.items   = clayout.items  || [];
44801                 // replace this exitems with the clayout ones..
44802                 xitems = clayout.items;
44803                  
44804                 
44805                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44806                     cfg.background = false;
44807                 }
44808                 var layout = new Roo.BorderLayout(el, clayout);
44809                 
44810                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44811                 //console.log('adding nested layout panel '  + cfg.toSource());
44812                 this.add(region, ret);
44813                 nb = {}; /// find first...
44814                 break;
44815                 
44816             case 'GridPanel': 
44817             
44818                 // needs grid and region
44819                 
44820                 //var el = this.getRegion(region).el.createChild();
44821                 var el = this.el.createChild();
44822                 // create the grid first...
44823                 
44824                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44825                 delete cfg.grid;
44826                 if (region == 'center' && this.active ) {
44827                     cfg.background = false;
44828                 }
44829                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44830                 
44831                 this.add(region, ret);
44832                 if (cfg.background) {
44833                     ret.on('activate', function(gp) {
44834                         if (!gp.grid.rendered) {
44835                             gp.grid.render();
44836                         }
44837                     });
44838                 } else {
44839                     grid.render();
44840                 }
44841                 break;
44842            
44843                
44844                 
44845                 
44846             default: 
44847                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44848                 return null;
44849              // GridPanel (grid, cfg)
44850             
44851         }
44852         this.beginUpdate();
44853         // add children..
44854         var region = '';
44855         var abn = {};
44856         Roo.each(xitems, function(i)  {
44857             region = nb && i.region ? i.region : false;
44858             
44859             var add = ret.addxtype(i);
44860            
44861             if (region) {
44862                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44863                 if (!i.background) {
44864                     abn[region] = nb[region] ;
44865                 }
44866             }
44867             
44868         });
44869         this.endUpdate();
44870
44871         // make the last non-background panel active..
44872         //if (nb) { Roo.log(abn); }
44873         if (nb) {
44874             
44875             for(var r in abn) {
44876                 region = this.getRegion(r);
44877                 if (region) {
44878                     // tried using nb[r], but it does not work..
44879                      
44880                     region.showPanel(abn[r]);
44881                    
44882                 }
44883             }
44884         }
44885         return ret;
44886         
44887     }
44888 });
44889
44890 /**
44891  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44892  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44893  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44894  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44895  * <pre><code>
44896 // shorthand
44897 var CP = Roo.ContentPanel;
44898
44899 var layout = Roo.BorderLayout.create({
44900     north: {
44901         initialSize: 25,
44902         titlebar: false,
44903         panels: [new CP("north", "North")]
44904     },
44905     west: {
44906         split:true,
44907         initialSize: 200,
44908         minSize: 175,
44909         maxSize: 400,
44910         titlebar: true,
44911         collapsible: true,
44912         panels: [new CP("west", {title: "West"})]
44913     },
44914     east: {
44915         split:true,
44916         initialSize: 202,
44917         minSize: 175,
44918         maxSize: 400,
44919         titlebar: true,
44920         collapsible: true,
44921         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44922     },
44923     south: {
44924         split:true,
44925         initialSize: 100,
44926         minSize: 100,
44927         maxSize: 200,
44928         titlebar: true,
44929         collapsible: true,
44930         panels: [new CP("south", {title: "South", closable: true})]
44931     },
44932     center: {
44933         titlebar: true,
44934         autoScroll:true,
44935         resizeTabs: true,
44936         minTabWidth: 50,
44937         preferredTabWidth: 150,
44938         panels: [
44939             new CP("center1", {title: "Close Me", closable: true}),
44940             new CP("center2", {title: "Center Panel", closable: false})
44941         ]
44942     }
44943 }, document.body);
44944
44945 layout.getRegion("center").showPanel("center1");
44946 </code></pre>
44947  * @param config
44948  * @param targetEl
44949  */
44950 Roo.BorderLayout.create = function(config, targetEl){
44951     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44952     layout.beginUpdate();
44953     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44954     for(var j = 0, jlen = regions.length; j < jlen; j++){
44955         var lr = regions[j];
44956         if(layout.regions[lr] && config[lr].panels){
44957             var r = layout.regions[lr];
44958             var ps = config[lr].panels;
44959             layout.addTypedPanels(r, ps);
44960         }
44961     }
44962     layout.endUpdate();
44963     return layout;
44964 };
44965
44966 // private
44967 Roo.BorderLayout.RegionFactory = {
44968     // private
44969     validRegions : ["north","south","east","west","center"],
44970
44971     // private
44972     create : function(target, mgr, config){
44973         target = target.toLowerCase();
44974         if(config.lightweight || config.basic){
44975             return new Roo.BasicLayoutRegion(mgr, config, target);
44976         }
44977         switch(target){
44978             case "north":
44979                 return new Roo.NorthLayoutRegion(mgr, config);
44980             case "south":
44981                 return new Roo.SouthLayoutRegion(mgr, config);
44982             case "east":
44983                 return new Roo.EastLayoutRegion(mgr, config);
44984             case "west":
44985                 return new Roo.WestLayoutRegion(mgr, config);
44986             case "center":
44987                 return new Roo.CenterLayoutRegion(mgr, config);
44988         }
44989         throw 'Layout region "'+target+'" not supported.';
44990     }
44991 };/*
44992  * Based on:
44993  * Ext JS Library 1.1.1
44994  * Copyright(c) 2006-2007, Ext JS, LLC.
44995  *
44996  * Originally Released Under LGPL - original licence link has changed is not relivant.
44997  *
44998  * Fork - LGPL
44999  * <script type="text/javascript">
45000  */
45001  
45002 /**
45003  * @class Roo.BasicLayoutRegion
45004  * @extends Roo.util.Observable
45005  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45006  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45007  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45008  */
45009 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45010     this.mgr = mgr;
45011     this.position  = pos;
45012     this.events = {
45013         /**
45014          * @scope Roo.BasicLayoutRegion
45015          */
45016         
45017         /**
45018          * @event beforeremove
45019          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45020          * @param {Roo.LayoutRegion} this
45021          * @param {Roo.ContentPanel} panel The panel
45022          * @param {Object} e The cancel event object
45023          */
45024         "beforeremove" : true,
45025         /**
45026          * @event invalidated
45027          * Fires when the layout for this region is changed.
45028          * @param {Roo.LayoutRegion} this
45029          */
45030         "invalidated" : true,
45031         /**
45032          * @event visibilitychange
45033          * Fires when this region is shown or hidden 
45034          * @param {Roo.LayoutRegion} this
45035          * @param {Boolean} visibility true or false
45036          */
45037         "visibilitychange" : true,
45038         /**
45039          * @event paneladded
45040          * Fires when a panel is added. 
45041          * @param {Roo.LayoutRegion} this
45042          * @param {Roo.ContentPanel} panel The panel
45043          */
45044         "paneladded" : true,
45045         /**
45046          * @event panelremoved
45047          * Fires when a panel is removed. 
45048          * @param {Roo.LayoutRegion} this
45049          * @param {Roo.ContentPanel} panel The panel
45050          */
45051         "panelremoved" : true,
45052         /**
45053          * @event collapsed
45054          * Fires when this region is collapsed.
45055          * @param {Roo.LayoutRegion} this
45056          */
45057         "collapsed" : true,
45058         /**
45059          * @event expanded
45060          * Fires when this region is expanded.
45061          * @param {Roo.LayoutRegion} this
45062          */
45063         "expanded" : true,
45064         /**
45065          * @event slideshow
45066          * Fires when this region is slid into view.
45067          * @param {Roo.LayoutRegion} this
45068          */
45069         "slideshow" : true,
45070         /**
45071          * @event slidehide
45072          * Fires when this region slides out of view. 
45073          * @param {Roo.LayoutRegion} this
45074          */
45075         "slidehide" : true,
45076         /**
45077          * @event panelactivated
45078          * Fires when a panel is activated. 
45079          * @param {Roo.LayoutRegion} this
45080          * @param {Roo.ContentPanel} panel The activated panel
45081          */
45082         "panelactivated" : true,
45083         /**
45084          * @event resized
45085          * Fires when the user resizes this region. 
45086          * @param {Roo.LayoutRegion} this
45087          * @param {Number} newSize The new size (width for east/west, height for north/south)
45088          */
45089         "resized" : true
45090     };
45091     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45092     this.panels = new Roo.util.MixedCollection();
45093     this.panels.getKey = this.getPanelId.createDelegate(this);
45094     this.box = null;
45095     this.activePanel = null;
45096     // ensure listeners are added...
45097     
45098     if (config.listeners || config.events) {
45099         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45100             listeners : config.listeners || {},
45101             events : config.events || {}
45102         });
45103     }
45104     
45105     if(skipConfig !== true){
45106         this.applyConfig(config);
45107     }
45108 };
45109
45110 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45111     getPanelId : function(p){
45112         return p.getId();
45113     },
45114     
45115     applyConfig : function(config){
45116         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45117         this.config = config;
45118         
45119     },
45120     
45121     /**
45122      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45123      * the width, for horizontal (north, south) the height.
45124      * @param {Number} newSize The new width or height
45125      */
45126     resizeTo : function(newSize){
45127         var el = this.el ? this.el :
45128                  (this.activePanel ? this.activePanel.getEl() : null);
45129         if(el){
45130             switch(this.position){
45131                 case "east":
45132                 case "west":
45133                     el.setWidth(newSize);
45134                     this.fireEvent("resized", this, newSize);
45135                 break;
45136                 case "north":
45137                 case "south":
45138                     el.setHeight(newSize);
45139                     this.fireEvent("resized", this, newSize);
45140                 break;                
45141             }
45142         }
45143     },
45144     
45145     getBox : function(){
45146         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45147     },
45148     
45149     getMargins : function(){
45150         return this.margins;
45151     },
45152     
45153     updateBox : function(box){
45154         this.box = box;
45155         var el = this.activePanel.getEl();
45156         el.dom.style.left = box.x + "px";
45157         el.dom.style.top = box.y + "px";
45158         this.activePanel.setSize(box.width, box.height);
45159     },
45160     
45161     /**
45162      * Returns the container element for this region.
45163      * @return {Roo.Element}
45164      */
45165     getEl : function(){
45166         return this.activePanel;
45167     },
45168     
45169     /**
45170      * Returns true if this region is currently visible.
45171      * @return {Boolean}
45172      */
45173     isVisible : function(){
45174         return this.activePanel ? true : false;
45175     },
45176     
45177     setActivePanel : function(panel){
45178         panel = this.getPanel(panel);
45179         if(this.activePanel && this.activePanel != panel){
45180             this.activePanel.setActiveState(false);
45181             this.activePanel.getEl().setLeftTop(-10000,-10000);
45182         }
45183         this.activePanel = panel;
45184         panel.setActiveState(true);
45185         if(this.box){
45186             panel.setSize(this.box.width, this.box.height);
45187         }
45188         this.fireEvent("panelactivated", this, panel);
45189         this.fireEvent("invalidated");
45190     },
45191     
45192     /**
45193      * Show the specified panel.
45194      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45195      * @return {Roo.ContentPanel} The shown panel or null
45196      */
45197     showPanel : function(panel){
45198         if(panel = this.getPanel(panel)){
45199             this.setActivePanel(panel);
45200         }
45201         return panel;
45202     },
45203     
45204     /**
45205      * Get the active panel for this region.
45206      * @return {Roo.ContentPanel} The active panel or null
45207      */
45208     getActivePanel : function(){
45209         return this.activePanel;
45210     },
45211     
45212     /**
45213      * Add the passed ContentPanel(s)
45214      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45215      * @return {Roo.ContentPanel} The panel added (if only one was added)
45216      */
45217     add : function(panel){
45218         if(arguments.length > 1){
45219             for(var i = 0, len = arguments.length; i < len; i++) {
45220                 this.add(arguments[i]);
45221             }
45222             return null;
45223         }
45224         if(this.hasPanel(panel)){
45225             this.showPanel(panel);
45226             return panel;
45227         }
45228         var el = panel.getEl();
45229         if(el.dom.parentNode != this.mgr.el.dom){
45230             this.mgr.el.dom.appendChild(el.dom);
45231         }
45232         if(panel.setRegion){
45233             panel.setRegion(this);
45234         }
45235         this.panels.add(panel);
45236         el.setStyle("position", "absolute");
45237         if(!panel.background){
45238             this.setActivePanel(panel);
45239             if(this.config.initialSize && this.panels.getCount()==1){
45240                 this.resizeTo(this.config.initialSize);
45241             }
45242         }
45243         this.fireEvent("paneladded", this, panel);
45244         return panel;
45245     },
45246     
45247     /**
45248      * Returns true if the panel is in this region.
45249      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45250      * @return {Boolean}
45251      */
45252     hasPanel : function(panel){
45253         if(typeof panel == "object"){ // must be panel obj
45254             panel = panel.getId();
45255         }
45256         return this.getPanel(panel) ? true : false;
45257     },
45258     
45259     /**
45260      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45261      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45262      * @param {Boolean} preservePanel Overrides the config preservePanel option
45263      * @return {Roo.ContentPanel} The panel that was removed
45264      */
45265     remove : function(panel, preservePanel){
45266         panel = this.getPanel(panel);
45267         if(!panel){
45268             return null;
45269         }
45270         var e = {};
45271         this.fireEvent("beforeremove", this, panel, e);
45272         if(e.cancel === true){
45273             return null;
45274         }
45275         var panelId = panel.getId();
45276         this.panels.removeKey(panelId);
45277         return panel;
45278     },
45279     
45280     /**
45281      * Returns the panel specified or null if it's not in this region.
45282      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45283      * @return {Roo.ContentPanel}
45284      */
45285     getPanel : function(id){
45286         if(typeof id == "object"){ // must be panel obj
45287             return id;
45288         }
45289         return this.panels.get(id);
45290     },
45291     
45292     /**
45293      * Returns this regions position (north/south/east/west/center).
45294      * @return {String} 
45295      */
45296     getPosition: function(){
45297         return this.position;    
45298     }
45299 });/*
45300  * Based on:
45301  * Ext JS Library 1.1.1
45302  * Copyright(c) 2006-2007, Ext JS, LLC.
45303  *
45304  * Originally Released Under LGPL - original licence link has changed is not relivant.
45305  *
45306  * Fork - LGPL
45307  * <script type="text/javascript">
45308  */
45309  
45310 /**
45311  * @class Roo.LayoutRegion
45312  * @extends Roo.BasicLayoutRegion
45313  * This class represents a region in a layout manager.
45314  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45315  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45316  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45317  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45318  * @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})
45319  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45320  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45321  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45322  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45323  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45324  * @cfg {String}    title           The title for the region (overrides panel titles)
45325  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45326  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45327  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45328  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45329  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45330  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45331  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45332  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45333  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45334  * @cfg {Boolean}   showPin         True to show a pin button
45335  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45336  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45337  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45338  * @cfg {Number}    width           For East/West panels
45339  * @cfg {Number}    height          For North/South panels
45340  * @cfg {Boolean}   split           To show the splitter
45341  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45342  */
45343 Roo.LayoutRegion = function(mgr, config, pos){
45344     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45345     var dh = Roo.DomHelper;
45346     /** This region's container element 
45347     * @type Roo.Element */
45348     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45349     /** This region's title element 
45350     * @type Roo.Element */
45351
45352     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45353         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45354         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45355     ]}, true);
45356     this.titleEl.enableDisplayMode();
45357     /** This region's title text element 
45358     * @type HTMLElement */
45359     this.titleTextEl = this.titleEl.dom.firstChild;
45360     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45361     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45362     this.closeBtn.enableDisplayMode();
45363     this.closeBtn.on("click", this.closeClicked, this);
45364     this.closeBtn.hide();
45365
45366     this.createBody(config);
45367     this.visible = true;
45368     this.collapsed = false;
45369
45370     if(config.hideWhenEmpty){
45371         this.hide();
45372         this.on("paneladded", this.validateVisibility, this);
45373         this.on("panelremoved", this.validateVisibility, this);
45374     }
45375     this.applyConfig(config);
45376 };
45377
45378 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45379
45380     createBody : function(){
45381         /** This region's body element 
45382         * @type Roo.Element */
45383         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45384     },
45385
45386     applyConfig : function(c){
45387         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45388             var dh = Roo.DomHelper;
45389             if(c.titlebar !== false){
45390                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45391                 this.collapseBtn.on("click", this.collapse, this);
45392                 this.collapseBtn.enableDisplayMode();
45393
45394                 if(c.showPin === true || this.showPin){
45395                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45396                     this.stickBtn.enableDisplayMode();
45397                     this.stickBtn.on("click", this.expand, this);
45398                     this.stickBtn.hide();
45399                 }
45400             }
45401             /** This region's collapsed element
45402             * @type Roo.Element */
45403             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45404                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45405             ]}, true);
45406             if(c.floatable !== false){
45407                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45408                this.collapsedEl.on("click", this.collapseClick, this);
45409             }
45410
45411             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45412                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45413                    id: "message", unselectable: "on", style:{"float":"left"}});
45414                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45415              }
45416             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45417             this.expandBtn.on("click", this.expand, this);
45418         }
45419         if(this.collapseBtn){
45420             this.collapseBtn.setVisible(c.collapsible == true);
45421         }
45422         this.cmargins = c.cmargins || this.cmargins ||
45423                          (this.position == "west" || this.position == "east" ?
45424                              {top: 0, left: 2, right:2, bottom: 0} :
45425                              {top: 2, left: 0, right:0, bottom: 2});
45426         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45427         this.bottomTabs = c.tabPosition != "top";
45428         this.autoScroll = c.autoScroll || false;
45429         if(this.autoScroll){
45430             this.bodyEl.setStyle("overflow", "auto");
45431         }else{
45432             this.bodyEl.setStyle("overflow", "hidden");
45433         }
45434         //if(c.titlebar !== false){
45435             if((!c.titlebar && !c.title) || c.titlebar === false){
45436                 this.titleEl.hide();
45437             }else{
45438                 this.titleEl.show();
45439                 if(c.title){
45440                     this.titleTextEl.innerHTML = c.title;
45441                 }
45442             }
45443         //}
45444         this.duration = c.duration || .30;
45445         this.slideDuration = c.slideDuration || .45;
45446         this.config = c;
45447         if(c.collapsed){
45448             this.collapse(true);
45449         }
45450         if(c.hidden){
45451             this.hide();
45452         }
45453     },
45454     /**
45455      * Returns true if this region is currently visible.
45456      * @return {Boolean}
45457      */
45458     isVisible : function(){
45459         return this.visible;
45460     },
45461
45462     /**
45463      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45464      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45465      */
45466     setCollapsedTitle : function(title){
45467         title = title || "&#160;";
45468         if(this.collapsedTitleTextEl){
45469             this.collapsedTitleTextEl.innerHTML = title;
45470         }
45471     },
45472
45473     getBox : function(){
45474         var b;
45475         if(!this.collapsed){
45476             b = this.el.getBox(false, true);
45477         }else{
45478             b = this.collapsedEl.getBox(false, true);
45479         }
45480         return b;
45481     },
45482
45483     getMargins : function(){
45484         return this.collapsed ? this.cmargins : this.margins;
45485     },
45486
45487     highlight : function(){
45488         this.el.addClass("x-layout-panel-dragover");
45489     },
45490
45491     unhighlight : function(){
45492         this.el.removeClass("x-layout-panel-dragover");
45493     },
45494
45495     updateBox : function(box){
45496         this.box = box;
45497         if(!this.collapsed){
45498             this.el.dom.style.left = box.x + "px";
45499             this.el.dom.style.top = box.y + "px";
45500             this.updateBody(box.width, box.height);
45501         }else{
45502             this.collapsedEl.dom.style.left = box.x + "px";
45503             this.collapsedEl.dom.style.top = box.y + "px";
45504             this.collapsedEl.setSize(box.width, box.height);
45505         }
45506         if(this.tabs){
45507             this.tabs.autoSizeTabs();
45508         }
45509     },
45510
45511     updateBody : function(w, h){
45512         if(w !== null){
45513             this.el.setWidth(w);
45514             w -= this.el.getBorderWidth("rl");
45515             if(this.config.adjustments){
45516                 w += this.config.adjustments[0];
45517             }
45518         }
45519         if(h !== null){
45520             this.el.setHeight(h);
45521             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45522             h -= this.el.getBorderWidth("tb");
45523             if(this.config.adjustments){
45524                 h += this.config.adjustments[1];
45525             }
45526             this.bodyEl.setHeight(h);
45527             if(this.tabs){
45528                 h = this.tabs.syncHeight(h);
45529             }
45530         }
45531         if(this.panelSize){
45532             w = w !== null ? w : this.panelSize.width;
45533             h = h !== null ? h : this.panelSize.height;
45534         }
45535         if(this.activePanel){
45536             var el = this.activePanel.getEl();
45537             w = w !== null ? w : el.getWidth();
45538             h = h !== null ? h : el.getHeight();
45539             this.panelSize = {width: w, height: h};
45540             this.activePanel.setSize(w, h);
45541         }
45542         if(Roo.isIE && this.tabs){
45543             this.tabs.el.repaint();
45544         }
45545     },
45546
45547     /**
45548      * Returns the container element for this region.
45549      * @return {Roo.Element}
45550      */
45551     getEl : function(){
45552         return this.el;
45553     },
45554
45555     /**
45556      * Hides this region.
45557      */
45558     hide : function(){
45559         if(!this.collapsed){
45560             this.el.dom.style.left = "-2000px";
45561             this.el.hide();
45562         }else{
45563             this.collapsedEl.dom.style.left = "-2000px";
45564             this.collapsedEl.hide();
45565         }
45566         this.visible = false;
45567         this.fireEvent("visibilitychange", this, false);
45568     },
45569
45570     /**
45571      * Shows this region if it was previously hidden.
45572      */
45573     show : function(){
45574         if(!this.collapsed){
45575             this.el.show();
45576         }else{
45577             this.collapsedEl.show();
45578         }
45579         this.visible = true;
45580         this.fireEvent("visibilitychange", this, true);
45581     },
45582
45583     closeClicked : function(){
45584         if(this.activePanel){
45585             this.remove(this.activePanel);
45586         }
45587     },
45588
45589     collapseClick : function(e){
45590         if(this.isSlid){
45591            e.stopPropagation();
45592            this.slideIn();
45593         }else{
45594            e.stopPropagation();
45595            this.slideOut();
45596         }
45597     },
45598
45599     /**
45600      * Collapses this region.
45601      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45602      */
45603     collapse : function(skipAnim){
45604         if(this.collapsed) return;
45605         this.collapsed = true;
45606         if(this.split){
45607             this.split.el.hide();
45608         }
45609         if(this.config.animate && skipAnim !== true){
45610             this.fireEvent("invalidated", this);
45611             this.animateCollapse();
45612         }else{
45613             this.el.setLocation(-20000,-20000);
45614             this.el.hide();
45615             this.collapsedEl.show();
45616             this.fireEvent("collapsed", this);
45617             this.fireEvent("invalidated", this);
45618         }
45619     },
45620
45621     animateCollapse : function(){
45622         // overridden
45623     },
45624
45625     /**
45626      * Expands this region if it was previously collapsed.
45627      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45628      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45629      */
45630     expand : function(e, skipAnim){
45631         if(e) e.stopPropagation();
45632         if(!this.collapsed || this.el.hasActiveFx()) return;
45633         if(this.isSlid){
45634             this.afterSlideIn();
45635             skipAnim = true;
45636         }
45637         this.collapsed = false;
45638         if(this.config.animate && skipAnim !== true){
45639             this.animateExpand();
45640         }else{
45641             this.el.show();
45642             if(this.split){
45643                 this.split.el.show();
45644             }
45645             this.collapsedEl.setLocation(-2000,-2000);
45646             this.collapsedEl.hide();
45647             this.fireEvent("invalidated", this);
45648             this.fireEvent("expanded", this);
45649         }
45650     },
45651
45652     animateExpand : function(){
45653         // overridden
45654     },
45655
45656     initTabs : function()
45657     {
45658         this.bodyEl.setStyle("overflow", "hidden");
45659         var ts = new Roo.TabPanel(
45660                 this.bodyEl.dom,
45661                 {
45662                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45663                     disableTooltips: this.config.disableTabTips,
45664                     toolbar : this.config.toolbar
45665                 }
45666         );
45667         if(this.config.hideTabs){
45668             ts.stripWrap.setDisplayed(false);
45669         }
45670         this.tabs = ts;
45671         ts.resizeTabs = this.config.resizeTabs === true;
45672         ts.minTabWidth = this.config.minTabWidth || 40;
45673         ts.maxTabWidth = this.config.maxTabWidth || 250;
45674         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45675         ts.monitorResize = false;
45676         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45677         ts.bodyEl.addClass('x-layout-tabs-body');
45678         this.panels.each(this.initPanelAsTab, this);
45679     },
45680
45681     initPanelAsTab : function(panel){
45682         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45683                     this.config.closeOnTab && panel.isClosable());
45684         if(panel.tabTip !== undefined){
45685             ti.setTooltip(panel.tabTip);
45686         }
45687         ti.on("activate", function(){
45688               this.setActivePanel(panel);
45689         }, this);
45690         if(this.config.closeOnTab){
45691             ti.on("beforeclose", function(t, e){
45692                 e.cancel = true;
45693                 this.remove(panel);
45694             }, this);
45695         }
45696         return ti;
45697     },
45698
45699     updatePanelTitle : function(panel, title){
45700         if(this.activePanel == panel){
45701             this.updateTitle(title);
45702         }
45703         if(this.tabs){
45704             var ti = this.tabs.getTab(panel.getEl().id);
45705             ti.setText(title);
45706             if(panel.tabTip !== undefined){
45707                 ti.setTooltip(panel.tabTip);
45708             }
45709         }
45710     },
45711
45712     updateTitle : function(title){
45713         if(this.titleTextEl && !this.config.title){
45714             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45715         }
45716     },
45717
45718     setActivePanel : function(panel){
45719         panel = this.getPanel(panel);
45720         if(this.activePanel && this.activePanel != panel){
45721             this.activePanel.setActiveState(false);
45722         }
45723         this.activePanel = panel;
45724         panel.setActiveState(true);
45725         if(this.panelSize){
45726             panel.setSize(this.panelSize.width, this.panelSize.height);
45727         }
45728         if(this.closeBtn){
45729             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45730         }
45731         this.updateTitle(panel.getTitle());
45732         if(this.tabs){
45733             this.fireEvent("invalidated", this);
45734         }
45735         this.fireEvent("panelactivated", this, panel);
45736     },
45737
45738     /**
45739      * Shows the specified panel.
45740      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45741      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45742      */
45743     showPanel : function(panel){
45744         if(panel = this.getPanel(panel)){
45745             if(this.tabs){
45746                 var tab = this.tabs.getTab(panel.getEl().id);
45747                 if(tab.isHidden()){
45748                     this.tabs.unhideTab(tab.id);
45749                 }
45750                 tab.activate();
45751             }else{
45752                 this.setActivePanel(panel);
45753             }
45754         }
45755         return panel;
45756     },
45757
45758     /**
45759      * Get the active panel for this region.
45760      * @return {Roo.ContentPanel} The active panel or null
45761      */
45762     getActivePanel : function(){
45763         return this.activePanel;
45764     },
45765
45766     validateVisibility : function(){
45767         if(this.panels.getCount() < 1){
45768             this.updateTitle("&#160;");
45769             this.closeBtn.hide();
45770             this.hide();
45771         }else{
45772             if(!this.isVisible()){
45773                 this.show();
45774             }
45775         }
45776     },
45777
45778     /**
45779      * Adds the passed ContentPanel(s) to this region.
45780      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45781      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45782      */
45783     add : function(panel){
45784         if(arguments.length > 1){
45785             for(var i = 0, len = arguments.length; i < len; i++) {
45786                 this.add(arguments[i]);
45787             }
45788             return null;
45789         }
45790         if(this.hasPanel(panel)){
45791             this.showPanel(panel);
45792             return panel;
45793         }
45794         panel.setRegion(this);
45795         this.panels.add(panel);
45796         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45797             this.bodyEl.dom.appendChild(panel.getEl().dom);
45798             if(panel.background !== true){
45799                 this.setActivePanel(panel);
45800             }
45801             this.fireEvent("paneladded", this, panel);
45802             return panel;
45803         }
45804         if(!this.tabs){
45805             this.initTabs();
45806         }else{
45807             this.initPanelAsTab(panel);
45808         }
45809         if(panel.background !== true){
45810             this.tabs.activate(panel.getEl().id);
45811         }
45812         this.fireEvent("paneladded", this, panel);
45813         return panel;
45814     },
45815
45816     /**
45817      * Hides the tab for the specified panel.
45818      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45819      */
45820     hidePanel : function(panel){
45821         if(this.tabs && (panel = this.getPanel(panel))){
45822             this.tabs.hideTab(panel.getEl().id);
45823         }
45824     },
45825
45826     /**
45827      * Unhides the tab for a previously hidden panel.
45828      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45829      */
45830     unhidePanel : function(panel){
45831         if(this.tabs && (panel = this.getPanel(panel))){
45832             this.tabs.unhideTab(panel.getEl().id);
45833         }
45834     },
45835
45836     clearPanels : function(){
45837         while(this.panels.getCount() > 0){
45838              this.remove(this.panels.first());
45839         }
45840     },
45841
45842     /**
45843      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45844      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45845      * @param {Boolean} preservePanel Overrides the config preservePanel option
45846      * @return {Roo.ContentPanel} The panel that was removed
45847      */
45848     remove : function(panel, preservePanel){
45849         panel = this.getPanel(panel);
45850         if(!panel){
45851             return null;
45852         }
45853         var e = {};
45854         this.fireEvent("beforeremove", this, panel, e);
45855         if(e.cancel === true){
45856             return null;
45857         }
45858         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45859         var panelId = panel.getId();
45860         this.panels.removeKey(panelId);
45861         if(preservePanel){
45862             document.body.appendChild(panel.getEl().dom);
45863         }
45864         if(this.tabs){
45865             this.tabs.removeTab(panel.getEl().id);
45866         }else if (!preservePanel){
45867             this.bodyEl.dom.removeChild(panel.getEl().dom);
45868         }
45869         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45870             var p = this.panels.first();
45871             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45872             tempEl.appendChild(p.getEl().dom);
45873             this.bodyEl.update("");
45874             this.bodyEl.dom.appendChild(p.getEl().dom);
45875             tempEl = null;
45876             this.updateTitle(p.getTitle());
45877             this.tabs = null;
45878             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45879             this.setActivePanel(p);
45880         }
45881         panel.setRegion(null);
45882         if(this.activePanel == panel){
45883             this.activePanel = null;
45884         }
45885         if(this.config.autoDestroy !== false && preservePanel !== true){
45886             try{panel.destroy();}catch(e){}
45887         }
45888         this.fireEvent("panelremoved", this, panel);
45889         return panel;
45890     },
45891
45892     /**
45893      * Returns the TabPanel component used by this region
45894      * @return {Roo.TabPanel}
45895      */
45896     getTabs : function(){
45897         return this.tabs;
45898     },
45899
45900     createTool : function(parentEl, className){
45901         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45902             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45903         btn.addClassOnOver("x-layout-tools-button-over");
45904         return btn;
45905     }
45906 });/*
45907  * Based on:
45908  * Ext JS Library 1.1.1
45909  * Copyright(c) 2006-2007, Ext JS, LLC.
45910  *
45911  * Originally Released Under LGPL - original licence link has changed is not relivant.
45912  *
45913  * Fork - LGPL
45914  * <script type="text/javascript">
45915  */
45916  
45917
45918
45919 /**
45920  * @class Roo.SplitLayoutRegion
45921  * @extends Roo.LayoutRegion
45922  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45923  */
45924 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45925     this.cursor = cursor;
45926     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45927 };
45928
45929 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45930     splitTip : "Drag to resize.",
45931     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45932     useSplitTips : false,
45933
45934     applyConfig : function(config){
45935         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45936         if(config.split){
45937             if(!this.split){
45938                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45939                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45940                 /** The SplitBar for this region 
45941                 * @type Roo.SplitBar */
45942                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45943                 this.split.on("moved", this.onSplitMove, this);
45944                 this.split.useShim = config.useShim === true;
45945                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45946                 if(this.useSplitTips){
45947                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45948                 }
45949                 if(config.collapsible){
45950                     this.split.el.on("dblclick", this.collapse,  this);
45951                 }
45952             }
45953             if(typeof config.minSize != "undefined"){
45954                 this.split.minSize = config.minSize;
45955             }
45956             if(typeof config.maxSize != "undefined"){
45957                 this.split.maxSize = config.maxSize;
45958             }
45959             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45960                 this.hideSplitter();
45961             }
45962         }
45963     },
45964
45965     getHMaxSize : function(){
45966          var cmax = this.config.maxSize || 10000;
45967          var center = this.mgr.getRegion("center");
45968          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45969     },
45970
45971     getVMaxSize : function(){
45972          var cmax = this.config.maxSize || 10000;
45973          var center = this.mgr.getRegion("center");
45974          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45975     },
45976
45977     onSplitMove : function(split, newSize){
45978         this.fireEvent("resized", this, newSize);
45979     },
45980     
45981     /** 
45982      * Returns the {@link Roo.SplitBar} for this region.
45983      * @return {Roo.SplitBar}
45984      */
45985     getSplitBar : function(){
45986         return this.split;
45987     },
45988     
45989     hide : function(){
45990         this.hideSplitter();
45991         Roo.SplitLayoutRegion.superclass.hide.call(this);
45992     },
45993
45994     hideSplitter : function(){
45995         if(this.split){
45996             this.split.el.setLocation(-2000,-2000);
45997             this.split.el.hide();
45998         }
45999     },
46000
46001     show : function(){
46002         if(this.split){
46003             this.split.el.show();
46004         }
46005         Roo.SplitLayoutRegion.superclass.show.call(this);
46006     },
46007     
46008     beforeSlide: function(){
46009         if(Roo.isGecko){// firefox overflow auto bug workaround
46010             this.bodyEl.clip();
46011             if(this.tabs) this.tabs.bodyEl.clip();
46012             if(this.activePanel){
46013                 this.activePanel.getEl().clip();
46014                 
46015                 if(this.activePanel.beforeSlide){
46016                     this.activePanel.beforeSlide();
46017                 }
46018             }
46019         }
46020     },
46021     
46022     afterSlide : function(){
46023         if(Roo.isGecko){// firefox overflow auto bug workaround
46024             this.bodyEl.unclip();
46025             if(this.tabs) this.tabs.bodyEl.unclip();
46026             if(this.activePanel){
46027                 this.activePanel.getEl().unclip();
46028                 if(this.activePanel.afterSlide){
46029                     this.activePanel.afterSlide();
46030                 }
46031             }
46032         }
46033     },
46034
46035     initAutoHide : function(){
46036         if(this.autoHide !== false){
46037             if(!this.autoHideHd){
46038                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46039                 this.autoHideHd = {
46040                     "mouseout": function(e){
46041                         if(!e.within(this.el, true)){
46042                             st.delay(500);
46043                         }
46044                     },
46045                     "mouseover" : function(e){
46046                         st.cancel();
46047                     },
46048                     scope : this
46049                 };
46050             }
46051             this.el.on(this.autoHideHd);
46052         }
46053     },
46054
46055     clearAutoHide : function(){
46056         if(this.autoHide !== false){
46057             this.el.un("mouseout", this.autoHideHd.mouseout);
46058             this.el.un("mouseover", this.autoHideHd.mouseover);
46059         }
46060     },
46061
46062     clearMonitor : function(){
46063         Roo.get(document).un("click", this.slideInIf, this);
46064     },
46065
46066     // these names are backwards but not changed for compat
46067     slideOut : function(){
46068         if(this.isSlid || this.el.hasActiveFx()){
46069             return;
46070         }
46071         this.isSlid = true;
46072         if(this.collapseBtn){
46073             this.collapseBtn.hide();
46074         }
46075         this.closeBtnState = this.closeBtn.getStyle('display');
46076         this.closeBtn.hide();
46077         if(this.stickBtn){
46078             this.stickBtn.show();
46079         }
46080         this.el.show();
46081         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46082         this.beforeSlide();
46083         this.el.setStyle("z-index", 10001);
46084         this.el.slideIn(this.getSlideAnchor(), {
46085             callback: function(){
46086                 this.afterSlide();
46087                 this.initAutoHide();
46088                 Roo.get(document).on("click", this.slideInIf, this);
46089                 this.fireEvent("slideshow", this);
46090             },
46091             scope: this,
46092             block: true
46093         });
46094     },
46095
46096     afterSlideIn : function(){
46097         this.clearAutoHide();
46098         this.isSlid = false;
46099         this.clearMonitor();
46100         this.el.setStyle("z-index", "");
46101         if(this.collapseBtn){
46102             this.collapseBtn.show();
46103         }
46104         this.closeBtn.setStyle('display', this.closeBtnState);
46105         if(this.stickBtn){
46106             this.stickBtn.hide();
46107         }
46108         this.fireEvent("slidehide", this);
46109     },
46110
46111     slideIn : function(cb){
46112         if(!this.isSlid || this.el.hasActiveFx()){
46113             Roo.callback(cb);
46114             return;
46115         }
46116         this.isSlid = false;
46117         this.beforeSlide();
46118         this.el.slideOut(this.getSlideAnchor(), {
46119             callback: function(){
46120                 this.el.setLeftTop(-10000, -10000);
46121                 this.afterSlide();
46122                 this.afterSlideIn();
46123                 Roo.callback(cb);
46124             },
46125             scope: this,
46126             block: true
46127         });
46128     },
46129     
46130     slideInIf : function(e){
46131         if(!e.within(this.el)){
46132             this.slideIn();
46133         }
46134     },
46135
46136     animateCollapse : function(){
46137         this.beforeSlide();
46138         this.el.setStyle("z-index", 20000);
46139         var anchor = this.getSlideAnchor();
46140         this.el.slideOut(anchor, {
46141             callback : function(){
46142                 this.el.setStyle("z-index", "");
46143                 this.collapsedEl.slideIn(anchor, {duration:.3});
46144                 this.afterSlide();
46145                 this.el.setLocation(-10000,-10000);
46146                 this.el.hide();
46147                 this.fireEvent("collapsed", this);
46148             },
46149             scope: this,
46150             block: true
46151         });
46152     },
46153
46154     animateExpand : function(){
46155         this.beforeSlide();
46156         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46157         this.el.setStyle("z-index", 20000);
46158         this.collapsedEl.hide({
46159             duration:.1
46160         });
46161         this.el.slideIn(this.getSlideAnchor(), {
46162             callback : function(){
46163                 this.el.setStyle("z-index", "");
46164                 this.afterSlide();
46165                 if(this.split){
46166                     this.split.el.show();
46167                 }
46168                 this.fireEvent("invalidated", this);
46169                 this.fireEvent("expanded", this);
46170             },
46171             scope: this,
46172             block: true
46173         });
46174     },
46175
46176     anchors : {
46177         "west" : "left",
46178         "east" : "right",
46179         "north" : "top",
46180         "south" : "bottom"
46181     },
46182
46183     sanchors : {
46184         "west" : "l",
46185         "east" : "r",
46186         "north" : "t",
46187         "south" : "b"
46188     },
46189
46190     canchors : {
46191         "west" : "tl-tr",
46192         "east" : "tr-tl",
46193         "north" : "tl-bl",
46194         "south" : "bl-tl"
46195     },
46196
46197     getAnchor : function(){
46198         return this.anchors[this.position];
46199     },
46200
46201     getCollapseAnchor : function(){
46202         return this.canchors[this.position];
46203     },
46204
46205     getSlideAnchor : function(){
46206         return this.sanchors[this.position];
46207     },
46208
46209     getAlignAdj : function(){
46210         var cm = this.cmargins;
46211         switch(this.position){
46212             case "west":
46213                 return [0, 0];
46214             break;
46215             case "east":
46216                 return [0, 0];
46217             break;
46218             case "north":
46219                 return [0, 0];
46220             break;
46221             case "south":
46222                 return [0, 0];
46223             break;
46224         }
46225     },
46226
46227     getExpandAdj : function(){
46228         var c = this.collapsedEl, cm = this.cmargins;
46229         switch(this.position){
46230             case "west":
46231                 return [-(cm.right+c.getWidth()+cm.left), 0];
46232             break;
46233             case "east":
46234                 return [cm.right+c.getWidth()+cm.left, 0];
46235             break;
46236             case "north":
46237                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46238             break;
46239             case "south":
46240                 return [0, cm.top+cm.bottom+c.getHeight()];
46241             break;
46242         }
46243     }
46244 });/*
46245  * Based on:
46246  * Ext JS Library 1.1.1
46247  * Copyright(c) 2006-2007, Ext JS, LLC.
46248  *
46249  * Originally Released Under LGPL - original licence link has changed is not relivant.
46250  *
46251  * Fork - LGPL
46252  * <script type="text/javascript">
46253  */
46254 /*
46255  * These classes are private internal classes
46256  */
46257 Roo.CenterLayoutRegion = function(mgr, config){
46258     Roo.LayoutRegion.call(this, mgr, config, "center");
46259     this.visible = true;
46260     this.minWidth = config.minWidth || 20;
46261     this.minHeight = config.minHeight || 20;
46262 };
46263
46264 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46265     hide : function(){
46266         // center panel can't be hidden
46267     },
46268     
46269     show : function(){
46270         // center panel can't be hidden
46271     },
46272     
46273     getMinWidth: function(){
46274         return this.minWidth;
46275     },
46276     
46277     getMinHeight: function(){
46278         return this.minHeight;
46279     }
46280 });
46281
46282
46283 Roo.NorthLayoutRegion = function(mgr, config){
46284     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46285     if(this.split){
46286         this.split.placement = Roo.SplitBar.TOP;
46287         this.split.orientation = Roo.SplitBar.VERTICAL;
46288         this.split.el.addClass("x-layout-split-v");
46289     }
46290     var size = config.initialSize || config.height;
46291     if(typeof size != "undefined"){
46292         this.el.setHeight(size);
46293     }
46294 };
46295 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46296     orientation: Roo.SplitBar.VERTICAL,
46297     getBox : function(){
46298         if(this.collapsed){
46299             return this.collapsedEl.getBox();
46300         }
46301         var box = this.el.getBox();
46302         if(this.split){
46303             box.height += this.split.el.getHeight();
46304         }
46305         return box;
46306     },
46307     
46308     updateBox : function(box){
46309         if(this.split && !this.collapsed){
46310             box.height -= this.split.el.getHeight();
46311             this.split.el.setLeft(box.x);
46312             this.split.el.setTop(box.y+box.height);
46313             this.split.el.setWidth(box.width);
46314         }
46315         if(this.collapsed){
46316             this.updateBody(box.width, null);
46317         }
46318         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46319     }
46320 });
46321
46322 Roo.SouthLayoutRegion = function(mgr, config){
46323     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46324     if(this.split){
46325         this.split.placement = Roo.SplitBar.BOTTOM;
46326         this.split.orientation = Roo.SplitBar.VERTICAL;
46327         this.split.el.addClass("x-layout-split-v");
46328     }
46329     var size = config.initialSize || config.height;
46330     if(typeof size != "undefined"){
46331         this.el.setHeight(size);
46332     }
46333 };
46334 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46335     orientation: Roo.SplitBar.VERTICAL,
46336     getBox : function(){
46337         if(this.collapsed){
46338             return this.collapsedEl.getBox();
46339         }
46340         var box = this.el.getBox();
46341         if(this.split){
46342             var sh = this.split.el.getHeight();
46343             box.height += sh;
46344             box.y -= sh;
46345         }
46346         return box;
46347     },
46348     
46349     updateBox : function(box){
46350         if(this.split && !this.collapsed){
46351             var sh = this.split.el.getHeight();
46352             box.height -= sh;
46353             box.y += sh;
46354             this.split.el.setLeft(box.x);
46355             this.split.el.setTop(box.y-sh);
46356             this.split.el.setWidth(box.width);
46357         }
46358         if(this.collapsed){
46359             this.updateBody(box.width, null);
46360         }
46361         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46362     }
46363 });
46364
46365 Roo.EastLayoutRegion = function(mgr, config){
46366     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46367     if(this.split){
46368         this.split.placement = Roo.SplitBar.RIGHT;
46369         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46370         this.split.el.addClass("x-layout-split-h");
46371     }
46372     var size = config.initialSize || config.width;
46373     if(typeof size != "undefined"){
46374         this.el.setWidth(size);
46375     }
46376 };
46377 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46378     orientation: Roo.SplitBar.HORIZONTAL,
46379     getBox : function(){
46380         if(this.collapsed){
46381             return this.collapsedEl.getBox();
46382         }
46383         var box = this.el.getBox();
46384         if(this.split){
46385             var sw = this.split.el.getWidth();
46386             box.width += sw;
46387             box.x -= sw;
46388         }
46389         return box;
46390     },
46391
46392     updateBox : function(box){
46393         if(this.split && !this.collapsed){
46394             var sw = this.split.el.getWidth();
46395             box.width -= sw;
46396             this.split.el.setLeft(box.x);
46397             this.split.el.setTop(box.y);
46398             this.split.el.setHeight(box.height);
46399             box.x += sw;
46400         }
46401         if(this.collapsed){
46402             this.updateBody(null, box.height);
46403         }
46404         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46405     }
46406 });
46407
46408 Roo.WestLayoutRegion = function(mgr, config){
46409     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46410     if(this.split){
46411         this.split.placement = Roo.SplitBar.LEFT;
46412         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46413         this.split.el.addClass("x-layout-split-h");
46414     }
46415     var size = config.initialSize || config.width;
46416     if(typeof size != "undefined"){
46417         this.el.setWidth(size);
46418     }
46419 };
46420 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46421     orientation: Roo.SplitBar.HORIZONTAL,
46422     getBox : function(){
46423         if(this.collapsed){
46424             return this.collapsedEl.getBox();
46425         }
46426         var box = this.el.getBox();
46427         if(this.split){
46428             box.width += this.split.el.getWidth();
46429         }
46430         return box;
46431     },
46432     
46433     updateBox : function(box){
46434         if(this.split && !this.collapsed){
46435             var sw = this.split.el.getWidth();
46436             box.width -= sw;
46437             this.split.el.setLeft(box.x+box.width);
46438             this.split.el.setTop(box.y);
46439             this.split.el.setHeight(box.height);
46440         }
46441         if(this.collapsed){
46442             this.updateBody(null, box.height);
46443         }
46444         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46445     }
46446 });
46447 /*
46448  * Based on:
46449  * Ext JS Library 1.1.1
46450  * Copyright(c) 2006-2007, Ext JS, LLC.
46451  *
46452  * Originally Released Under LGPL - original licence link has changed is not relivant.
46453  *
46454  * Fork - LGPL
46455  * <script type="text/javascript">
46456  */
46457  
46458  
46459 /*
46460  * Private internal class for reading and applying state
46461  */
46462 Roo.LayoutStateManager = function(layout){
46463      // default empty state
46464      this.state = {
46465         north: {},
46466         south: {},
46467         east: {},
46468         west: {}       
46469     };
46470 };
46471
46472 Roo.LayoutStateManager.prototype = {
46473     init : function(layout, provider){
46474         this.provider = provider;
46475         var state = provider.get(layout.id+"-layout-state");
46476         if(state){
46477             var wasUpdating = layout.isUpdating();
46478             if(!wasUpdating){
46479                 layout.beginUpdate();
46480             }
46481             for(var key in state){
46482                 if(typeof state[key] != "function"){
46483                     var rstate = state[key];
46484                     var r = layout.getRegion(key);
46485                     if(r && rstate){
46486                         if(rstate.size){
46487                             r.resizeTo(rstate.size);
46488                         }
46489                         if(rstate.collapsed == true){
46490                             r.collapse(true);
46491                         }else{
46492                             r.expand(null, true);
46493                         }
46494                     }
46495                 }
46496             }
46497             if(!wasUpdating){
46498                 layout.endUpdate();
46499             }
46500             this.state = state; 
46501         }
46502         this.layout = layout;
46503         layout.on("regionresized", this.onRegionResized, this);
46504         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46505         layout.on("regionexpanded", this.onRegionExpanded, this);
46506     },
46507     
46508     storeState : function(){
46509         this.provider.set(this.layout.id+"-layout-state", this.state);
46510     },
46511     
46512     onRegionResized : function(region, newSize){
46513         this.state[region.getPosition()].size = newSize;
46514         this.storeState();
46515     },
46516     
46517     onRegionCollapsed : function(region){
46518         this.state[region.getPosition()].collapsed = true;
46519         this.storeState();
46520     },
46521     
46522     onRegionExpanded : function(region){
46523         this.state[region.getPosition()].collapsed = false;
46524         this.storeState();
46525     }
46526 };/*
46527  * Based on:
46528  * Ext JS Library 1.1.1
46529  * Copyright(c) 2006-2007, Ext JS, LLC.
46530  *
46531  * Originally Released Under LGPL - original licence link has changed is not relivant.
46532  *
46533  * Fork - LGPL
46534  * <script type="text/javascript">
46535  */
46536 /**
46537  * @class Roo.ContentPanel
46538  * @extends Roo.util.Observable
46539  * A basic ContentPanel element.
46540  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46541  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46542  * @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
46543  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46544  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46545  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46546  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46547  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46548  * @cfg {String} title          The title for this panel
46549  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46550  * @cfg {String} url            Calls {@link #setUrl} with this value
46551  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46552  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46553  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46554  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46555
46556  * @constructor
46557  * Create a new ContentPanel.
46558  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46559  * @param {String/Object} config A string to set only the title or a config object
46560  * @param {String} content (optional) Set the HTML content for this panel
46561  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46562  */
46563 Roo.ContentPanel = function(el, config, content){
46564     
46565      
46566     /*
46567     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46568         config = el;
46569         el = Roo.id();
46570     }
46571     if (config && config.parentLayout) { 
46572         el = config.parentLayout.el.createChild(); 
46573     }
46574     */
46575     if(el.autoCreate){ // xtype is available if this is called from factory
46576         config = el;
46577         el = Roo.id();
46578     }
46579     this.el = Roo.get(el);
46580     if(!this.el && config && config.autoCreate){
46581         if(typeof config.autoCreate == "object"){
46582             if(!config.autoCreate.id){
46583                 config.autoCreate.id = config.id||el;
46584             }
46585             this.el = Roo.DomHelper.append(document.body,
46586                         config.autoCreate, true);
46587         }else{
46588             this.el = Roo.DomHelper.append(document.body,
46589                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46590         }
46591     }
46592     this.closable = false;
46593     this.loaded = false;
46594     this.active = false;
46595     if(typeof config == "string"){
46596         this.title = config;
46597     }else{
46598         Roo.apply(this, config);
46599     }
46600     
46601     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46602         this.wrapEl = this.el.wrap();
46603         this.toolbar.container = this.el.insertSibling(false, 'before');
46604         this.toolbar = new Roo.Toolbar(this.toolbar);
46605     }
46606     
46607     
46608     
46609     if(this.resizeEl){
46610         this.resizeEl = Roo.get(this.resizeEl, true);
46611     }else{
46612         this.resizeEl = this.el;
46613     }
46614     this.addEvents({
46615         /**
46616          * @event activate
46617          * Fires when this panel is activated. 
46618          * @param {Roo.ContentPanel} this
46619          */
46620         "activate" : true,
46621         /**
46622          * @event deactivate
46623          * Fires when this panel is activated. 
46624          * @param {Roo.ContentPanel} this
46625          */
46626         "deactivate" : true,
46627
46628         /**
46629          * @event resize
46630          * Fires when this panel is resized if fitToFrame is true.
46631          * @param {Roo.ContentPanel} this
46632          * @param {Number} width The width after any component adjustments
46633          * @param {Number} height The height after any component adjustments
46634          */
46635         "resize" : true,
46636         
46637          /**
46638          * @event render
46639          * Fires when this tab is created
46640          * @param {Roo.ContentPanel} this
46641          */
46642         "render" : true
46643         
46644         
46645         
46646     });
46647     if(this.autoScroll){
46648         this.resizeEl.setStyle("overflow", "auto");
46649     } else {
46650         // fix randome scrolling
46651         this.el.on('scroll', function() {
46652             Roo.log('fix random scolling');
46653             this.scrollTo('top',0); 
46654         });
46655     }
46656     content = content || this.content;
46657     if(content){
46658         this.setContent(content);
46659     }
46660     if(config && config.url){
46661         this.setUrl(this.url, this.params, this.loadOnce);
46662     }
46663     
46664     
46665     
46666     Roo.ContentPanel.superclass.constructor.call(this);
46667     
46668     this.fireEvent('render', this);
46669 };
46670
46671 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46672     tabTip:'',
46673     setRegion : function(region){
46674         this.region = region;
46675         if(region){
46676            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46677         }else{
46678            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46679         } 
46680     },
46681     
46682     /**
46683      * Returns the toolbar for this Panel if one was configured. 
46684      * @return {Roo.Toolbar} 
46685      */
46686     getToolbar : function(){
46687         return this.toolbar;
46688     },
46689     
46690     setActiveState : function(active){
46691         this.active = active;
46692         if(!active){
46693             this.fireEvent("deactivate", this);
46694         }else{
46695             this.fireEvent("activate", this);
46696         }
46697     },
46698     /**
46699      * Updates this panel's element
46700      * @param {String} content The new content
46701      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46702     */
46703     setContent : function(content, loadScripts){
46704         this.el.update(content, loadScripts);
46705     },
46706
46707     ignoreResize : function(w, h){
46708         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46709             return true;
46710         }else{
46711             this.lastSize = {width: w, height: h};
46712             return false;
46713         }
46714     },
46715     /**
46716      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46717      * @return {Roo.UpdateManager} The UpdateManager
46718      */
46719     getUpdateManager : function(){
46720         return this.el.getUpdateManager();
46721     },
46722      /**
46723      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46724      * @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:
46725 <pre><code>
46726 panel.load({
46727     url: "your-url.php",
46728     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46729     callback: yourFunction,
46730     scope: yourObject, //(optional scope)
46731     discardUrl: false,
46732     nocache: false,
46733     text: "Loading...",
46734     timeout: 30,
46735     scripts: false
46736 });
46737 </code></pre>
46738      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46739      * 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.
46740      * @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}
46741      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46742      * @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.
46743      * @return {Roo.ContentPanel} this
46744      */
46745     load : function(){
46746         var um = this.el.getUpdateManager();
46747         um.update.apply(um, arguments);
46748         return this;
46749     },
46750
46751
46752     /**
46753      * 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.
46754      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46755      * @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)
46756      * @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)
46757      * @return {Roo.UpdateManager} The UpdateManager
46758      */
46759     setUrl : function(url, params, loadOnce){
46760         if(this.refreshDelegate){
46761             this.removeListener("activate", this.refreshDelegate);
46762         }
46763         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46764         this.on("activate", this.refreshDelegate);
46765         return this.el.getUpdateManager();
46766     },
46767     
46768     _handleRefresh : function(url, params, loadOnce){
46769         if(!loadOnce || !this.loaded){
46770             var updater = this.el.getUpdateManager();
46771             updater.update(url, params, this._setLoaded.createDelegate(this));
46772         }
46773     },
46774     
46775     _setLoaded : function(){
46776         this.loaded = true;
46777     }, 
46778     
46779     /**
46780      * Returns this panel's id
46781      * @return {String} 
46782      */
46783     getId : function(){
46784         return this.el.id;
46785     },
46786     
46787     /** 
46788      * Returns this panel's element - used by regiosn to add.
46789      * @return {Roo.Element} 
46790      */
46791     getEl : function(){
46792         return this.wrapEl || this.el;
46793     },
46794     
46795     adjustForComponents : function(width, height){
46796         if(this.resizeEl != this.el){
46797             width -= this.el.getFrameWidth('lr');
46798             height -= this.el.getFrameWidth('tb');
46799         }
46800         if(this.toolbar){
46801             var te = this.toolbar.getEl();
46802             height -= te.getHeight();
46803             te.setWidth(width);
46804         }
46805         if(this.adjustments){
46806             width += this.adjustments[0];
46807             height += this.adjustments[1];
46808         }
46809         return {"width": width, "height": height};
46810     },
46811     
46812     setSize : function(width, height){
46813         if(this.fitToFrame && !this.ignoreResize(width, height)){
46814             if(this.fitContainer && this.resizeEl != this.el){
46815                 this.el.setSize(width, height);
46816             }
46817             var size = this.adjustForComponents(width, height);
46818             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46819             this.fireEvent('resize', this, size.width, size.height);
46820         }
46821     },
46822     
46823     /**
46824      * Returns this panel's title
46825      * @return {String} 
46826      */
46827     getTitle : function(){
46828         return this.title;
46829     },
46830     
46831     /**
46832      * Set this panel's title
46833      * @param {String} title
46834      */
46835     setTitle : function(title){
46836         this.title = title;
46837         if(this.region){
46838             this.region.updatePanelTitle(this, title);
46839         }
46840     },
46841     
46842     /**
46843      * Returns true is this panel was configured to be closable
46844      * @return {Boolean} 
46845      */
46846     isClosable : function(){
46847         return this.closable;
46848     },
46849     
46850     beforeSlide : function(){
46851         this.el.clip();
46852         this.resizeEl.clip();
46853     },
46854     
46855     afterSlide : function(){
46856         this.el.unclip();
46857         this.resizeEl.unclip();
46858     },
46859     
46860     /**
46861      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46862      *   Will fail silently if the {@link #setUrl} method has not been called.
46863      *   This does not activate the panel, just updates its content.
46864      */
46865     refresh : function(){
46866         if(this.refreshDelegate){
46867            this.loaded = false;
46868            this.refreshDelegate();
46869         }
46870     },
46871     
46872     /**
46873      * Destroys this panel
46874      */
46875     destroy : function(){
46876         this.el.removeAllListeners();
46877         var tempEl = document.createElement("span");
46878         tempEl.appendChild(this.el.dom);
46879         tempEl.innerHTML = "";
46880         this.el.remove();
46881         this.el = null;
46882     },
46883     
46884     /**
46885      * form - if the content panel contains a form - this is a reference to it.
46886      * @type {Roo.form.Form}
46887      */
46888     form : false,
46889     /**
46890      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46891      *    This contains a reference to it.
46892      * @type {Roo.View}
46893      */
46894     view : false,
46895     
46896       /**
46897      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46898      * <pre><code>
46899
46900 layout.addxtype({
46901        xtype : 'Form',
46902        items: [ .... ]
46903    }
46904 );
46905
46906 </code></pre>
46907      * @param {Object} cfg Xtype definition of item to add.
46908      */
46909     
46910     addxtype : function(cfg) {
46911         // add form..
46912         if (cfg.xtype.match(/^Form$/)) {
46913             var el = this.el.createChild();
46914
46915             this.form = new  Roo.form.Form(cfg);
46916             
46917             
46918             if ( this.form.allItems.length) this.form.render(el.dom);
46919             return this.form;
46920         }
46921         // should only have one of theses..
46922         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46923             // views..
46924             cfg.el = this.el.appendChild(document.createElement("div"));
46925             // factory?
46926             
46927             var ret = new Roo.factory(cfg);
46928             ret.render && ret.render(false, ''); // render blank..
46929             this.view = ret;
46930             return ret;
46931         }
46932         return false;
46933     }
46934 });
46935
46936 /**
46937  * @class Roo.GridPanel
46938  * @extends Roo.ContentPanel
46939  * @constructor
46940  * Create a new GridPanel.
46941  * @param {Roo.grid.Grid} grid The grid for this panel
46942  * @param {String/Object} config A string to set only the panel's title, or a config object
46943  */
46944 Roo.GridPanel = function(grid, config){
46945     
46946   
46947     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46948         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46949         
46950     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46951     
46952     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46953     
46954     if(this.toolbar){
46955         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46956     }
46957     // xtype created footer. - not sure if will work as we normally have to render first..
46958     if (this.footer && !this.footer.el && this.footer.xtype) {
46959         
46960         this.footer.container = this.grid.getView().getFooterPanel(true);
46961         this.footer.dataSource = this.grid.dataSource;
46962         this.footer = Roo.factory(this.footer, Roo);
46963         
46964     }
46965     
46966     grid.monitorWindowResize = false; // turn off autosizing
46967     grid.autoHeight = false;
46968     grid.autoWidth = false;
46969     this.grid = grid;
46970     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46971 };
46972
46973 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46974     getId : function(){
46975         return this.grid.id;
46976     },
46977     
46978     /**
46979      * Returns the grid for this panel
46980      * @return {Roo.grid.Grid} 
46981      */
46982     getGrid : function(){
46983         return this.grid;    
46984     },
46985     
46986     setSize : function(width, height){
46987         if(!this.ignoreResize(width, height)){
46988             var grid = this.grid;
46989             var size = this.adjustForComponents(width, height);
46990             grid.getGridEl().setSize(size.width, size.height);
46991             grid.autoSize();
46992         }
46993     },
46994     
46995     beforeSlide : function(){
46996         this.grid.getView().scroller.clip();
46997     },
46998     
46999     afterSlide : function(){
47000         this.grid.getView().scroller.unclip();
47001     },
47002     
47003     destroy : function(){
47004         this.grid.destroy();
47005         delete this.grid;
47006         Roo.GridPanel.superclass.destroy.call(this); 
47007     }
47008 });
47009
47010
47011 /**
47012  * @class Roo.NestedLayoutPanel
47013  * @extends Roo.ContentPanel
47014  * @constructor
47015  * Create a new NestedLayoutPanel.
47016  * 
47017  * 
47018  * @param {Roo.BorderLayout} layout The layout for this panel
47019  * @param {String/Object} config A string to set only the title or a config object
47020  */
47021 Roo.NestedLayoutPanel = function(layout, config)
47022 {
47023     // construct with only one argument..
47024     /* FIXME - implement nicer consturctors
47025     if (layout.layout) {
47026         config = layout;
47027         layout = config.layout;
47028         delete config.layout;
47029     }
47030     if (layout.xtype && !layout.getEl) {
47031         // then layout needs constructing..
47032         layout = Roo.factory(layout, Roo);
47033     }
47034     */
47035     
47036     
47037     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47038     
47039     layout.monitorWindowResize = false; // turn off autosizing
47040     this.layout = layout;
47041     this.layout.getEl().addClass("x-layout-nested-layout");
47042     
47043     
47044     
47045     
47046 };
47047
47048 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47049
47050     setSize : function(width, height){
47051         if(!this.ignoreResize(width, height)){
47052             var size = this.adjustForComponents(width, height);
47053             var el = this.layout.getEl();
47054             el.setSize(size.width, size.height);
47055             var touch = el.dom.offsetWidth;
47056             this.layout.layout();
47057             // ie requires a double layout on the first pass
47058             if(Roo.isIE && !this.initialized){
47059                 this.initialized = true;
47060                 this.layout.layout();
47061             }
47062         }
47063     },
47064     
47065     // activate all subpanels if not currently active..
47066     
47067     setActiveState : function(active){
47068         this.active = active;
47069         if(!active){
47070             this.fireEvent("deactivate", this);
47071             return;
47072         }
47073         
47074         this.fireEvent("activate", this);
47075         // not sure if this should happen before or after..
47076         if (!this.layout) {
47077             return; // should not happen..
47078         }
47079         var reg = false;
47080         for (var r in this.layout.regions) {
47081             reg = this.layout.getRegion(r);
47082             if (reg.getActivePanel()) {
47083                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47084                 reg.setActivePanel(reg.getActivePanel());
47085                 continue;
47086             }
47087             if (!reg.panels.length) {
47088                 continue;
47089             }
47090             reg.showPanel(reg.getPanel(0));
47091         }
47092         
47093         
47094         
47095         
47096     },
47097     
47098     /**
47099      * Returns the nested BorderLayout for this panel
47100      * @return {Roo.BorderLayout} 
47101      */
47102     getLayout : function(){
47103         return this.layout;
47104     },
47105     
47106      /**
47107      * Adds a xtype elements to the layout of the nested panel
47108      * <pre><code>
47109
47110 panel.addxtype({
47111        xtype : 'ContentPanel',
47112        region: 'west',
47113        items: [ .... ]
47114    }
47115 );
47116
47117 panel.addxtype({
47118         xtype : 'NestedLayoutPanel',
47119         region: 'west',
47120         layout: {
47121            center: { },
47122            west: { }   
47123         },
47124         items : [ ... list of content panels or nested layout panels.. ]
47125    }
47126 );
47127 </code></pre>
47128      * @param {Object} cfg Xtype definition of item to add.
47129      */
47130     addxtype : function(cfg) {
47131         return this.layout.addxtype(cfg);
47132     
47133     }
47134 });
47135
47136 Roo.ScrollPanel = function(el, config, content){
47137     config = config || {};
47138     config.fitToFrame = true;
47139     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47140     
47141     this.el.dom.style.overflow = "hidden";
47142     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47143     this.el.removeClass("x-layout-inactive-content");
47144     this.el.on("mousewheel", this.onWheel, this);
47145
47146     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47147     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47148     up.unselectable(); down.unselectable();
47149     up.on("click", this.scrollUp, this);
47150     down.on("click", this.scrollDown, this);
47151     up.addClassOnOver("x-scroller-btn-over");
47152     down.addClassOnOver("x-scroller-btn-over");
47153     up.addClassOnClick("x-scroller-btn-click");
47154     down.addClassOnClick("x-scroller-btn-click");
47155     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47156
47157     this.resizeEl = this.el;
47158     this.el = wrap; this.up = up; this.down = down;
47159 };
47160
47161 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47162     increment : 100,
47163     wheelIncrement : 5,
47164     scrollUp : function(){
47165         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47166     },
47167
47168     scrollDown : function(){
47169         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47170     },
47171
47172     afterScroll : function(){
47173         var el = this.resizeEl;
47174         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47175         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47176         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47177     },
47178
47179     setSize : function(){
47180         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47181         this.afterScroll();
47182     },
47183
47184     onWheel : function(e){
47185         var d = e.getWheelDelta();
47186         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47187         this.afterScroll();
47188         e.stopEvent();
47189     },
47190
47191     setContent : function(content, loadScripts){
47192         this.resizeEl.update(content, loadScripts);
47193     }
47194
47195 });
47196
47197
47198
47199
47200
47201
47202
47203
47204
47205 /**
47206  * @class Roo.TreePanel
47207  * @extends Roo.ContentPanel
47208  * @constructor
47209  * Create a new TreePanel. - defaults to fit/scoll contents.
47210  * @param {String/Object} config A string to set only the panel's title, or a config object
47211  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47212  */
47213 Roo.TreePanel = function(config){
47214     var el = config.el;
47215     var tree = config.tree;
47216     delete config.tree; 
47217     delete config.el; // hopefull!
47218     
47219     // wrapper for IE7 strict & safari scroll issue
47220     
47221     var treeEl = el.createChild();
47222     config.resizeEl = treeEl;
47223     
47224     
47225     
47226     Roo.TreePanel.superclass.constructor.call(this, el, config);
47227  
47228  
47229     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47230     //console.log(tree);
47231     this.on('activate', function()
47232     {
47233         if (this.tree.rendered) {
47234             return;
47235         }
47236         //console.log('render tree');
47237         this.tree.render();
47238     });
47239     
47240     this.on('resize',  function (cp, w, h) {
47241             this.tree.innerCt.setWidth(w);
47242             this.tree.innerCt.setHeight(h);
47243             this.tree.innerCt.setStyle('overflow-y', 'auto');
47244     });
47245
47246         
47247     
47248 };
47249
47250 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47251     fitToFrame : true,
47252     autoScroll : true
47253 });
47254
47255
47256
47257
47258
47259
47260
47261
47262
47263
47264
47265 /*
47266  * Based on:
47267  * Ext JS Library 1.1.1
47268  * Copyright(c) 2006-2007, Ext JS, LLC.
47269  *
47270  * Originally Released Under LGPL - original licence link has changed is not relivant.
47271  *
47272  * Fork - LGPL
47273  * <script type="text/javascript">
47274  */
47275  
47276
47277 /**
47278  * @class Roo.ReaderLayout
47279  * @extends Roo.BorderLayout
47280  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47281  * center region containing two nested regions (a top one for a list view and one for item preview below),
47282  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47283  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47284  * expedites the setup of the overall layout and regions for this common application style.
47285  * Example:
47286  <pre><code>
47287 var reader = new Roo.ReaderLayout();
47288 var CP = Roo.ContentPanel;  // shortcut for adding
47289
47290 reader.beginUpdate();
47291 reader.add("north", new CP("north", "North"));
47292 reader.add("west", new CP("west", {title: "West"}));
47293 reader.add("east", new CP("east", {title: "East"}));
47294
47295 reader.regions.listView.add(new CP("listView", "List"));
47296 reader.regions.preview.add(new CP("preview", "Preview"));
47297 reader.endUpdate();
47298 </code></pre>
47299 * @constructor
47300 * Create a new ReaderLayout
47301 * @param {Object} config Configuration options
47302 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47303 * document.body if omitted)
47304 */
47305 Roo.ReaderLayout = function(config, renderTo){
47306     var c = config || {size:{}};
47307     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47308         north: c.north !== false ? Roo.apply({
47309             split:false,
47310             initialSize: 32,
47311             titlebar: false
47312         }, c.north) : false,
47313         west: c.west !== 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:5,right:0,bottom:5,top:5},
47322             cmargins:{left:5,right:5,bottom:5,top:5}
47323         }, c.west) : false,
47324         east: c.east !== false ? Roo.apply({
47325             split:true,
47326             initialSize: 200,
47327             minSize: 175,
47328             maxSize: 400,
47329             titlebar: true,
47330             collapsible: true,
47331             animate: true,
47332             margins:{left:0,right:5,bottom:5,top:5},
47333             cmargins:{left:5,right:5,bottom:5,top:5}
47334         }, c.east) : false,
47335         center: Roo.apply({
47336             tabPosition: 'top',
47337             autoScroll:false,
47338             closeOnTab: true,
47339             titlebar:false,
47340             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47341         }, c.center)
47342     });
47343
47344     this.el.addClass('x-reader');
47345
47346     this.beginUpdate();
47347
47348     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47349         south: c.preview !== false ? Roo.apply({
47350             split:true,
47351             initialSize: 200,
47352             minSize: 100,
47353             autoScroll:true,
47354             collapsible:true,
47355             titlebar: true,
47356             cmargins:{top:5,left:0, right:0, bottom:0}
47357         }, c.preview) : false,
47358         center: Roo.apply({
47359             autoScroll:false,
47360             titlebar:false,
47361             minHeight:200
47362         }, c.listView)
47363     });
47364     this.add('center', new Roo.NestedLayoutPanel(inner,
47365             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47366
47367     this.endUpdate();
47368
47369     this.regions.preview = inner.getRegion('south');
47370     this.regions.listView = inner.getRegion('center');
47371 };
47372
47373 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47374  * Based on:
47375  * Ext JS Library 1.1.1
47376  * Copyright(c) 2006-2007, Ext JS, LLC.
47377  *
47378  * Originally Released Under LGPL - original licence link has changed is not relivant.
47379  *
47380  * Fork - LGPL
47381  * <script type="text/javascript">
47382  */
47383  
47384 /**
47385  * @class Roo.grid.Grid
47386  * @extends Roo.util.Observable
47387  * This class represents the primary interface of a component based grid control.
47388  * <br><br>Usage:<pre><code>
47389  var grid = new Roo.grid.Grid("my-container-id", {
47390      ds: myDataStore,
47391      cm: myColModel,
47392      selModel: mySelectionModel,
47393      autoSizeColumns: true,
47394      monitorWindowResize: false,
47395      trackMouseOver: true
47396  });
47397  // set any options
47398  grid.render();
47399  * </code></pre>
47400  * <b>Common Problems:</b><br/>
47401  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47402  * element will correct this<br/>
47403  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47404  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47405  * are unpredictable.<br/>
47406  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47407  * grid to calculate dimensions/offsets.<br/>
47408   * @constructor
47409  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47410  * The container MUST have some type of size defined for the grid to fill. The container will be
47411  * automatically set to position relative if it isn't already.
47412  * @param {Object} config A config object that sets properties on this grid.
47413  */
47414 Roo.grid.Grid = function(container, config){
47415         // initialize the container
47416         this.container = Roo.get(container);
47417         this.container.update("");
47418         this.container.setStyle("overflow", "hidden");
47419     this.container.addClass('x-grid-container');
47420
47421     this.id = this.container.id;
47422
47423     Roo.apply(this, config);
47424     // check and correct shorthanded configs
47425     if(this.ds){
47426         this.dataSource = this.ds;
47427         delete this.ds;
47428     }
47429     if(this.cm){
47430         this.colModel = this.cm;
47431         delete this.cm;
47432     }
47433     if(this.sm){
47434         this.selModel = this.sm;
47435         delete this.sm;
47436     }
47437
47438     if (this.selModel) {
47439         this.selModel = Roo.factory(this.selModel, Roo.grid);
47440         this.sm = this.selModel;
47441         this.sm.xmodule = this.xmodule || false;
47442     }
47443     if (typeof(this.colModel.config) == 'undefined') {
47444         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47445         this.cm = this.colModel;
47446         this.cm.xmodule = this.xmodule || false;
47447     }
47448     if (this.dataSource) {
47449         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47450         this.ds = this.dataSource;
47451         this.ds.xmodule = this.xmodule || false;
47452          
47453     }
47454     
47455     
47456     
47457     if(this.width){
47458         this.container.setWidth(this.width);
47459     }
47460
47461     if(this.height){
47462         this.container.setHeight(this.height);
47463     }
47464     /** @private */
47465         this.addEvents({
47466         // raw events
47467         /**
47468          * @event click
47469          * The raw click event for the entire grid.
47470          * @param {Roo.EventObject} e
47471          */
47472         "click" : true,
47473         /**
47474          * @event dblclick
47475          * The raw dblclick event for the entire grid.
47476          * @param {Roo.EventObject} e
47477          */
47478         "dblclick" : true,
47479         /**
47480          * @event contextmenu
47481          * The raw contextmenu event for the entire grid.
47482          * @param {Roo.EventObject} e
47483          */
47484         "contextmenu" : true,
47485         /**
47486          * @event mousedown
47487          * The raw mousedown event for the entire grid.
47488          * @param {Roo.EventObject} e
47489          */
47490         "mousedown" : true,
47491         /**
47492          * @event mouseup
47493          * The raw mouseup event for the entire grid.
47494          * @param {Roo.EventObject} e
47495          */
47496         "mouseup" : true,
47497         /**
47498          * @event mouseover
47499          * The raw mouseover event for the entire grid.
47500          * @param {Roo.EventObject} e
47501          */
47502         "mouseover" : true,
47503         /**
47504          * @event mouseout
47505          * The raw mouseout event for the entire grid.
47506          * @param {Roo.EventObject} e
47507          */
47508         "mouseout" : true,
47509         /**
47510          * @event keypress
47511          * The raw keypress event for the entire grid.
47512          * @param {Roo.EventObject} e
47513          */
47514         "keypress" : true,
47515         /**
47516          * @event keydown
47517          * The raw keydown event for the entire grid.
47518          * @param {Roo.EventObject} e
47519          */
47520         "keydown" : true,
47521
47522         // custom events
47523
47524         /**
47525          * @event cellclick
47526          * Fires when a cell is clicked
47527          * @param {Grid} this
47528          * @param {Number} rowIndex
47529          * @param {Number} columnIndex
47530          * @param {Roo.EventObject} e
47531          */
47532         "cellclick" : true,
47533         /**
47534          * @event celldblclick
47535          * Fires when a cell is double clicked
47536          * @param {Grid} this
47537          * @param {Number} rowIndex
47538          * @param {Number} columnIndex
47539          * @param {Roo.EventObject} e
47540          */
47541         "celldblclick" : true,
47542         /**
47543          * @event rowclick
47544          * Fires when a row is clicked
47545          * @param {Grid} this
47546          * @param {Number} rowIndex
47547          * @param {Roo.EventObject} e
47548          */
47549         "rowclick" : true,
47550         /**
47551          * @event rowdblclick
47552          * Fires when a row is double clicked
47553          * @param {Grid} this
47554          * @param {Number} rowIndex
47555          * @param {Roo.EventObject} e
47556          */
47557         "rowdblclick" : true,
47558         /**
47559          * @event headerclick
47560          * Fires when a header is clicked
47561          * @param {Grid} this
47562          * @param {Number} columnIndex
47563          * @param {Roo.EventObject} e
47564          */
47565         "headerclick" : true,
47566         /**
47567          * @event headerdblclick
47568          * Fires when a header cell is double clicked
47569          * @param {Grid} this
47570          * @param {Number} columnIndex
47571          * @param {Roo.EventObject} e
47572          */
47573         "headerdblclick" : true,
47574         /**
47575          * @event rowcontextmenu
47576          * Fires when a row is right clicked
47577          * @param {Grid} this
47578          * @param {Number} rowIndex
47579          * @param {Roo.EventObject} e
47580          */
47581         "rowcontextmenu" : true,
47582         /**
47583          * @event cellcontextmenu
47584          * Fires when a cell is right clicked
47585          * @param {Grid} this
47586          * @param {Number} rowIndex
47587          * @param {Number} cellIndex
47588          * @param {Roo.EventObject} e
47589          */
47590          "cellcontextmenu" : true,
47591         /**
47592          * @event headercontextmenu
47593          * Fires when a header is right clicked
47594          * @param {Grid} this
47595          * @param {Number} columnIndex
47596          * @param {Roo.EventObject} e
47597          */
47598         "headercontextmenu" : true,
47599         /**
47600          * @event bodyscroll
47601          * Fires when the body element is scrolled
47602          * @param {Number} scrollLeft
47603          * @param {Number} scrollTop
47604          */
47605         "bodyscroll" : true,
47606         /**
47607          * @event columnresize
47608          * Fires when the user resizes a column
47609          * @param {Number} columnIndex
47610          * @param {Number} newSize
47611          */
47612         "columnresize" : true,
47613         /**
47614          * @event columnmove
47615          * Fires when the user moves a column
47616          * @param {Number} oldIndex
47617          * @param {Number} newIndex
47618          */
47619         "columnmove" : true,
47620         /**
47621          * @event startdrag
47622          * Fires when row(s) start being dragged
47623          * @param {Grid} this
47624          * @param {Roo.GridDD} dd The drag drop object
47625          * @param {event} e The raw browser event
47626          */
47627         "startdrag" : true,
47628         /**
47629          * @event enddrag
47630          * Fires when a drag operation is complete
47631          * @param {Grid} this
47632          * @param {Roo.GridDD} dd The drag drop object
47633          * @param {event} e The raw browser event
47634          */
47635         "enddrag" : true,
47636         /**
47637          * @event dragdrop
47638          * Fires when dragged row(s) are dropped on a valid DD target
47639          * @param {Grid} this
47640          * @param {Roo.GridDD} dd The drag drop object
47641          * @param {String} targetId The target drag drop object
47642          * @param {event} e The raw browser event
47643          */
47644         "dragdrop" : true,
47645         /**
47646          * @event dragover
47647          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47648          * @param {Grid} this
47649          * @param {Roo.GridDD} dd The drag drop object
47650          * @param {String} targetId The target drag drop object
47651          * @param {event} e The raw browser event
47652          */
47653         "dragover" : true,
47654         /**
47655          * @event dragenter
47656          *  Fires when the dragged row(s) first cross another DD target while being dragged
47657          * @param {Grid} this
47658          * @param {Roo.GridDD} dd The drag drop object
47659          * @param {String} targetId The target drag drop object
47660          * @param {event} e The raw browser event
47661          */
47662         "dragenter" : true,
47663         /**
47664          * @event dragout
47665          * Fires when the dragged row(s) leave another DD target while being dragged
47666          * @param {Grid} this
47667          * @param {Roo.GridDD} dd The drag drop object
47668          * @param {String} targetId The target drag drop object
47669          * @param {event} e The raw browser event
47670          */
47671         "dragout" : true,
47672         /**
47673          * @event rowclass
47674          * Fires when a row is rendered, so you can change add a style to it.
47675          * @param {GridView} gridview   The grid view
47676          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47677          */
47678         'rowclass' : true,
47679
47680         /**
47681          * @event render
47682          * Fires when the grid is rendered
47683          * @param {Grid} grid
47684          */
47685         'render' : true
47686     });
47687
47688     Roo.grid.Grid.superclass.constructor.call(this);
47689 };
47690 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47691     
47692     /**
47693      * @cfg {String} ddGroup - drag drop group.
47694      */
47695
47696     /**
47697      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47698      */
47699     minColumnWidth : 25,
47700
47701     /**
47702      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47703      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47704      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47705      */
47706     autoSizeColumns : false,
47707
47708     /**
47709      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47710      */
47711     autoSizeHeaders : true,
47712
47713     /**
47714      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47715      */
47716     monitorWindowResize : true,
47717
47718     /**
47719      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47720      * rows measured to get a columns size. Default is 0 (all rows).
47721      */
47722     maxRowsToMeasure : 0,
47723
47724     /**
47725      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47726      */
47727     trackMouseOver : true,
47728
47729     /**
47730     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47731     */
47732     
47733     /**
47734     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47735     */
47736     enableDragDrop : false,
47737     
47738     /**
47739     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47740     */
47741     enableColumnMove : true,
47742     
47743     /**
47744     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47745     */
47746     enableColumnHide : true,
47747     
47748     /**
47749     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47750     */
47751     enableRowHeightSync : false,
47752     
47753     /**
47754     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47755     */
47756     stripeRows : true,
47757     
47758     /**
47759     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47760     */
47761     autoHeight : false,
47762
47763     /**
47764      * @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.
47765      */
47766     autoExpandColumn : false,
47767
47768     /**
47769     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47770     * Default is 50.
47771     */
47772     autoExpandMin : 50,
47773
47774     /**
47775     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47776     */
47777     autoExpandMax : 1000,
47778
47779     /**
47780     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47781     */
47782     view : null,
47783
47784     /**
47785     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47786     */
47787     loadMask : false,
47788     /**
47789     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47790     */
47791     dropTarget: false,
47792     
47793    
47794     
47795     // private
47796     rendered : false,
47797
47798     /**
47799     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47800     * of a fixed width. Default is false.
47801     */
47802     /**
47803     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47804     */
47805     /**
47806      * Called once after all setup has been completed and the grid is ready to be rendered.
47807      * @return {Roo.grid.Grid} this
47808      */
47809     render : function()
47810     {
47811         var c = this.container;
47812         // try to detect autoHeight/width mode
47813         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47814             this.autoHeight = true;
47815         }
47816         var view = this.getView();
47817         view.init(this);
47818
47819         c.on("click", this.onClick, this);
47820         c.on("dblclick", this.onDblClick, this);
47821         c.on("contextmenu", this.onContextMenu, this);
47822         c.on("keydown", this.onKeyDown, this);
47823
47824         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47825
47826         this.getSelectionModel().init(this);
47827
47828         view.render();
47829
47830         if(this.loadMask){
47831             this.loadMask = new Roo.LoadMask(this.container,
47832                     Roo.apply({store:this.dataSource}, this.loadMask));
47833         }
47834         
47835         
47836         if (this.toolbar && this.toolbar.xtype) {
47837             this.toolbar.container = this.getView().getHeaderPanel(true);
47838             this.toolbar = new Roo.Toolbar(this.toolbar);
47839         }
47840         if (this.footer && this.footer.xtype) {
47841             this.footer.dataSource = this.getDataSource();
47842             this.footer.container = this.getView().getFooterPanel(true);
47843             this.footer = Roo.factory(this.footer, Roo);
47844         }
47845         if (this.dropTarget && this.dropTarget.xtype) {
47846             delete this.dropTarget.xtype;
47847             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47848         }
47849         
47850         
47851         this.rendered = true;
47852         this.fireEvent('render', this);
47853         return this;
47854     },
47855
47856         /**
47857          * Reconfigures the grid to use a different Store and Column Model.
47858          * The View will be bound to the new objects and refreshed.
47859          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47860          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47861          */
47862     reconfigure : function(dataSource, colModel){
47863         if(this.loadMask){
47864             this.loadMask.destroy();
47865             this.loadMask = new Roo.LoadMask(this.container,
47866                     Roo.apply({store:dataSource}, this.loadMask));
47867         }
47868         this.view.bind(dataSource, colModel);
47869         this.dataSource = dataSource;
47870         this.colModel = colModel;
47871         this.view.refresh(true);
47872     },
47873
47874     // private
47875     onKeyDown : function(e){
47876         this.fireEvent("keydown", e);
47877     },
47878
47879     /**
47880      * Destroy this grid.
47881      * @param {Boolean} removeEl True to remove the element
47882      */
47883     destroy : function(removeEl, keepListeners){
47884         if(this.loadMask){
47885             this.loadMask.destroy();
47886         }
47887         var c = this.container;
47888         c.removeAllListeners();
47889         this.view.destroy();
47890         this.colModel.purgeListeners();
47891         if(!keepListeners){
47892             this.purgeListeners();
47893         }
47894         c.update("");
47895         if(removeEl === true){
47896             c.remove();
47897         }
47898     },
47899
47900     // private
47901     processEvent : function(name, e){
47902         this.fireEvent(name, e);
47903         var t = e.getTarget();
47904         var v = this.view;
47905         var header = v.findHeaderIndex(t);
47906         if(header !== false){
47907             this.fireEvent("header" + name, this, header, e);
47908         }else{
47909             var row = v.findRowIndex(t);
47910             var cell = v.findCellIndex(t);
47911             if(row !== false){
47912                 this.fireEvent("row" + name, this, row, e);
47913                 if(cell !== false){
47914                     this.fireEvent("cell" + name, this, row, cell, e);
47915                 }
47916             }
47917         }
47918     },
47919
47920     // private
47921     onClick : function(e){
47922         this.processEvent("click", e);
47923     },
47924
47925     // private
47926     onContextMenu : function(e, t){
47927         this.processEvent("contextmenu", e);
47928     },
47929
47930     // private
47931     onDblClick : function(e){
47932         this.processEvent("dblclick", e);
47933     },
47934
47935     // private
47936     walkCells : function(row, col, step, fn, scope){
47937         var cm = this.colModel, clen = cm.getColumnCount();
47938         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47939         if(step < 0){
47940             if(col < 0){
47941                 row--;
47942                 first = false;
47943             }
47944             while(row >= 0){
47945                 if(!first){
47946                     col = clen-1;
47947                 }
47948                 first = false;
47949                 while(col >= 0){
47950                     if(fn.call(scope || this, row, col, cm) === true){
47951                         return [row, col];
47952                     }
47953                     col--;
47954                 }
47955                 row--;
47956             }
47957         } else {
47958             if(col >= clen){
47959                 row++;
47960                 first = false;
47961             }
47962             while(row < rlen){
47963                 if(!first){
47964                     col = 0;
47965                 }
47966                 first = false;
47967                 while(col < clen){
47968                     if(fn.call(scope || this, row, col, cm) === true){
47969                         return [row, col];
47970                     }
47971                     col++;
47972                 }
47973                 row++;
47974             }
47975         }
47976         return null;
47977     },
47978
47979     // private
47980     getSelections : function(){
47981         return this.selModel.getSelections();
47982     },
47983
47984     /**
47985      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47986      * but if manual update is required this method will initiate it.
47987      */
47988     autoSize : function(){
47989         if(this.rendered){
47990             this.view.layout();
47991             if(this.view.adjustForScroll){
47992                 this.view.adjustForScroll();
47993             }
47994         }
47995     },
47996
47997     /**
47998      * Returns the grid's underlying element.
47999      * @return {Element} The element
48000      */
48001     getGridEl : function(){
48002         return this.container;
48003     },
48004
48005     // private for compatibility, overridden by editor grid
48006     stopEditing : function(){},
48007
48008     /**
48009      * Returns the grid's SelectionModel.
48010      * @return {SelectionModel}
48011      */
48012     getSelectionModel : function(){
48013         if(!this.selModel){
48014             this.selModel = new Roo.grid.RowSelectionModel();
48015         }
48016         return this.selModel;
48017     },
48018
48019     /**
48020      * Returns the grid's DataSource.
48021      * @return {DataSource}
48022      */
48023     getDataSource : function(){
48024         return this.dataSource;
48025     },
48026
48027     /**
48028      * Returns the grid's ColumnModel.
48029      * @return {ColumnModel}
48030      */
48031     getColumnModel : function(){
48032         return this.colModel;
48033     },
48034
48035     /**
48036      * Returns the grid's GridView object.
48037      * @return {GridView}
48038      */
48039     getView : function(){
48040         if(!this.view){
48041             this.view = new Roo.grid.GridView(this.viewConfig);
48042         }
48043         return this.view;
48044     },
48045     /**
48046      * Called to get grid's drag proxy text, by default returns this.ddText.
48047      * @return {String}
48048      */
48049     getDragDropText : function(){
48050         var count = this.selModel.getCount();
48051         return String.format(this.ddText, count, count == 1 ? '' : 's');
48052     }
48053 });
48054 /**
48055  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48056  * %0 is replaced with the number of selected rows.
48057  * @type String
48058  */
48059 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48060  * Based on:
48061  * Ext JS Library 1.1.1
48062  * Copyright(c) 2006-2007, Ext JS, LLC.
48063  *
48064  * Originally Released Under LGPL - original licence link has changed is not relivant.
48065  *
48066  * Fork - LGPL
48067  * <script type="text/javascript">
48068  */
48069  
48070 Roo.grid.AbstractGridView = function(){
48071         this.grid = null;
48072         
48073         this.events = {
48074             "beforerowremoved" : true,
48075             "beforerowsinserted" : true,
48076             "beforerefresh" : true,
48077             "rowremoved" : true,
48078             "rowsinserted" : true,
48079             "rowupdated" : true,
48080             "refresh" : true
48081         };
48082     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48083 };
48084
48085 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48086     rowClass : "x-grid-row",
48087     cellClass : "x-grid-cell",
48088     tdClass : "x-grid-td",
48089     hdClass : "x-grid-hd",
48090     splitClass : "x-grid-hd-split",
48091     
48092         init: function(grid){
48093         this.grid = grid;
48094                 var cid = this.grid.getGridEl().id;
48095         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48096         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48097         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48098         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48099         },
48100         
48101         getColumnRenderers : function(){
48102         var renderers = [];
48103         var cm = this.grid.colModel;
48104         var colCount = cm.getColumnCount();
48105         for(var i = 0; i < colCount; i++){
48106             renderers[i] = cm.getRenderer(i);
48107         }
48108         return renderers;
48109     },
48110     
48111     getColumnIds : function(){
48112         var ids = [];
48113         var cm = this.grid.colModel;
48114         var colCount = cm.getColumnCount();
48115         for(var i = 0; i < colCount; i++){
48116             ids[i] = cm.getColumnId(i);
48117         }
48118         return ids;
48119     },
48120     
48121     getDataIndexes : function(){
48122         if(!this.indexMap){
48123             this.indexMap = this.buildIndexMap();
48124         }
48125         return this.indexMap.colToData;
48126     },
48127     
48128     getColumnIndexByDataIndex : function(dataIndex){
48129         if(!this.indexMap){
48130             this.indexMap = this.buildIndexMap();
48131         }
48132         return this.indexMap.dataToCol[dataIndex];
48133     },
48134     
48135     /**
48136      * Set a css style for a column dynamically. 
48137      * @param {Number} colIndex The index of the column
48138      * @param {String} name The css property name
48139      * @param {String} value The css value
48140      */
48141     setCSSStyle : function(colIndex, name, value){
48142         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48143         Roo.util.CSS.updateRule(selector, name, value);
48144     },
48145     
48146     generateRules : function(cm){
48147         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48148         Roo.util.CSS.removeStyleSheet(rulesId);
48149         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48150             var cid = cm.getColumnId(i);
48151             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48152                          this.tdSelector, cid, " {\n}\n",
48153                          this.hdSelector, cid, " {\n}\n",
48154                          this.splitSelector, cid, " {\n}\n");
48155         }
48156         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48157     }
48158 });/*
48159  * Based on:
48160  * Ext JS Library 1.1.1
48161  * Copyright(c) 2006-2007, Ext JS, LLC.
48162  *
48163  * Originally Released Under LGPL - original licence link has changed is not relivant.
48164  *
48165  * Fork - LGPL
48166  * <script type="text/javascript">
48167  */
48168
48169 // private
48170 // This is a support class used internally by the Grid components
48171 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48172     this.grid = grid;
48173     this.view = grid.getView();
48174     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48175     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48176     if(hd2){
48177         this.setHandleElId(Roo.id(hd));
48178         this.setOuterHandleElId(Roo.id(hd2));
48179     }
48180     this.scroll = false;
48181 };
48182 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48183     maxDragWidth: 120,
48184     getDragData : function(e){
48185         var t = Roo.lib.Event.getTarget(e);
48186         var h = this.view.findHeaderCell(t);
48187         if(h){
48188             return {ddel: h.firstChild, header:h};
48189         }
48190         return false;
48191     },
48192
48193     onInitDrag : function(e){
48194         this.view.headersDisabled = true;
48195         var clone = this.dragData.ddel.cloneNode(true);
48196         clone.id = Roo.id();
48197         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48198         this.proxy.update(clone);
48199         return true;
48200     },
48201
48202     afterValidDrop : function(){
48203         var v = this.view;
48204         setTimeout(function(){
48205             v.headersDisabled = false;
48206         }, 50);
48207     },
48208
48209     afterInvalidDrop : function(){
48210         var v = this.view;
48211         setTimeout(function(){
48212             v.headersDisabled = false;
48213         }, 50);
48214     }
48215 });
48216 /*
48217  * Based on:
48218  * Ext JS Library 1.1.1
48219  * Copyright(c) 2006-2007, Ext JS, LLC.
48220  *
48221  * Originally Released Under LGPL - original licence link has changed is not relivant.
48222  *
48223  * Fork - LGPL
48224  * <script type="text/javascript">
48225  */
48226 // private
48227 // This is a support class used internally by the Grid components
48228 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48229     this.grid = grid;
48230     this.view = grid.getView();
48231     // split the proxies so they don't interfere with mouse events
48232     this.proxyTop = Roo.DomHelper.append(document.body, {
48233         cls:"col-move-top", html:"&#160;"
48234     }, true);
48235     this.proxyBottom = Roo.DomHelper.append(document.body, {
48236         cls:"col-move-bottom", html:"&#160;"
48237     }, true);
48238     this.proxyTop.hide = this.proxyBottom.hide = function(){
48239         this.setLeftTop(-100,-100);
48240         this.setStyle("visibility", "hidden");
48241     };
48242     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48243     // temporarily disabled
48244     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48245     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48246 };
48247 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48248     proxyOffsets : [-4, -9],
48249     fly: Roo.Element.fly,
48250
48251     getTargetFromEvent : function(e){
48252         var t = Roo.lib.Event.getTarget(e);
48253         var cindex = this.view.findCellIndex(t);
48254         if(cindex !== false){
48255             return this.view.getHeaderCell(cindex);
48256         }
48257         return null;
48258     },
48259
48260     nextVisible : function(h){
48261         var v = this.view, cm = this.grid.colModel;
48262         h = h.nextSibling;
48263         while(h){
48264             if(!cm.isHidden(v.getCellIndex(h))){
48265                 return h;
48266             }
48267             h = h.nextSibling;
48268         }
48269         return null;
48270     },
48271
48272     prevVisible : function(h){
48273         var v = this.view, cm = this.grid.colModel;
48274         h = h.prevSibling;
48275         while(h){
48276             if(!cm.isHidden(v.getCellIndex(h))){
48277                 return h;
48278             }
48279             h = h.prevSibling;
48280         }
48281         return null;
48282     },
48283
48284     positionIndicator : function(h, n, e){
48285         var x = Roo.lib.Event.getPageX(e);
48286         var r = Roo.lib.Dom.getRegion(n.firstChild);
48287         var px, pt, py = r.top + this.proxyOffsets[1];
48288         if((r.right - x) <= (r.right-r.left)/2){
48289             px = r.right+this.view.borderWidth;
48290             pt = "after";
48291         }else{
48292             px = r.left;
48293             pt = "before";
48294         }
48295         var oldIndex = this.view.getCellIndex(h);
48296         var newIndex = this.view.getCellIndex(n);
48297
48298         if(this.grid.colModel.isFixed(newIndex)){
48299             return false;
48300         }
48301
48302         var locked = this.grid.colModel.isLocked(newIndex);
48303
48304         if(pt == "after"){
48305             newIndex++;
48306         }
48307         if(oldIndex < newIndex){
48308             newIndex--;
48309         }
48310         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48311             return false;
48312         }
48313         px +=  this.proxyOffsets[0];
48314         this.proxyTop.setLeftTop(px, py);
48315         this.proxyTop.show();
48316         if(!this.bottomOffset){
48317             this.bottomOffset = this.view.mainHd.getHeight();
48318         }
48319         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48320         this.proxyBottom.show();
48321         return pt;
48322     },
48323
48324     onNodeEnter : function(n, dd, e, data){
48325         if(data.header != n){
48326             this.positionIndicator(data.header, n, e);
48327         }
48328     },
48329
48330     onNodeOver : function(n, dd, e, data){
48331         var result = false;
48332         if(data.header != n){
48333             result = this.positionIndicator(data.header, n, e);
48334         }
48335         if(!result){
48336             this.proxyTop.hide();
48337             this.proxyBottom.hide();
48338         }
48339         return result ? this.dropAllowed : this.dropNotAllowed;
48340     },
48341
48342     onNodeOut : function(n, dd, e, data){
48343         this.proxyTop.hide();
48344         this.proxyBottom.hide();
48345     },
48346
48347     onNodeDrop : function(n, dd, e, data){
48348         var h = data.header;
48349         if(h != n){
48350             var cm = this.grid.colModel;
48351             var x = Roo.lib.Event.getPageX(e);
48352             var r = Roo.lib.Dom.getRegion(n.firstChild);
48353             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48354             var oldIndex = this.view.getCellIndex(h);
48355             var newIndex = this.view.getCellIndex(n);
48356             var locked = cm.isLocked(newIndex);
48357             if(pt == "after"){
48358                 newIndex++;
48359             }
48360             if(oldIndex < newIndex){
48361                 newIndex--;
48362             }
48363             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48364                 return false;
48365             }
48366             cm.setLocked(oldIndex, locked, true);
48367             cm.moveColumn(oldIndex, newIndex);
48368             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48369             return true;
48370         }
48371         return false;
48372     }
48373 });
48374 /*
48375  * Based on:
48376  * Ext JS Library 1.1.1
48377  * Copyright(c) 2006-2007, Ext JS, LLC.
48378  *
48379  * Originally Released Under LGPL - original licence link has changed is not relivant.
48380  *
48381  * Fork - LGPL
48382  * <script type="text/javascript">
48383  */
48384   
48385 /**
48386  * @class Roo.grid.GridView
48387  * @extends Roo.util.Observable
48388  *
48389  * @constructor
48390  * @param {Object} config
48391  */
48392 Roo.grid.GridView = function(config){
48393     Roo.grid.GridView.superclass.constructor.call(this);
48394     this.el = null;
48395
48396     Roo.apply(this, config);
48397 };
48398
48399 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48400
48401     
48402     rowClass : "x-grid-row",
48403
48404     cellClass : "x-grid-col",
48405
48406     tdClass : "x-grid-td",
48407
48408     hdClass : "x-grid-hd",
48409
48410     splitClass : "x-grid-split",
48411
48412     sortClasses : ["sort-asc", "sort-desc"],
48413
48414     enableMoveAnim : false,
48415
48416     hlColor: "C3DAF9",
48417
48418     dh : Roo.DomHelper,
48419
48420     fly : Roo.Element.fly,
48421
48422     css : Roo.util.CSS,
48423
48424     borderWidth: 1,
48425
48426     splitOffset: 3,
48427
48428     scrollIncrement : 22,
48429
48430     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48431
48432     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48433
48434     bind : function(ds, cm){
48435         if(this.ds){
48436             this.ds.un("load", this.onLoad, this);
48437             this.ds.un("datachanged", this.onDataChange, this);
48438             this.ds.un("add", this.onAdd, this);
48439             this.ds.un("remove", this.onRemove, this);
48440             this.ds.un("update", this.onUpdate, this);
48441             this.ds.un("clear", this.onClear, this);
48442         }
48443         if(ds){
48444             ds.on("load", this.onLoad, this);
48445             ds.on("datachanged", this.onDataChange, this);
48446             ds.on("add", this.onAdd, this);
48447             ds.on("remove", this.onRemove, this);
48448             ds.on("update", this.onUpdate, this);
48449             ds.on("clear", this.onClear, this);
48450         }
48451         this.ds = ds;
48452
48453         if(this.cm){
48454             this.cm.un("widthchange", this.onColWidthChange, this);
48455             this.cm.un("headerchange", this.onHeaderChange, this);
48456             this.cm.un("hiddenchange", this.onHiddenChange, this);
48457             this.cm.un("columnmoved", this.onColumnMove, this);
48458             this.cm.un("columnlockchange", this.onColumnLock, this);
48459         }
48460         if(cm){
48461             this.generateRules(cm);
48462             cm.on("widthchange", this.onColWidthChange, this);
48463             cm.on("headerchange", this.onHeaderChange, this);
48464             cm.on("hiddenchange", this.onHiddenChange, this);
48465             cm.on("columnmoved", this.onColumnMove, this);
48466             cm.on("columnlockchange", this.onColumnLock, this);
48467         }
48468         this.cm = cm;
48469     },
48470
48471     init: function(grid){
48472         Roo.grid.GridView.superclass.init.call(this, grid);
48473
48474         this.bind(grid.dataSource, grid.colModel);
48475
48476         grid.on("headerclick", this.handleHeaderClick, this);
48477
48478         if(grid.trackMouseOver){
48479             grid.on("mouseover", this.onRowOver, this);
48480             grid.on("mouseout", this.onRowOut, this);
48481         }
48482         grid.cancelTextSelection = function(){};
48483         this.gridId = grid.id;
48484
48485         var tpls = this.templates || {};
48486
48487         if(!tpls.master){
48488             tpls.master = new Roo.Template(
48489                '<div class="x-grid" hidefocus="true">',
48490                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48491                   '<div class="x-grid-topbar"></div>',
48492                   '<div class="x-grid-scroller"><div></div></div>',
48493                   '<div class="x-grid-locked">',
48494                       '<div class="x-grid-header">{lockedHeader}</div>',
48495                       '<div class="x-grid-body">{lockedBody}</div>',
48496                   "</div>",
48497                   '<div class="x-grid-viewport">',
48498                       '<div class="x-grid-header">{header}</div>',
48499                       '<div class="x-grid-body">{body}</div>',
48500                   "</div>",
48501                   '<div class="x-grid-bottombar"></div>',
48502                  
48503                   '<div class="x-grid-resize-proxy">&#160;</div>',
48504                "</div>"
48505             );
48506             tpls.master.disableformats = true;
48507         }
48508
48509         if(!tpls.header){
48510             tpls.header = new Roo.Template(
48511                '<table border="0" cellspacing="0" cellpadding="0">',
48512                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48513                "</table>{splits}"
48514             );
48515             tpls.header.disableformats = true;
48516         }
48517         tpls.header.compile();
48518
48519         if(!tpls.hcell){
48520             tpls.hcell = new Roo.Template(
48521                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48522                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48523                 "</div></td>"
48524              );
48525              tpls.hcell.disableFormats = true;
48526         }
48527         tpls.hcell.compile();
48528
48529         if(!tpls.hsplit){
48530             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48531             tpls.hsplit.disableFormats = true;
48532         }
48533         tpls.hsplit.compile();
48534
48535         if(!tpls.body){
48536             tpls.body = new Roo.Template(
48537                '<table border="0" cellspacing="0" cellpadding="0">',
48538                "<tbody>{rows}</tbody>",
48539                "</table>"
48540             );
48541             tpls.body.disableFormats = true;
48542         }
48543         tpls.body.compile();
48544
48545         if(!tpls.row){
48546             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48547             tpls.row.disableFormats = true;
48548         }
48549         tpls.row.compile();
48550
48551         if(!tpls.cell){
48552             tpls.cell = new Roo.Template(
48553                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48554                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48555                 "</td>"
48556             );
48557             tpls.cell.disableFormats = true;
48558         }
48559         tpls.cell.compile();
48560
48561         this.templates = tpls;
48562     },
48563
48564     // remap these for backwards compat
48565     onColWidthChange : function(){
48566         this.updateColumns.apply(this, arguments);
48567     },
48568     onHeaderChange : function(){
48569         this.updateHeaders.apply(this, arguments);
48570     }, 
48571     onHiddenChange : function(){
48572         this.handleHiddenChange.apply(this, arguments);
48573     },
48574     onColumnMove : function(){
48575         this.handleColumnMove.apply(this, arguments);
48576     },
48577     onColumnLock : function(){
48578         this.handleLockChange.apply(this, arguments);
48579     },
48580
48581     onDataChange : function(){
48582         this.refresh();
48583         this.updateHeaderSortState();
48584     },
48585
48586     onClear : function(){
48587         this.refresh();
48588     },
48589
48590     onUpdate : function(ds, record){
48591         this.refreshRow(record);
48592     },
48593
48594     refreshRow : function(record){
48595         var ds = this.ds, index;
48596         if(typeof record == 'number'){
48597             index = record;
48598             record = ds.getAt(index);
48599         }else{
48600             index = ds.indexOf(record);
48601         }
48602         this.insertRows(ds, index, index, true);
48603         this.onRemove(ds, record, index+1, true);
48604         this.syncRowHeights(index, index);
48605         this.layout();
48606         this.fireEvent("rowupdated", this, index, record);
48607     },
48608
48609     onAdd : function(ds, records, index){
48610         this.insertRows(ds, index, index + (records.length-1));
48611     },
48612
48613     onRemove : function(ds, record, index, isUpdate){
48614         if(isUpdate !== true){
48615             this.fireEvent("beforerowremoved", this, index, record);
48616         }
48617         var bt = this.getBodyTable(), lt = this.getLockedTable();
48618         if(bt.rows[index]){
48619             bt.firstChild.removeChild(bt.rows[index]);
48620         }
48621         if(lt.rows[index]){
48622             lt.firstChild.removeChild(lt.rows[index]);
48623         }
48624         if(isUpdate !== true){
48625             this.stripeRows(index);
48626             this.syncRowHeights(index, index);
48627             this.layout();
48628             this.fireEvent("rowremoved", this, index, record);
48629         }
48630     },
48631
48632     onLoad : function(){
48633         this.scrollToTop();
48634     },
48635
48636     /**
48637      * Scrolls the grid to the top
48638      */
48639     scrollToTop : function(){
48640         if(this.scroller){
48641             this.scroller.dom.scrollTop = 0;
48642             this.syncScroll();
48643         }
48644     },
48645
48646     /**
48647      * Gets a panel in the header of the grid that can be used for toolbars etc.
48648      * After modifying the contents of this panel a call to grid.autoSize() may be
48649      * required to register any changes in size.
48650      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48651      * @return Roo.Element
48652      */
48653     getHeaderPanel : function(doShow){
48654         if(doShow){
48655             this.headerPanel.show();
48656         }
48657         return this.headerPanel;
48658     },
48659
48660     /**
48661      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48662      * After modifying the contents of this panel a call to grid.autoSize() may be
48663      * required to register any changes in size.
48664      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48665      * @return Roo.Element
48666      */
48667     getFooterPanel : function(doShow){
48668         if(doShow){
48669             this.footerPanel.show();
48670         }
48671         return this.footerPanel;
48672     },
48673
48674     initElements : function(){
48675         var E = Roo.Element;
48676         var el = this.grid.getGridEl().dom.firstChild;
48677         var cs = el.childNodes;
48678
48679         this.el = new E(el);
48680         
48681          this.focusEl = new E(el.firstChild);
48682         this.focusEl.swallowEvent("click", true);
48683         
48684         this.headerPanel = new E(cs[1]);
48685         this.headerPanel.enableDisplayMode("block");
48686
48687         this.scroller = new E(cs[2]);
48688         this.scrollSizer = new E(this.scroller.dom.firstChild);
48689
48690         this.lockedWrap = new E(cs[3]);
48691         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48692         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48693
48694         this.mainWrap = new E(cs[4]);
48695         this.mainHd = new E(this.mainWrap.dom.firstChild);
48696         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48697
48698         this.footerPanel = new E(cs[5]);
48699         this.footerPanel.enableDisplayMode("block");
48700
48701         this.resizeProxy = new E(cs[6]);
48702
48703         this.headerSelector = String.format(
48704            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48705            this.lockedHd.id, this.mainHd.id
48706         );
48707
48708         this.splitterSelector = String.format(
48709            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48710            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48711         );
48712     },
48713     idToCssName : function(s)
48714     {
48715         return s.replace(/[^a-z0-9]+/ig, '-');
48716     },
48717
48718     getHeaderCell : function(index){
48719         return Roo.DomQuery.select(this.headerSelector)[index];
48720     },
48721
48722     getHeaderCellMeasure : function(index){
48723         return this.getHeaderCell(index).firstChild;
48724     },
48725
48726     getHeaderCellText : function(index){
48727         return this.getHeaderCell(index).firstChild.firstChild;
48728     },
48729
48730     getLockedTable : function(){
48731         return this.lockedBody.dom.firstChild;
48732     },
48733
48734     getBodyTable : function(){
48735         return this.mainBody.dom.firstChild;
48736     },
48737
48738     getLockedRow : function(index){
48739         return this.getLockedTable().rows[index];
48740     },
48741
48742     getRow : function(index){
48743         return this.getBodyTable().rows[index];
48744     },
48745
48746     getRowComposite : function(index){
48747         if(!this.rowEl){
48748             this.rowEl = new Roo.CompositeElementLite();
48749         }
48750         var els = [], lrow, mrow;
48751         if(lrow = this.getLockedRow(index)){
48752             els.push(lrow);
48753         }
48754         if(mrow = this.getRow(index)){
48755             els.push(mrow);
48756         }
48757         this.rowEl.elements = els;
48758         return this.rowEl;
48759     },
48760     /**
48761      * Gets the 'td' of the cell
48762      * 
48763      * @param {Integer} rowIndex row to select
48764      * @param {Integer} colIndex column to select
48765      * 
48766      * @return {Object} 
48767      */
48768     getCell : function(rowIndex, colIndex){
48769         var locked = this.cm.getLockedCount();
48770         var source;
48771         if(colIndex < locked){
48772             source = this.lockedBody.dom.firstChild;
48773         }else{
48774             source = this.mainBody.dom.firstChild;
48775             colIndex -= locked;
48776         }
48777         return source.rows[rowIndex].childNodes[colIndex];
48778     },
48779
48780     getCellText : function(rowIndex, colIndex){
48781         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48782     },
48783
48784     getCellBox : function(cell){
48785         var b = this.fly(cell).getBox();
48786         if(Roo.isOpera){ // opera fails to report the Y
48787             b.y = cell.offsetTop + this.mainBody.getY();
48788         }
48789         return b;
48790     },
48791
48792     getCellIndex : function(cell){
48793         var id = String(cell.className).match(this.cellRE);
48794         if(id){
48795             return parseInt(id[1], 10);
48796         }
48797         return 0;
48798     },
48799
48800     findHeaderIndex : function(n){
48801         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48802         return r ? this.getCellIndex(r) : false;
48803     },
48804
48805     findHeaderCell : function(n){
48806         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48807         return r ? r : false;
48808     },
48809
48810     findRowIndex : function(n){
48811         if(!n){
48812             return false;
48813         }
48814         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48815         return r ? r.rowIndex : false;
48816     },
48817
48818     findCellIndex : function(node){
48819         var stop = this.el.dom;
48820         while(node && node != stop){
48821             if(this.findRE.test(node.className)){
48822                 return this.getCellIndex(node);
48823             }
48824             node = node.parentNode;
48825         }
48826         return false;
48827     },
48828
48829     getColumnId : function(index){
48830         return this.cm.getColumnId(index);
48831     },
48832
48833     getSplitters : function()
48834     {
48835         if(this.splitterSelector){
48836            return Roo.DomQuery.select(this.splitterSelector);
48837         }else{
48838             return null;
48839       }
48840     },
48841
48842     getSplitter : function(index){
48843         return this.getSplitters()[index];
48844     },
48845
48846     onRowOver : function(e, t){
48847         var row;
48848         if((row = this.findRowIndex(t)) !== false){
48849             this.getRowComposite(row).addClass("x-grid-row-over");
48850         }
48851     },
48852
48853     onRowOut : function(e, t){
48854         var row;
48855         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48856             this.getRowComposite(row).removeClass("x-grid-row-over");
48857         }
48858     },
48859
48860     renderHeaders : function(){
48861         var cm = this.cm;
48862         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48863         var cb = [], lb = [], sb = [], lsb = [], p = {};
48864         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48865             p.cellId = "x-grid-hd-0-" + i;
48866             p.splitId = "x-grid-csplit-0-" + i;
48867             p.id = cm.getColumnId(i);
48868             p.title = cm.getColumnTooltip(i) || "";
48869             p.value = cm.getColumnHeader(i) || "";
48870             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48871             if(!cm.isLocked(i)){
48872                 cb[cb.length] = ct.apply(p);
48873                 sb[sb.length] = st.apply(p);
48874             }else{
48875                 lb[lb.length] = ct.apply(p);
48876                 lsb[lsb.length] = st.apply(p);
48877             }
48878         }
48879         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48880                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48881     },
48882
48883     updateHeaders : function(){
48884         var html = this.renderHeaders();
48885         this.lockedHd.update(html[0]);
48886         this.mainHd.update(html[1]);
48887     },
48888
48889     /**
48890      * Focuses the specified row.
48891      * @param {Number} row The row index
48892      */
48893     focusRow : function(row)
48894     {
48895         //Roo.log('GridView.focusRow');
48896         var x = this.scroller.dom.scrollLeft;
48897         this.focusCell(row, 0, false);
48898         this.scroller.dom.scrollLeft = x;
48899     },
48900
48901     /**
48902      * Focuses the specified cell.
48903      * @param {Number} row The row index
48904      * @param {Number} col The column index
48905      * @param {Boolean} hscroll false to disable horizontal scrolling
48906      */
48907     focusCell : function(row, col, hscroll)
48908     {
48909         //Roo.log('GridView.focusCell');
48910         var el = this.ensureVisible(row, col, hscroll);
48911         this.focusEl.alignTo(el, "tl-tl");
48912         if(Roo.isGecko){
48913             this.focusEl.focus();
48914         }else{
48915             this.focusEl.focus.defer(1, this.focusEl);
48916         }
48917     },
48918
48919     /**
48920      * Scrolls the specified cell into view
48921      * @param {Number} row The row index
48922      * @param {Number} col The column index
48923      * @param {Boolean} hscroll false to disable horizontal scrolling
48924      */
48925     ensureVisible : function(row, col, hscroll)
48926     {
48927         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48928         //return null; //disable for testing.
48929         if(typeof row != "number"){
48930             row = row.rowIndex;
48931         }
48932         if(row < 0 && row >= this.ds.getCount()){
48933             return  null;
48934         }
48935         col = (col !== undefined ? col : 0);
48936         var cm = this.grid.colModel;
48937         while(cm.isHidden(col)){
48938             col++;
48939         }
48940
48941         var el = this.getCell(row, col);
48942         if(!el){
48943             return null;
48944         }
48945         var c = this.scroller.dom;
48946
48947         var ctop = parseInt(el.offsetTop, 10);
48948         var cleft = parseInt(el.offsetLeft, 10);
48949         var cbot = ctop + el.offsetHeight;
48950         var cright = cleft + el.offsetWidth;
48951         
48952         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48953         var stop = parseInt(c.scrollTop, 10);
48954         var sleft = parseInt(c.scrollLeft, 10);
48955         var sbot = stop + ch;
48956         var sright = sleft + c.clientWidth;
48957         /*
48958         Roo.log('GridView.ensureVisible:' +
48959                 ' ctop:' + ctop +
48960                 ' c.clientHeight:' + c.clientHeight +
48961                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48962                 ' stop:' + stop +
48963                 ' cbot:' + cbot +
48964                 ' sbot:' + sbot +
48965                 ' ch:' + ch  
48966                 );
48967         */
48968         if(ctop < stop){
48969              c.scrollTop = ctop;
48970             //Roo.log("set scrolltop to ctop DISABLE?");
48971         }else if(cbot > sbot){
48972             //Roo.log("set scrolltop to cbot-ch");
48973             c.scrollTop = cbot-ch;
48974         }
48975         
48976         if(hscroll !== false){
48977             if(cleft < sleft){
48978                 c.scrollLeft = cleft;
48979             }else if(cright > sright){
48980                 c.scrollLeft = cright-c.clientWidth;
48981             }
48982         }
48983          
48984         return el;
48985     },
48986
48987     updateColumns : function(){
48988         this.grid.stopEditing();
48989         var cm = this.grid.colModel, colIds = this.getColumnIds();
48990         //var totalWidth = cm.getTotalWidth();
48991         var pos = 0;
48992         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48993             //if(cm.isHidden(i)) continue;
48994             var w = cm.getColumnWidth(i);
48995             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48996             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48997         }
48998         this.updateSplitters();
48999     },
49000
49001     generateRules : function(cm){
49002         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49003         Roo.util.CSS.removeStyleSheet(rulesId);
49004         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49005             var cid = cm.getColumnId(i);
49006             var align = '';
49007             if(cm.config[i].align){
49008                 align = 'text-align:'+cm.config[i].align+';';
49009             }
49010             var hidden = '';
49011             if(cm.isHidden(i)){
49012                 hidden = 'display:none;';
49013             }
49014             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49015             ruleBuf.push(
49016                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49017                     this.hdSelector, cid, " {\n", align, width, "}\n",
49018                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49019                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49020         }
49021         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49022     },
49023
49024     updateSplitters : function(){
49025         var cm = this.cm, s = this.getSplitters();
49026         if(s){ // splitters not created yet
49027             var pos = 0, locked = true;
49028             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49029                 if(cm.isHidden(i)) continue;
49030                 var w = cm.getColumnWidth(i); // make sure it's a number
49031                 if(!cm.isLocked(i) && locked){
49032                     pos = 0;
49033                     locked = false;
49034                 }
49035                 pos += w;
49036                 s[i].style.left = (pos-this.splitOffset) + "px";
49037             }
49038         }
49039     },
49040
49041     handleHiddenChange : function(colModel, colIndex, hidden){
49042         if(hidden){
49043             this.hideColumn(colIndex);
49044         }else{
49045             this.unhideColumn(colIndex);
49046         }
49047     },
49048
49049     hideColumn : function(colIndex){
49050         var cid = this.getColumnId(colIndex);
49051         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49052         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49053         if(Roo.isSafari){
49054             this.updateHeaders();
49055         }
49056         this.updateSplitters();
49057         this.layout();
49058     },
49059
49060     unhideColumn : function(colIndex){
49061         var cid = this.getColumnId(colIndex);
49062         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49063         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49064
49065         if(Roo.isSafari){
49066             this.updateHeaders();
49067         }
49068         this.updateSplitters();
49069         this.layout();
49070     },
49071
49072     insertRows : function(dm, firstRow, lastRow, isUpdate){
49073         if(firstRow == 0 && lastRow == dm.getCount()-1){
49074             this.refresh();
49075         }else{
49076             if(!isUpdate){
49077                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49078             }
49079             var s = this.getScrollState();
49080             var markup = this.renderRows(firstRow, lastRow);
49081             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49082             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49083             this.restoreScroll(s);
49084             if(!isUpdate){
49085                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49086                 this.syncRowHeights(firstRow, lastRow);
49087                 this.stripeRows(firstRow);
49088                 this.layout();
49089             }
49090         }
49091     },
49092
49093     bufferRows : function(markup, target, index){
49094         var before = null, trows = target.rows, tbody = target.tBodies[0];
49095         if(index < trows.length){
49096             before = trows[index];
49097         }
49098         var b = document.createElement("div");
49099         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49100         var rows = b.firstChild.rows;
49101         for(var i = 0, len = rows.length; i < len; i++){
49102             if(before){
49103                 tbody.insertBefore(rows[0], before);
49104             }else{
49105                 tbody.appendChild(rows[0]);
49106             }
49107         }
49108         b.innerHTML = "";
49109         b = null;
49110     },
49111
49112     deleteRows : function(dm, firstRow, lastRow){
49113         if(dm.getRowCount()<1){
49114             this.fireEvent("beforerefresh", this);
49115             this.mainBody.update("");
49116             this.lockedBody.update("");
49117             this.fireEvent("refresh", this);
49118         }else{
49119             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49120             var bt = this.getBodyTable();
49121             var tbody = bt.firstChild;
49122             var rows = bt.rows;
49123             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49124                 tbody.removeChild(rows[firstRow]);
49125             }
49126             this.stripeRows(firstRow);
49127             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49128         }
49129     },
49130
49131     updateRows : function(dataSource, firstRow, lastRow){
49132         var s = this.getScrollState();
49133         this.refresh();
49134         this.restoreScroll(s);
49135     },
49136
49137     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49138         if(!noRefresh){
49139            this.refresh();
49140         }
49141         this.updateHeaderSortState();
49142     },
49143
49144     getScrollState : function(){
49145         
49146         var sb = this.scroller.dom;
49147         return {left: sb.scrollLeft, top: sb.scrollTop};
49148     },
49149
49150     stripeRows : function(startRow){
49151         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49152             return;
49153         }
49154         startRow = startRow || 0;
49155         var rows = this.getBodyTable().rows;
49156         var lrows = this.getLockedTable().rows;
49157         var cls = ' x-grid-row-alt ';
49158         for(var i = startRow, len = rows.length; i < len; i++){
49159             var row = rows[i], lrow = lrows[i];
49160             var isAlt = ((i+1) % 2 == 0);
49161             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49162             if(isAlt == hasAlt){
49163                 continue;
49164             }
49165             if(isAlt){
49166                 row.className += " x-grid-row-alt";
49167             }else{
49168                 row.className = row.className.replace("x-grid-row-alt", "");
49169             }
49170             if(lrow){
49171                 lrow.className = row.className;
49172             }
49173         }
49174     },
49175
49176     restoreScroll : function(state){
49177         //Roo.log('GridView.restoreScroll');
49178         var sb = this.scroller.dom;
49179         sb.scrollLeft = state.left;
49180         sb.scrollTop = state.top;
49181         this.syncScroll();
49182     },
49183
49184     syncScroll : function(){
49185         //Roo.log('GridView.syncScroll');
49186         var sb = this.scroller.dom;
49187         var sh = this.mainHd.dom;
49188         var bs = this.mainBody.dom;
49189         var lv = this.lockedBody.dom;
49190         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49191         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49192     },
49193
49194     handleScroll : function(e){
49195         this.syncScroll();
49196         var sb = this.scroller.dom;
49197         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49198         e.stopEvent();
49199     },
49200
49201     handleWheel : function(e){
49202         var d = e.getWheelDelta();
49203         this.scroller.dom.scrollTop -= d*22;
49204         // set this here to prevent jumpy scrolling on large tables
49205         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49206         e.stopEvent();
49207     },
49208
49209     renderRows : function(startRow, endRow){
49210         // pull in all the crap needed to render rows
49211         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49212         var colCount = cm.getColumnCount();
49213
49214         if(ds.getCount() < 1){
49215             return ["", ""];
49216         }
49217
49218         // build a map for all the columns
49219         var cs = [];
49220         for(var i = 0; i < colCount; i++){
49221             var name = cm.getDataIndex(i);
49222             cs[i] = {
49223                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49224                 renderer : cm.getRenderer(i),
49225                 id : cm.getColumnId(i),
49226                 locked : cm.isLocked(i)
49227             };
49228         }
49229
49230         startRow = startRow || 0;
49231         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49232
49233         // records to render
49234         var rs = ds.getRange(startRow, endRow);
49235
49236         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49237     },
49238
49239     // As much as I hate to duplicate code, this was branched because FireFox really hates
49240     // [].join("") on strings. The performance difference was substantial enough to
49241     // branch this function
49242     doRender : Roo.isGecko ?
49243             function(cs, rs, ds, startRow, colCount, stripe){
49244                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49245                 // buffers
49246                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49247                 
49248                 var hasListener = this.grid.hasListener('rowclass');
49249                 var rowcfg = {};
49250                 for(var j = 0, len = rs.length; j < len; j++){
49251                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49252                     for(var i = 0; i < colCount; i++){
49253                         c = cs[i];
49254                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49255                         p.id = c.id;
49256                         p.css = p.attr = "";
49257                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49258                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49259                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49260                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49261                         }
49262                         var markup = ct.apply(p);
49263                         if(!c.locked){
49264                             cb+= markup;
49265                         }else{
49266                             lcb+= markup;
49267                         }
49268                     }
49269                     var alt = [];
49270                     if(stripe && ((rowIndex+1) % 2 == 0)){
49271                         alt.push("x-grid-row-alt")
49272                     }
49273                     if(r.dirty){
49274                         alt.push(  " x-grid-dirty-row");
49275                     }
49276                     rp.cells = lcb;
49277                     if(this.getRowClass){
49278                         alt.push(this.getRowClass(r, rowIndex));
49279                     }
49280                     if (hasListener) {
49281                         rowcfg = {
49282                              
49283                             record: r,
49284                             rowIndex : rowIndex,
49285                             rowClass : ''
49286                         }
49287                         this.grid.fireEvent('rowclass', this, rowcfg);
49288                         alt.push(rowcfg.rowClass);
49289                     }
49290                     rp.alt = alt.join(" ");
49291                     lbuf+= rt.apply(rp);
49292                     rp.cells = cb;
49293                     buf+=  rt.apply(rp);
49294                 }
49295                 return [lbuf, buf];
49296             } :
49297             function(cs, rs, ds, startRow, colCount, stripe){
49298                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49299                 // buffers
49300                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49301                 var hasListener = this.grid.hasListener('rowclass');
49302  
49303                 var rowcfg = {};
49304                 for(var j = 0, len = rs.length; j < len; j++){
49305                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49306                     for(var i = 0; i < colCount; i++){
49307                         c = cs[i];
49308                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49309                         p.id = c.id;
49310                         p.css = p.attr = "";
49311                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49312                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49313                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49314                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49315                         }
49316                         
49317                         var markup = ct.apply(p);
49318                         if(!c.locked){
49319                             cb[cb.length] = markup;
49320                         }else{
49321                             lcb[lcb.length] = markup;
49322                         }
49323                     }
49324                     var alt = [];
49325                     if(stripe && ((rowIndex+1) % 2 == 0)){
49326                         alt.push( "x-grid-row-alt");
49327                     }
49328                     if(r.dirty){
49329                         alt.push(" x-grid-dirty-row");
49330                     }
49331                     rp.cells = lcb;
49332                     if(this.getRowClass){
49333                         alt.push( this.getRowClass(r, rowIndex));
49334                     }
49335                     if (hasListener) {
49336                         rowcfg = {
49337                              
49338                             record: r,
49339                             rowIndex : rowIndex,
49340                             rowClass : ''
49341                         }
49342                         this.grid.fireEvent('rowclass', this, rowcfg);
49343                         alt.push(rowcfg.rowClass);
49344                     }
49345                     rp.alt = alt.join(" ");
49346                     rp.cells = lcb.join("");
49347                     lbuf[lbuf.length] = rt.apply(rp);
49348                     rp.cells = cb.join("");
49349                     buf[buf.length] =  rt.apply(rp);
49350                 }
49351                 return [lbuf.join(""), buf.join("")];
49352             },
49353
49354     renderBody : function(){
49355         var markup = this.renderRows();
49356         var bt = this.templates.body;
49357         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49358     },
49359
49360     /**
49361      * Refreshes the grid
49362      * @param {Boolean} headersToo
49363      */
49364     refresh : function(headersToo){
49365         this.fireEvent("beforerefresh", this);
49366         this.grid.stopEditing();
49367         var result = this.renderBody();
49368         this.lockedBody.update(result[0]);
49369         this.mainBody.update(result[1]);
49370         if(headersToo === true){
49371             this.updateHeaders();
49372             this.updateColumns();
49373             this.updateSplitters();
49374             this.updateHeaderSortState();
49375         }
49376         this.syncRowHeights();
49377         this.layout();
49378         this.fireEvent("refresh", this);
49379     },
49380
49381     handleColumnMove : function(cm, oldIndex, newIndex){
49382         this.indexMap = null;
49383         var s = this.getScrollState();
49384         this.refresh(true);
49385         this.restoreScroll(s);
49386         this.afterMove(newIndex);
49387     },
49388
49389     afterMove : function(colIndex){
49390         if(this.enableMoveAnim && Roo.enableFx){
49391             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49392         }
49393         // if multisort - fix sortOrder, and reload..
49394         if (this.grid.dataSource.multiSort) {
49395             // the we can call sort again..
49396             var dm = this.grid.dataSource;
49397             var cm = this.grid.colModel;
49398             var so = [];
49399             for(var i = 0; i < cm.config.length; i++ ) {
49400                 
49401                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49402                     continue; // dont' bother, it's not in sort list or being set.
49403                 }
49404                 
49405                 so.push(cm.config[i].dataIndex);
49406             };
49407             dm.sortOrder = so;
49408             dm.load(dm.lastOptions);
49409             
49410             
49411         }
49412         
49413     },
49414
49415     updateCell : function(dm, rowIndex, dataIndex){
49416         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49417         if(typeof colIndex == "undefined"){ // not present in grid
49418             return;
49419         }
49420         var cm = this.grid.colModel;
49421         var cell = this.getCell(rowIndex, colIndex);
49422         var cellText = this.getCellText(rowIndex, colIndex);
49423
49424         var p = {
49425             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49426             id : cm.getColumnId(colIndex),
49427             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49428         };
49429         var renderer = cm.getRenderer(colIndex);
49430         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49431         if(typeof val == "undefined" || val === "") val = "&#160;";
49432         cellText.innerHTML = val;
49433         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49434         this.syncRowHeights(rowIndex, rowIndex);
49435     },
49436
49437     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49438         var maxWidth = 0;
49439         if(this.grid.autoSizeHeaders){
49440             var h = this.getHeaderCellMeasure(colIndex);
49441             maxWidth = Math.max(maxWidth, h.scrollWidth);
49442         }
49443         var tb, index;
49444         if(this.cm.isLocked(colIndex)){
49445             tb = this.getLockedTable();
49446             index = colIndex;
49447         }else{
49448             tb = this.getBodyTable();
49449             index = colIndex - this.cm.getLockedCount();
49450         }
49451         if(tb && tb.rows){
49452             var rows = tb.rows;
49453             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49454             for(var i = 0; i < stopIndex; i++){
49455                 var cell = rows[i].childNodes[index].firstChild;
49456                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49457             }
49458         }
49459         return maxWidth + /*margin for error in IE*/ 5;
49460     },
49461     /**
49462      * Autofit a column to its content.
49463      * @param {Number} colIndex
49464      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49465      */
49466      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49467          if(this.cm.isHidden(colIndex)){
49468              return; // can't calc a hidden column
49469          }
49470         if(forceMinSize){
49471             var cid = this.cm.getColumnId(colIndex);
49472             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49473            if(this.grid.autoSizeHeaders){
49474                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49475            }
49476         }
49477         var newWidth = this.calcColumnWidth(colIndex);
49478         this.cm.setColumnWidth(colIndex,
49479             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49480         if(!suppressEvent){
49481             this.grid.fireEvent("columnresize", colIndex, newWidth);
49482         }
49483     },
49484
49485     /**
49486      * Autofits all columns to their content and then expands to fit any extra space in the grid
49487      */
49488      autoSizeColumns : function(){
49489         var cm = this.grid.colModel;
49490         var colCount = cm.getColumnCount();
49491         for(var i = 0; i < colCount; i++){
49492             this.autoSizeColumn(i, true, true);
49493         }
49494         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49495             this.fitColumns();
49496         }else{
49497             this.updateColumns();
49498             this.layout();
49499         }
49500     },
49501
49502     /**
49503      * Autofits all columns to the grid's width proportionate with their current size
49504      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49505      */
49506     fitColumns : function(reserveScrollSpace){
49507         var cm = this.grid.colModel;
49508         var colCount = cm.getColumnCount();
49509         var cols = [];
49510         var width = 0;
49511         var i, w;
49512         for (i = 0; i < colCount; i++){
49513             if(!cm.isHidden(i) && !cm.isFixed(i)){
49514                 w = cm.getColumnWidth(i);
49515                 cols.push(i);
49516                 cols.push(w);
49517                 width += w;
49518             }
49519         }
49520         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49521         if(reserveScrollSpace){
49522             avail -= 17;
49523         }
49524         var frac = (avail - cm.getTotalWidth())/width;
49525         while (cols.length){
49526             w = cols.pop();
49527             i = cols.pop();
49528             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49529         }
49530         this.updateColumns();
49531         this.layout();
49532     },
49533
49534     onRowSelect : function(rowIndex){
49535         var row = this.getRowComposite(rowIndex);
49536         row.addClass("x-grid-row-selected");
49537     },
49538
49539     onRowDeselect : function(rowIndex){
49540         var row = this.getRowComposite(rowIndex);
49541         row.removeClass("x-grid-row-selected");
49542     },
49543
49544     onCellSelect : function(row, col){
49545         var cell = this.getCell(row, col);
49546         if(cell){
49547             Roo.fly(cell).addClass("x-grid-cell-selected");
49548         }
49549     },
49550
49551     onCellDeselect : function(row, col){
49552         var cell = this.getCell(row, col);
49553         if(cell){
49554             Roo.fly(cell).removeClass("x-grid-cell-selected");
49555         }
49556     },
49557
49558     updateHeaderSortState : function(){
49559         
49560         // sort state can be single { field: xxx, direction : yyy}
49561         // or   { xxx=>ASC , yyy : DESC ..... }
49562         
49563         var mstate = {};
49564         if (!this.ds.multiSort) { 
49565             var state = this.ds.getSortState();
49566             if(!state){
49567                 return;
49568             }
49569             mstate[state.field] = state.direction;
49570             // FIXME... - this is not used here.. but might be elsewhere..
49571             this.sortState = state;
49572             
49573         } else {
49574             mstate = this.ds.sortToggle;
49575         }
49576         //remove existing sort classes..
49577         
49578         var sc = this.sortClasses;
49579         var hds = this.el.select(this.headerSelector).removeClass(sc);
49580         
49581         for(var f in mstate) {
49582         
49583             var sortColumn = this.cm.findColumnIndex(f);
49584             
49585             if(sortColumn != -1){
49586                 var sortDir = mstate[f];        
49587                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49588             }
49589         }
49590         
49591          
49592         
49593     },
49594
49595
49596     handleHeaderClick : function(g, index){
49597         if(this.headersDisabled){
49598             return;
49599         }
49600         var dm = g.dataSource, cm = g.colModel;
49601         if(!cm.isSortable(index)){
49602             return;
49603         }
49604         g.stopEditing();
49605         
49606         if (dm.multiSort) {
49607             // update the sortOrder
49608             var so = [];
49609             for(var i = 0; i < cm.config.length; i++ ) {
49610                 
49611                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49612                     continue; // dont' bother, it's not in sort list or being set.
49613                 }
49614                 
49615                 so.push(cm.config[i].dataIndex);
49616             };
49617             dm.sortOrder = so;
49618         }
49619         
49620         
49621         dm.sort(cm.getDataIndex(index));
49622     },
49623
49624
49625     destroy : function(){
49626         if(this.colMenu){
49627             this.colMenu.removeAll();
49628             Roo.menu.MenuMgr.unregister(this.colMenu);
49629             this.colMenu.getEl().remove();
49630             delete this.colMenu;
49631         }
49632         if(this.hmenu){
49633             this.hmenu.removeAll();
49634             Roo.menu.MenuMgr.unregister(this.hmenu);
49635             this.hmenu.getEl().remove();
49636             delete this.hmenu;
49637         }
49638         if(this.grid.enableColumnMove){
49639             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49640             if(dds){
49641                 for(var dd in dds){
49642                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49643                         var elid = dds[dd].dragElId;
49644                         dds[dd].unreg();
49645                         Roo.get(elid).remove();
49646                     } else if(dds[dd].config.isTarget){
49647                         dds[dd].proxyTop.remove();
49648                         dds[dd].proxyBottom.remove();
49649                         dds[dd].unreg();
49650                     }
49651                     if(Roo.dd.DDM.locationCache[dd]){
49652                         delete Roo.dd.DDM.locationCache[dd];
49653                     }
49654                 }
49655                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49656             }
49657         }
49658         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49659         this.bind(null, null);
49660         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49661     },
49662
49663     handleLockChange : function(){
49664         this.refresh(true);
49665     },
49666
49667     onDenyColumnLock : function(){
49668
49669     },
49670
49671     onDenyColumnHide : function(){
49672
49673     },
49674
49675     handleHdMenuClick : function(item){
49676         var index = this.hdCtxIndex;
49677         var cm = this.cm, ds = this.ds;
49678         switch(item.id){
49679             case "asc":
49680                 ds.sort(cm.getDataIndex(index), "ASC");
49681                 break;
49682             case "desc":
49683                 ds.sort(cm.getDataIndex(index), "DESC");
49684                 break;
49685             case "lock":
49686                 var lc = cm.getLockedCount();
49687                 if(cm.getColumnCount(true) <= lc+1){
49688                     this.onDenyColumnLock();
49689                     return;
49690                 }
49691                 if(lc != index){
49692                     cm.setLocked(index, true, true);
49693                     cm.moveColumn(index, lc);
49694                     this.grid.fireEvent("columnmove", index, lc);
49695                 }else{
49696                     cm.setLocked(index, true);
49697                 }
49698             break;
49699             case "unlock":
49700                 var lc = cm.getLockedCount();
49701                 if((lc-1) != index){
49702                     cm.setLocked(index, false, true);
49703                     cm.moveColumn(index, lc-1);
49704                     this.grid.fireEvent("columnmove", index, lc-1);
49705                 }else{
49706                     cm.setLocked(index, false);
49707                 }
49708             break;
49709             default:
49710                 index = cm.getIndexById(item.id.substr(4));
49711                 if(index != -1){
49712                     if(item.checked && cm.getColumnCount(true) <= 1){
49713                         this.onDenyColumnHide();
49714                         return false;
49715                     }
49716                     cm.setHidden(index, item.checked);
49717                 }
49718         }
49719         return true;
49720     },
49721
49722     beforeColMenuShow : function(){
49723         var cm = this.cm,  colCount = cm.getColumnCount();
49724         this.colMenu.removeAll();
49725         for(var i = 0; i < colCount; i++){
49726             this.colMenu.add(new Roo.menu.CheckItem({
49727                 id: "col-"+cm.getColumnId(i),
49728                 text: cm.getColumnHeader(i),
49729                 checked: !cm.isHidden(i),
49730                 hideOnClick:false
49731             }));
49732         }
49733     },
49734
49735     handleHdCtx : function(g, index, e){
49736         e.stopEvent();
49737         var hd = this.getHeaderCell(index);
49738         this.hdCtxIndex = index;
49739         var ms = this.hmenu.items, cm = this.cm;
49740         ms.get("asc").setDisabled(!cm.isSortable(index));
49741         ms.get("desc").setDisabled(!cm.isSortable(index));
49742         if(this.grid.enableColLock !== false){
49743             ms.get("lock").setDisabled(cm.isLocked(index));
49744             ms.get("unlock").setDisabled(!cm.isLocked(index));
49745         }
49746         this.hmenu.show(hd, "tl-bl");
49747     },
49748
49749     handleHdOver : function(e){
49750         var hd = this.findHeaderCell(e.getTarget());
49751         if(hd && !this.headersDisabled){
49752             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49753                this.fly(hd).addClass("x-grid-hd-over");
49754             }
49755         }
49756     },
49757
49758     handleHdOut : function(e){
49759         var hd = this.findHeaderCell(e.getTarget());
49760         if(hd){
49761             this.fly(hd).removeClass("x-grid-hd-over");
49762         }
49763     },
49764
49765     handleSplitDblClick : function(e, t){
49766         var i = this.getCellIndex(t);
49767         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49768             this.autoSizeColumn(i, true);
49769             this.layout();
49770         }
49771     },
49772
49773     render : function(){
49774
49775         var cm = this.cm;
49776         var colCount = cm.getColumnCount();
49777
49778         if(this.grid.monitorWindowResize === true){
49779             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49780         }
49781         var header = this.renderHeaders();
49782         var body = this.templates.body.apply({rows:""});
49783         var html = this.templates.master.apply({
49784             lockedBody: body,
49785             body: body,
49786             lockedHeader: header[0],
49787             header: header[1]
49788         });
49789
49790         //this.updateColumns();
49791
49792         this.grid.getGridEl().dom.innerHTML = html;
49793
49794         this.initElements();
49795         
49796         // a kludge to fix the random scolling effect in webkit
49797         this.el.on("scroll", function() {
49798             this.el.dom.scrollTop=0; // hopefully not recursive..
49799         },this);
49800
49801         this.scroller.on("scroll", this.handleScroll, this);
49802         this.lockedBody.on("mousewheel", this.handleWheel, this);
49803         this.mainBody.on("mousewheel", this.handleWheel, this);
49804
49805         this.mainHd.on("mouseover", this.handleHdOver, this);
49806         this.mainHd.on("mouseout", this.handleHdOut, this);
49807         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49808                 {delegate: "."+this.splitClass});
49809
49810         this.lockedHd.on("mouseover", this.handleHdOver, this);
49811         this.lockedHd.on("mouseout", this.handleHdOut, this);
49812         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49813                 {delegate: "."+this.splitClass});
49814
49815         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49816             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49817         }
49818
49819         this.updateSplitters();
49820
49821         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49822             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49823             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49824         }
49825
49826         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49827             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49828             this.hmenu.add(
49829                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49830                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49831             );
49832             if(this.grid.enableColLock !== false){
49833                 this.hmenu.add('-',
49834                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49835                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49836                 );
49837             }
49838             if(this.grid.enableColumnHide !== false){
49839
49840                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49841                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49842                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49843
49844                 this.hmenu.add('-',
49845                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49846                 );
49847             }
49848             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49849
49850             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49851         }
49852
49853         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49854             this.dd = new Roo.grid.GridDragZone(this.grid, {
49855                 ddGroup : this.grid.ddGroup || 'GridDD'
49856             });
49857         }
49858
49859         /*
49860         for(var i = 0; i < colCount; i++){
49861             if(cm.isHidden(i)){
49862                 this.hideColumn(i);
49863             }
49864             if(cm.config[i].align){
49865                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49866                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49867             }
49868         }*/
49869         
49870         this.updateHeaderSortState();
49871
49872         this.beforeInitialResize();
49873         this.layout(true);
49874
49875         // two part rendering gives faster view to the user
49876         this.renderPhase2.defer(1, this);
49877     },
49878
49879     renderPhase2 : function(){
49880         // render the rows now
49881         this.refresh();
49882         if(this.grid.autoSizeColumns){
49883             this.autoSizeColumns();
49884         }
49885     },
49886
49887     beforeInitialResize : function(){
49888
49889     },
49890
49891     onColumnSplitterMoved : function(i, w){
49892         this.userResized = true;
49893         var cm = this.grid.colModel;
49894         cm.setColumnWidth(i, w, true);
49895         var cid = cm.getColumnId(i);
49896         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49897         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49898         this.updateSplitters();
49899         this.layout();
49900         this.grid.fireEvent("columnresize", i, w);
49901     },
49902
49903     syncRowHeights : function(startIndex, endIndex){
49904         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49905             startIndex = startIndex || 0;
49906             var mrows = this.getBodyTable().rows;
49907             var lrows = this.getLockedTable().rows;
49908             var len = mrows.length-1;
49909             endIndex = Math.min(endIndex || len, len);
49910             for(var i = startIndex; i <= endIndex; i++){
49911                 var m = mrows[i], l = lrows[i];
49912                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49913                 m.style.height = l.style.height = h + "px";
49914             }
49915         }
49916     },
49917
49918     layout : function(initialRender, is2ndPass){
49919         var g = this.grid;
49920         var auto = g.autoHeight;
49921         var scrollOffset = 16;
49922         var c = g.getGridEl(), cm = this.cm,
49923                 expandCol = g.autoExpandColumn,
49924                 gv = this;
49925         //c.beginMeasure();
49926
49927         if(!c.dom.offsetWidth){ // display:none?
49928             if(initialRender){
49929                 this.lockedWrap.show();
49930                 this.mainWrap.show();
49931             }
49932             return;
49933         }
49934
49935         var hasLock = this.cm.isLocked(0);
49936
49937         var tbh = this.headerPanel.getHeight();
49938         var bbh = this.footerPanel.getHeight();
49939
49940         if(auto){
49941             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49942             var newHeight = ch + c.getBorderWidth("tb");
49943             if(g.maxHeight){
49944                 newHeight = Math.min(g.maxHeight, newHeight);
49945             }
49946             c.setHeight(newHeight);
49947         }
49948
49949         if(g.autoWidth){
49950             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49951         }
49952
49953         var s = this.scroller;
49954
49955         var csize = c.getSize(true);
49956
49957         this.el.setSize(csize.width, csize.height);
49958
49959         this.headerPanel.setWidth(csize.width);
49960         this.footerPanel.setWidth(csize.width);
49961
49962         var hdHeight = this.mainHd.getHeight();
49963         var vw = csize.width;
49964         var vh = csize.height - (tbh + bbh);
49965
49966         s.setSize(vw, vh);
49967
49968         var bt = this.getBodyTable();
49969         var ltWidth = hasLock ?
49970                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49971
49972         var scrollHeight = bt.offsetHeight;
49973         var scrollWidth = ltWidth + bt.offsetWidth;
49974         var vscroll = false, hscroll = false;
49975
49976         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49977
49978         var lw = this.lockedWrap, mw = this.mainWrap;
49979         var lb = this.lockedBody, mb = this.mainBody;
49980
49981         setTimeout(function(){
49982             var t = s.dom.offsetTop;
49983             var w = s.dom.clientWidth,
49984                 h = s.dom.clientHeight;
49985
49986             lw.setTop(t);
49987             lw.setSize(ltWidth, h);
49988
49989             mw.setLeftTop(ltWidth, t);
49990             mw.setSize(w-ltWidth, h);
49991
49992             lb.setHeight(h-hdHeight);
49993             mb.setHeight(h-hdHeight);
49994
49995             if(is2ndPass !== true && !gv.userResized && expandCol){
49996                 // high speed resize without full column calculation
49997                 
49998                 var ci = cm.getIndexById(expandCol);
49999                 if (ci < 0) {
50000                     ci = cm.findColumnIndex(expandCol);
50001                 }
50002                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50003                 var expandId = cm.getColumnId(ci);
50004                 var  tw = cm.getTotalWidth(false);
50005                 var currentWidth = cm.getColumnWidth(ci);
50006                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50007                 if(currentWidth != cw){
50008                     cm.setColumnWidth(ci, cw, true);
50009                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50010                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50011                     gv.updateSplitters();
50012                     gv.layout(false, true);
50013                 }
50014             }
50015
50016             if(initialRender){
50017                 lw.show();
50018                 mw.show();
50019             }
50020             //c.endMeasure();
50021         }, 10);
50022     },
50023
50024     onWindowResize : function(){
50025         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50026             return;
50027         }
50028         this.layout();
50029     },
50030
50031     appendFooter : function(parentEl){
50032         return null;
50033     },
50034
50035     sortAscText : "Sort Ascending",
50036     sortDescText : "Sort Descending",
50037     lockText : "Lock Column",
50038     unlockText : "Unlock Column",
50039     columnsText : "Columns"
50040 });
50041
50042
50043 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50044     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50045     this.proxy.el.addClass('x-grid3-col-dd');
50046 };
50047
50048 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50049     handleMouseDown : function(e){
50050
50051     },
50052
50053     callHandleMouseDown : function(e){
50054         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50055     }
50056 });
50057 /*
50058  * Based on:
50059  * Ext JS Library 1.1.1
50060  * Copyright(c) 2006-2007, Ext JS, LLC.
50061  *
50062  * Originally Released Under LGPL - original licence link has changed is not relivant.
50063  *
50064  * Fork - LGPL
50065  * <script type="text/javascript">
50066  */
50067  
50068 // private
50069 // This is a support class used internally by the Grid components
50070 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50071     this.grid = grid;
50072     this.view = grid.getView();
50073     this.proxy = this.view.resizeProxy;
50074     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50075         "gridSplitters" + this.grid.getGridEl().id, {
50076         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50077     });
50078     this.setHandleElId(Roo.id(hd));
50079     this.setOuterHandleElId(Roo.id(hd2));
50080     this.scroll = false;
50081 };
50082 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50083     fly: Roo.Element.fly,
50084
50085     b4StartDrag : function(x, y){
50086         this.view.headersDisabled = true;
50087         this.proxy.setHeight(this.view.mainWrap.getHeight());
50088         var w = this.cm.getColumnWidth(this.cellIndex);
50089         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50090         this.resetConstraints();
50091         this.setXConstraint(minw, 1000);
50092         this.setYConstraint(0, 0);
50093         this.minX = x - minw;
50094         this.maxX = x + 1000;
50095         this.startPos = x;
50096         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50097     },
50098
50099
50100     handleMouseDown : function(e){
50101         ev = Roo.EventObject.setEvent(e);
50102         var t = this.fly(ev.getTarget());
50103         if(t.hasClass("x-grid-split")){
50104             this.cellIndex = this.view.getCellIndex(t.dom);
50105             this.split = t.dom;
50106             this.cm = this.grid.colModel;
50107             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50108                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50109             }
50110         }
50111     },
50112
50113     endDrag : function(e){
50114         this.view.headersDisabled = false;
50115         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50116         var diff = endX - this.startPos;
50117         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50118     },
50119
50120     autoOffset : function(){
50121         this.setDelta(0,0);
50122     }
50123 });/*
50124  * Based on:
50125  * Ext JS Library 1.1.1
50126  * Copyright(c) 2006-2007, Ext JS, LLC.
50127  *
50128  * Originally Released Under LGPL - original licence link has changed is not relivant.
50129  *
50130  * Fork - LGPL
50131  * <script type="text/javascript">
50132  */
50133  
50134 // private
50135 // This is a support class used internally by the Grid components
50136 Roo.grid.GridDragZone = function(grid, config){
50137     this.view = grid.getView();
50138     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50139     if(this.view.lockedBody){
50140         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50141         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50142     }
50143     this.scroll = false;
50144     this.grid = grid;
50145     this.ddel = document.createElement('div');
50146     this.ddel.className = 'x-grid-dd-wrap';
50147 };
50148
50149 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50150     ddGroup : "GridDD",
50151
50152     getDragData : function(e){
50153         var t = Roo.lib.Event.getTarget(e);
50154         var rowIndex = this.view.findRowIndex(t);
50155         if(rowIndex !== false){
50156             var sm = this.grid.selModel;
50157             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50158               //  sm.mouseDown(e, t);
50159             //}
50160             if (e.hasModifier()){
50161                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50162             }
50163             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50164         }
50165         return false;
50166     },
50167
50168     onInitDrag : function(e){
50169         var data = this.dragData;
50170         this.ddel.innerHTML = this.grid.getDragDropText();
50171         this.proxy.update(this.ddel);
50172         // fire start drag?
50173     },
50174
50175     afterRepair : function(){
50176         this.dragging = false;
50177     },
50178
50179     getRepairXY : function(e, data){
50180         return false;
50181     },
50182
50183     onEndDrag : function(data, e){
50184         // fire end drag?
50185     },
50186
50187     onValidDrop : function(dd, e, id){
50188         // fire drag drop?
50189         this.hideProxy();
50190     },
50191
50192     beforeInvalidDrop : function(e, id){
50193
50194     }
50195 });/*
50196  * Based on:
50197  * Ext JS Library 1.1.1
50198  * Copyright(c) 2006-2007, Ext JS, LLC.
50199  *
50200  * Originally Released Under LGPL - original licence link has changed is not relivant.
50201  *
50202  * Fork - LGPL
50203  * <script type="text/javascript">
50204  */
50205  
50206
50207 /**
50208  * @class Roo.grid.ColumnModel
50209  * @extends Roo.util.Observable
50210  * This is the default implementation of a ColumnModel used by the Grid. It defines
50211  * the columns in the grid.
50212  * <br>Usage:<br>
50213  <pre><code>
50214  var colModel = new Roo.grid.ColumnModel([
50215         {header: "Ticker", width: 60, sortable: true, locked: true},
50216         {header: "Company Name", width: 150, sortable: true},
50217         {header: "Market Cap.", width: 100, sortable: true},
50218         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50219         {header: "Employees", width: 100, sortable: true, resizable: false}
50220  ]);
50221  </code></pre>
50222  * <p>
50223  
50224  * The config options listed for this class are options which may appear in each
50225  * individual column definition.
50226  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50227  * @constructor
50228  * @param {Object} config An Array of column config objects. See this class's
50229  * config objects for details.
50230 */
50231 Roo.grid.ColumnModel = function(config){
50232         /**
50233      * The config passed into the constructor
50234      */
50235     this.config = config;
50236     this.lookup = {};
50237
50238     // if no id, create one
50239     // if the column does not have a dataIndex mapping,
50240     // map it to the order it is in the config
50241     for(var i = 0, len = config.length; i < len; i++){
50242         var c = config[i];
50243         if(typeof c.dataIndex == "undefined"){
50244             c.dataIndex = i;
50245         }
50246         if(typeof c.renderer == "string"){
50247             c.renderer = Roo.util.Format[c.renderer];
50248         }
50249         if(typeof c.id == "undefined"){
50250             c.id = Roo.id();
50251         }
50252         if(c.editor && c.editor.xtype){
50253             c.editor  = Roo.factory(c.editor, Roo.grid);
50254         }
50255         if(c.editor && c.editor.isFormField){
50256             c.editor = new Roo.grid.GridEditor(c.editor);
50257         }
50258         this.lookup[c.id] = c;
50259     }
50260
50261     /**
50262      * The width of columns which have no width specified (defaults to 100)
50263      * @type Number
50264      */
50265     this.defaultWidth = 100;
50266
50267     /**
50268      * Default sortable of columns which have no sortable specified (defaults to false)
50269      * @type Boolean
50270      */
50271     this.defaultSortable = false;
50272
50273     this.addEvents({
50274         /**
50275              * @event widthchange
50276              * Fires when the width of a column changes.
50277              * @param {ColumnModel} this
50278              * @param {Number} columnIndex The column index
50279              * @param {Number} newWidth The new width
50280              */
50281             "widthchange": true,
50282         /**
50283              * @event headerchange
50284              * Fires when the text of a header changes.
50285              * @param {ColumnModel} this
50286              * @param {Number} columnIndex The column index
50287              * @param {Number} newText The new header text
50288              */
50289             "headerchange": true,
50290         /**
50291              * @event hiddenchange
50292              * Fires when a column is hidden or "unhidden".
50293              * @param {ColumnModel} this
50294              * @param {Number} columnIndex The column index
50295              * @param {Boolean} hidden true if hidden, false otherwise
50296              */
50297             "hiddenchange": true,
50298             /**
50299          * @event columnmoved
50300          * Fires when a column is moved.
50301          * @param {ColumnModel} this
50302          * @param {Number} oldIndex
50303          * @param {Number} newIndex
50304          */
50305         "columnmoved" : true,
50306         /**
50307          * @event columlockchange
50308          * Fires when a column's locked state is changed
50309          * @param {ColumnModel} this
50310          * @param {Number} colIndex
50311          * @param {Boolean} locked true if locked
50312          */
50313         "columnlockchange" : true
50314     });
50315     Roo.grid.ColumnModel.superclass.constructor.call(this);
50316 };
50317 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50318     /**
50319      * @cfg {String} header The header text to display in the Grid view.
50320      */
50321     /**
50322      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50323      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50324      * specified, the column's index is used as an index into the Record's data Array.
50325      */
50326     /**
50327      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50328      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50329      */
50330     /**
50331      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50332      * Defaults to the value of the {@link #defaultSortable} property.
50333      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50334      */
50335     /**
50336      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50337      */
50338     /**
50339      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50340      */
50341     /**
50342      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50343      */
50344     /**
50345      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50346      */
50347     /**
50348      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50349      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50350      * default renderer uses the raw data value.
50351      */
50352        /**
50353      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50354      */
50355     /**
50356      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50357      */
50358
50359     /**
50360      * Returns the id of the column at the specified index.
50361      * @param {Number} index The column index
50362      * @return {String} the id
50363      */
50364     getColumnId : function(index){
50365         return this.config[index].id;
50366     },
50367
50368     /**
50369      * Returns the column for a specified id.
50370      * @param {String} id The column id
50371      * @return {Object} the column
50372      */
50373     getColumnById : function(id){
50374         return this.lookup[id];
50375     },
50376
50377     
50378     /**
50379      * Returns the column for a specified dataIndex.
50380      * @param {String} dataIndex The column dataIndex
50381      * @return {Object|Boolean} the column or false if not found
50382      */
50383     getColumnByDataIndex: function(dataIndex){
50384         var index = this.findColumnIndex(dataIndex);
50385         return index > -1 ? this.config[index] : false;
50386     },
50387     
50388     /**
50389      * Returns the index for a specified column id.
50390      * @param {String} id The column id
50391      * @return {Number} the index, or -1 if not found
50392      */
50393     getIndexById : function(id){
50394         for(var i = 0, len = this.config.length; i < len; i++){
50395             if(this.config[i].id == id){
50396                 return i;
50397             }
50398         }
50399         return -1;
50400     },
50401     
50402     /**
50403      * Returns the index for a specified column dataIndex.
50404      * @param {String} dataIndex The column dataIndex
50405      * @return {Number} the index, or -1 if not found
50406      */
50407     
50408     findColumnIndex : function(dataIndex){
50409         for(var i = 0, len = this.config.length; i < len; i++){
50410             if(this.config[i].dataIndex == dataIndex){
50411                 return i;
50412             }
50413         }
50414         return -1;
50415     },
50416     
50417     
50418     moveColumn : function(oldIndex, newIndex){
50419         var c = this.config[oldIndex];
50420         this.config.splice(oldIndex, 1);
50421         this.config.splice(newIndex, 0, c);
50422         this.dataMap = null;
50423         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50424     },
50425
50426     isLocked : function(colIndex){
50427         return this.config[colIndex].locked === true;
50428     },
50429
50430     setLocked : function(colIndex, value, suppressEvent){
50431         if(this.isLocked(colIndex) == value){
50432             return;
50433         }
50434         this.config[colIndex].locked = value;
50435         if(!suppressEvent){
50436             this.fireEvent("columnlockchange", this, colIndex, value);
50437         }
50438     },
50439
50440     getTotalLockedWidth : function(){
50441         var totalWidth = 0;
50442         for(var i = 0; i < this.config.length; i++){
50443             if(this.isLocked(i) && !this.isHidden(i)){
50444                 this.totalWidth += this.getColumnWidth(i);
50445             }
50446         }
50447         return totalWidth;
50448     },
50449
50450     getLockedCount : function(){
50451         for(var i = 0, len = this.config.length; i < len; i++){
50452             if(!this.isLocked(i)){
50453                 return i;
50454             }
50455         }
50456     },
50457
50458     /**
50459      * Returns the number of columns.
50460      * @return {Number}
50461      */
50462     getColumnCount : function(visibleOnly){
50463         if(visibleOnly === true){
50464             var c = 0;
50465             for(var i = 0, len = this.config.length; i < len; i++){
50466                 if(!this.isHidden(i)){
50467                     c++;
50468                 }
50469             }
50470             return c;
50471         }
50472         return this.config.length;
50473     },
50474
50475     /**
50476      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50477      * @param {Function} fn
50478      * @param {Object} scope (optional)
50479      * @return {Array} result
50480      */
50481     getColumnsBy : function(fn, scope){
50482         var r = [];
50483         for(var i = 0, len = this.config.length; i < len; i++){
50484             var c = this.config[i];
50485             if(fn.call(scope||this, c, i) === true){
50486                 r[r.length] = c;
50487             }
50488         }
50489         return r;
50490     },
50491
50492     /**
50493      * Returns true if the specified column is sortable.
50494      * @param {Number} col The column index
50495      * @return {Boolean}
50496      */
50497     isSortable : function(col){
50498         if(typeof this.config[col].sortable == "undefined"){
50499             return this.defaultSortable;
50500         }
50501         return this.config[col].sortable;
50502     },
50503
50504     /**
50505      * Returns the rendering (formatting) function defined for the column.
50506      * @param {Number} col The column index.
50507      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50508      */
50509     getRenderer : function(col){
50510         if(!this.config[col].renderer){
50511             return Roo.grid.ColumnModel.defaultRenderer;
50512         }
50513         return this.config[col].renderer;
50514     },
50515
50516     /**
50517      * Sets the rendering (formatting) function for a column.
50518      * @param {Number} col The column index
50519      * @param {Function} fn The function to use to process the cell's raw data
50520      * to return HTML markup for the grid view. The render function is called with
50521      * the following parameters:<ul>
50522      * <li>Data value.</li>
50523      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50524      * <li>css A CSS style string to apply to the table cell.</li>
50525      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50526      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50527      * <li>Row index</li>
50528      * <li>Column index</li>
50529      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50530      */
50531     setRenderer : function(col, fn){
50532         this.config[col].renderer = fn;
50533     },
50534
50535     /**
50536      * Returns the width for the specified column.
50537      * @param {Number} col The column index
50538      * @return {Number}
50539      */
50540     getColumnWidth : function(col){
50541         return this.config[col].width * 1 || this.defaultWidth;
50542     },
50543
50544     /**
50545      * Sets the width for a column.
50546      * @param {Number} col The column index
50547      * @param {Number} width The new width
50548      */
50549     setColumnWidth : function(col, width, suppressEvent){
50550         this.config[col].width = width;
50551         this.totalWidth = null;
50552         if(!suppressEvent){
50553              this.fireEvent("widthchange", this, col, width);
50554         }
50555     },
50556
50557     /**
50558      * Returns the total width of all columns.
50559      * @param {Boolean} includeHidden True to include hidden column widths
50560      * @return {Number}
50561      */
50562     getTotalWidth : function(includeHidden){
50563         if(!this.totalWidth){
50564             this.totalWidth = 0;
50565             for(var i = 0, len = this.config.length; i < len; i++){
50566                 if(includeHidden || !this.isHidden(i)){
50567                     this.totalWidth += this.getColumnWidth(i);
50568                 }
50569             }
50570         }
50571         return this.totalWidth;
50572     },
50573
50574     /**
50575      * Returns the header for the specified column.
50576      * @param {Number} col The column index
50577      * @return {String}
50578      */
50579     getColumnHeader : function(col){
50580         return this.config[col].header;
50581     },
50582
50583     /**
50584      * Sets the header for a column.
50585      * @param {Number} col The column index
50586      * @param {String} header The new header
50587      */
50588     setColumnHeader : function(col, header){
50589         this.config[col].header = header;
50590         this.fireEvent("headerchange", this, col, header);
50591     },
50592
50593     /**
50594      * Returns the tooltip for the specified column.
50595      * @param {Number} col The column index
50596      * @return {String}
50597      */
50598     getColumnTooltip : function(col){
50599             return this.config[col].tooltip;
50600     },
50601     /**
50602      * Sets the tooltip for a column.
50603      * @param {Number} col The column index
50604      * @param {String} tooltip The new tooltip
50605      */
50606     setColumnTooltip : function(col, tooltip){
50607             this.config[col].tooltip = tooltip;
50608     },
50609
50610     /**
50611      * Returns the dataIndex for the specified column.
50612      * @param {Number} col The column index
50613      * @return {Number}
50614      */
50615     getDataIndex : function(col){
50616         return this.config[col].dataIndex;
50617     },
50618
50619     /**
50620      * Sets the dataIndex for a column.
50621      * @param {Number} col The column index
50622      * @param {Number} dataIndex The new dataIndex
50623      */
50624     setDataIndex : function(col, dataIndex){
50625         this.config[col].dataIndex = dataIndex;
50626     },
50627
50628     
50629     
50630     /**
50631      * Returns true if the cell is editable.
50632      * @param {Number} colIndex The column index
50633      * @param {Number} rowIndex The row index
50634      * @return {Boolean}
50635      */
50636     isCellEditable : function(colIndex, rowIndex){
50637         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50638     },
50639
50640     /**
50641      * Returns the editor defined for the cell/column.
50642      * return false or null to disable editing.
50643      * @param {Number} colIndex The column index
50644      * @param {Number} rowIndex The row index
50645      * @return {Object}
50646      */
50647     getCellEditor : function(colIndex, rowIndex){
50648         return this.config[colIndex].editor;
50649     },
50650
50651     /**
50652      * Sets if a column is editable.
50653      * @param {Number} col The column index
50654      * @param {Boolean} editable True if the column is editable
50655      */
50656     setEditable : function(col, editable){
50657         this.config[col].editable = editable;
50658     },
50659
50660
50661     /**
50662      * Returns true if the column is hidden.
50663      * @param {Number} colIndex The column index
50664      * @return {Boolean}
50665      */
50666     isHidden : function(colIndex){
50667         return this.config[colIndex].hidden;
50668     },
50669
50670
50671     /**
50672      * Returns true if the column width cannot be changed
50673      */
50674     isFixed : function(colIndex){
50675         return this.config[colIndex].fixed;
50676     },
50677
50678     /**
50679      * Returns true if the column can be resized
50680      * @return {Boolean}
50681      */
50682     isResizable : function(colIndex){
50683         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50684     },
50685     /**
50686      * Sets if a column is hidden.
50687      * @param {Number} colIndex The column index
50688      * @param {Boolean} hidden True if the column is hidden
50689      */
50690     setHidden : function(colIndex, hidden){
50691         this.config[colIndex].hidden = hidden;
50692         this.totalWidth = null;
50693         this.fireEvent("hiddenchange", this, colIndex, hidden);
50694     },
50695
50696     /**
50697      * Sets the editor for a column.
50698      * @param {Number} col The column index
50699      * @param {Object} editor The editor object
50700      */
50701     setEditor : function(col, editor){
50702         this.config[col].editor = editor;
50703     }
50704 });
50705
50706 Roo.grid.ColumnModel.defaultRenderer = function(value){
50707         if(typeof value == "string" && value.length < 1){
50708             return "&#160;";
50709         }
50710         return value;
50711 };
50712
50713 // Alias for backwards compatibility
50714 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50715 /*
50716  * Based on:
50717  * Ext JS Library 1.1.1
50718  * Copyright(c) 2006-2007, Ext JS, LLC.
50719  *
50720  * Originally Released Under LGPL - original licence link has changed is not relivant.
50721  *
50722  * Fork - LGPL
50723  * <script type="text/javascript">
50724  */
50725
50726 /**
50727  * @class Roo.grid.AbstractSelectionModel
50728  * @extends Roo.util.Observable
50729  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50730  * implemented by descendant classes.  This class should not be directly instantiated.
50731  * @constructor
50732  */
50733 Roo.grid.AbstractSelectionModel = function(){
50734     this.locked = false;
50735     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50736 };
50737
50738 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50739     /** @ignore Called by the grid automatically. Do not call directly. */
50740     init : function(grid){
50741         this.grid = grid;
50742         this.initEvents();
50743     },
50744
50745     /**
50746      * Locks the selections.
50747      */
50748     lock : function(){
50749         this.locked = true;
50750     },
50751
50752     /**
50753      * Unlocks the selections.
50754      */
50755     unlock : function(){
50756         this.locked = false;
50757     },
50758
50759     /**
50760      * Returns true if the selections are locked.
50761      * @return {Boolean}
50762      */
50763     isLocked : function(){
50764         return this.locked;
50765     }
50766 });/*
50767  * Based on:
50768  * Ext JS Library 1.1.1
50769  * Copyright(c) 2006-2007, Ext JS, LLC.
50770  *
50771  * Originally Released Under LGPL - original licence link has changed is not relivant.
50772  *
50773  * Fork - LGPL
50774  * <script type="text/javascript">
50775  */
50776 /**
50777  * @extends Roo.grid.AbstractSelectionModel
50778  * @class Roo.grid.RowSelectionModel
50779  * The default SelectionModel used by {@link Roo.grid.Grid}.
50780  * It supports multiple selections and keyboard selection/navigation. 
50781  * @constructor
50782  * @param {Object} config
50783  */
50784 Roo.grid.RowSelectionModel = function(config){
50785     Roo.apply(this, config);
50786     this.selections = new Roo.util.MixedCollection(false, function(o){
50787         return o.id;
50788     });
50789
50790     this.last = false;
50791     this.lastActive = false;
50792
50793     this.addEvents({
50794         /**
50795              * @event selectionchange
50796              * Fires when the selection changes
50797              * @param {SelectionModel} this
50798              */
50799             "selectionchange" : true,
50800         /**
50801              * @event afterselectionchange
50802              * Fires after the selection changes (eg. by key press or clicking)
50803              * @param {SelectionModel} this
50804              */
50805             "afterselectionchange" : true,
50806         /**
50807              * @event beforerowselect
50808              * Fires when a row is selected being selected, return false to cancel.
50809              * @param {SelectionModel} this
50810              * @param {Number} rowIndex The selected index
50811              * @param {Boolean} keepExisting False if other selections will be cleared
50812              */
50813             "beforerowselect" : true,
50814         /**
50815              * @event rowselect
50816              * Fires when a row is selected.
50817              * @param {SelectionModel} this
50818              * @param {Number} rowIndex The selected index
50819              * @param {Roo.data.Record} r The record
50820              */
50821             "rowselect" : true,
50822         /**
50823              * @event rowdeselect
50824              * Fires when a row is deselected.
50825              * @param {SelectionModel} this
50826              * @param {Number} rowIndex The selected index
50827              */
50828         "rowdeselect" : true
50829     });
50830     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50831     this.locked = false;
50832 };
50833
50834 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50835     /**
50836      * @cfg {Boolean} singleSelect
50837      * True to allow selection of only one row at a time (defaults to false)
50838      */
50839     singleSelect : false,
50840
50841     // private
50842     initEvents : function(){
50843
50844         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50845             this.grid.on("mousedown", this.handleMouseDown, this);
50846         }else{ // allow click to work like normal
50847             this.grid.on("rowclick", this.handleDragableRowClick, this);
50848         }
50849
50850         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50851             "up" : function(e){
50852                 if(!e.shiftKey){
50853                     this.selectPrevious(e.shiftKey);
50854                 }else if(this.last !== false && this.lastActive !== false){
50855                     var last = this.last;
50856                     this.selectRange(this.last,  this.lastActive-1);
50857                     this.grid.getView().focusRow(this.lastActive);
50858                     if(last !== false){
50859                         this.last = last;
50860                     }
50861                 }else{
50862                     this.selectFirstRow();
50863                 }
50864                 this.fireEvent("afterselectionchange", this);
50865             },
50866             "down" : function(e){
50867                 if(!e.shiftKey){
50868                     this.selectNext(e.shiftKey);
50869                 }else if(this.last !== false && this.lastActive !== false){
50870                     var last = this.last;
50871                     this.selectRange(this.last,  this.lastActive+1);
50872                     this.grid.getView().focusRow(this.lastActive);
50873                     if(last !== false){
50874                         this.last = last;
50875                     }
50876                 }else{
50877                     this.selectFirstRow();
50878                 }
50879                 this.fireEvent("afterselectionchange", this);
50880             },
50881             scope: this
50882         });
50883
50884         var view = this.grid.view;
50885         view.on("refresh", this.onRefresh, this);
50886         view.on("rowupdated", this.onRowUpdated, this);
50887         view.on("rowremoved", this.onRemove, this);
50888     },
50889
50890     // private
50891     onRefresh : function(){
50892         var ds = this.grid.dataSource, i, v = this.grid.view;
50893         var s = this.selections;
50894         s.each(function(r){
50895             if((i = ds.indexOfId(r.id)) != -1){
50896                 v.onRowSelect(i);
50897             }else{
50898                 s.remove(r);
50899             }
50900         });
50901     },
50902
50903     // private
50904     onRemove : function(v, index, r){
50905         this.selections.remove(r);
50906     },
50907
50908     // private
50909     onRowUpdated : function(v, index, r){
50910         if(this.isSelected(r)){
50911             v.onRowSelect(index);
50912         }
50913     },
50914
50915     /**
50916      * Select records.
50917      * @param {Array} records The records to select
50918      * @param {Boolean} keepExisting (optional) True to keep existing selections
50919      */
50920     selectRecords : function(records, keepExisting){
50921         if(!keepExisting){
50922             this.clearSelections();
50923         }
50924         var ds = this.grid.dataSource;
50925         for(var i = 0, len = records.length; i < len; i++){
50926             this.selectRow(ds.indexOf(records[i]), true);
50927         }
50928     },
50929
50930     /**
50931      * Gets the number of selected rows.
50932      * @return {Number}
50933      */
50934     getCount : function(){
50935         return this.selections.length;
50936     },
50937
50938     /**
50939      * Selects the first row in the grid.
50940      */
50941     selectFirstRow : function(){
50942         this.selectRow(0);
50943     },
50944
50945     /**
50946      * Select the last row.
50947      * @param {Boolean} keepExisting (optional) True to keep existing selections
50948      */
50949     selectLastRow : function(keepExisting){
50950         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50951     },
50952
50953     /**
50954      * Selects the row immediately following the last selected row.
50955      * @param {Boolean} keepExisting (optional) True to keep existing selections
50956      */
50957     selectNext : function(keepExisting){
50958         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50959             this.selectRow(this.last+1, keepExisting);
50960             this.grid.getView().focusRow(this.last);
50961         }
50962     },
50963
50964     /**
50965      * Selects the row that precedes the last selected row.
50966      * @param {Boolean} keepExisting (optional) True to keep existing selections
50967      */
50968     selectPrevious : function(keepExisting){
50969         if(this.last){
50970             this.selectRow(this.last-1, keepExisting);
50971             this.grid.getView().focusRow(this.last);
50972         }
50973     },
50974
50975     /**
50976      * Returns the selected records
50977      * @return {Array} Array of selected records
50978      */
50979     getSelections : function(){
50980         return [].concat(this.selections.items);
50981     },
50982
50983     /**
50984      * Returns the first selected record.
50985      * @return {Record}
50986      */
50987     getSelected : function(){
50988         return this.selections.itemAt(0);
50989     },
50990
50991
50992     /**
50993      * Clears all selections.
50994      */
50995     clearSelections : function(fast){
50996         if(this.locked) return;
50997         if(fast !== true){
50998             var ds = this.grid.dataSource;
50999             var s = this.selections;
51000             s.each(function(r){
51001                 this.deselectRow(ds.indexOfId(r.id));
51002             }, this);
51003             s.clear();
51004         }else{
51005             this.selections.clear();
51006         }
51007         this.last = false;
51008     },
51009
51010
51011     /**
51012      * Selects all rows.
51013      */
51014     selectAll : function(){
51015         if(this.locked) return;
51016         this.selections.clear();
51017         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51018             this.selectRow(i, true);
51019         }
51020     },
51021
51022     /**
51023      * Returns True if there is a selection.
51024      * @return {Boolean}
51025      */
51026     hasSelection : function(){
51027         return this.selections.length > 0;
51028     },
51029
51030     /**
51031      * Returns True if the specified row is selected.
51032      * @param {Number/Record} record The record or index of the record to check
51033      * @return {Boolean}
51034      */
51035     isSelected : function(index){
51036         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51037         return (r && this.selections.key(r.id) ? true : false);
51038     },
51039
51040     /**
51041      * Returns True if the specified record id is selected.
51042      * @param {String} id The id of record to check
51043      * @return {Boolean}
51044      */
51045     isIdSelected : function(id){
51046         return (this.selections.key(id) ? true : false);
51047     },
51048
51049     // private
51050     handleMouseDown : function(e, t){
51051         var view = this.grid.getView(), rowIndex;
51052         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51053             return;
51054         };
51055         if(e.shiftKey && this.last !== false){
51056             var last = this.last;
51057             this.selectRange(last, rowIndex, e.ctrlKey);
51058             this.last = last; // reset the last
51059             view.focusRow(rowIndex);
51060         }else{
51061             var isSelected = this.isSelected(rowIndex);
51062             if(e.button !== 0 && isSelected){
51063                 view.focusRow(rowIndex);
51064             }else if(e.ctrlKey && isSelected){
51065                 this.deselectRow(rowIndex);
51066             }else if(!isSelected){
51067                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51068                 view.focusRow(rowIndex);
51069             }
51070         }
51071         this.fireEvent("afterselectionchange", this);
51072     },
51073     // private
51074     handleDragableRowClick :  function(grid, rowIndex, e) 
51075     {
51076         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51077             this.selectRow(rowIndex, false);
51078             grid.view.focusRow(rowIndex);
51079              this.fireEvent("afterselectionchange", this);
51080         }
51081     },
51082     
51083     /**
51084      * Selects multiple rows.
51085      * @param {Array} rows Array of the indexes of the row to select
51086      * @param {Boolean} keepExisting (optional) True to keep existing selections
51087      */
51088     selectRows : function(rows, keepExisting){
51089         if(!keepExisting){
51090             this.clearSelections();
51091         }
51092         for(var i = 0, len = rows.length; i < len; i++){
51093             this.selectRow(rows[i], true);
51094         }
51095     },
51096
51097     /**
51098      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51099      * @param {Number} startRow The index of the first row in the range
51100      * @param {Number} endRow The index of the last row in the range
51101      * @param {Boolean} keepExisting (optional) True to retain existing selections
51102      */
51103     selectRange : function(startRow, endRow, keepExisting){
51104         if(this.locked) return;
51105         if(!keepExisting){
51106             this.clearSelections();
51107         }
51108         if(startRow <= endRow){
51109             for(var i = startRow; i <= endRow; i++){
51110                 this.selectRow(i, true);
51111             }
51112         }else{
51113             for(var i = startRow; i >= endRow; i--){
51114                 this.selectRow(i, true);
51115             }
51116         }
51117     },
51118
51119     /**
51120      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51121      * @param {Number} startRow The index of the first row in the range
51122      * @param {Number} endRow The index of the last row in the range
51123      */
51124     deselectRange : function(startRow, endRow, preventViewNotify){
51125         if(this.locked) return;
51126         for(var i = startRow; i <= endRow; i++){
51127             this.deselectRow(i, preventViewNotify);
51128         }
51129     },
51130
51131     /**
51132      * Selects a row.
51133      * @param {Number} row The index of the row to select
51134      * @param {Boolean} keepExisting (optional) True to keep existing selections
51135      */
51136     selectRow : function(index, keepExisting, preventViewNotify){
51137         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51138         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51139             if(!keepExisting || this.singleSelect){
51140                 this.clearSelections();
51141             }
51142             var r = this.grid.dataSource.getAt(index);
51143             this.selections.add(r);
51144             this.last = this.lastActive = index;
51145             if(!preventViewNotify){
51146                 this.grid.getView().onRowSelect(index);
51147             }
51148             this.fireEvent("rowselect", this, index, r);
51149             this.fireEvent("selectionchange", this);
51150         }
51151     },
51152
51153     /**
51154      * Deselects a row.
51155      * @param {Number} row The index of the row to deselect
51156      */
51157     deselectRow : function(index, preventViewNotify){
51158         if(this.locked) return;
51159         if(this.last == index){
51160             this.last = false;
51161         }
51162         if(this.lastActive == index){
51163             this.lastActive = false;
51164         }
51165         var r = this.grid.dataSource.getAt(index);
51166         this.selections.remove(r);
51167         if(!preventViewNotify){
51168             this.grid.getView().onRowDeselect(index);
51169         }
51170         this.fireEvent("rowdeselect", this, index);
51171         this.fireEvent("selectionchange", this);
51172     },
51173
51174     // private
51175     restoreLast : function(){
51176         if(this._last){
51177             this.last = this._last;
51178         }
51179     },
51180
51181     // private
51182     acceptsNav : function(row, col, cm){
51183         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51184     },
51185
51186     // private
51187     onEditorKey : function(field, e){
51188         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51189         if(k == e.TAB){
51190             e.stopEvent();
51191             ed.completeEdit();
51192             if(e.shiftKey){
51193                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51194             }else{
51195                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51196             }
51197         }else if(k == e.ENTER && !e.ctrlKey){
51198             e.stopEvent();
51199             ed.completeEdit();
51200             if(e.shiftKey){
51201                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51202             }else{
51203                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51204             }
51205         }else if(k == e.ESC){
51206             ed.cancelEdit();
51207         }
51208         if(newCell){
51209             g.startEditing(newCell[0], newCell[1]);
51210         }
51211     }
51212 });/*
51213  * Based on:
51214  * Ext JS Library 1.1.1
51215  * Copyright(c) 2006-2007, Ext JS, LLC.
51216  *
51217  * Originally Released Under LGPL - original licence link has changed is not relivant.
51218  *
51219  * Fork - LGPL
51220  * <script type="text/javascript">
51221  */
51222 /**
51223  * @class Roo.grid.CellSelectionModel
51224  * @extends Roo.grid.AbstractSelectionModel
51225  * This class provides the basic implementation for cell selection in a grid.
51226  * @constructor
51227  * @param {Object} config The object containing the configuration of this model.
51228  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51229  */
51230 Roo.grid.CellSelectionModel = function(config){
51231     Roo.apply(this, config);
51232
51233     this.selection = null;
51234
51235     this.addEvents({
51236         /**
51237              * @event beforerowselect
51238              * Fires before 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             "beforecellselect" : true,
51244         /**
51245              * @event cellselect
51246              * Fires when a cell is selected.
51247              * @param {SelectionModel} this
51248              * @param {Number} rowIndex The selected row index
51249              * @param {Number} colIndex The selected cell index
51250              */
51251             "cellselect" : true,
51252         /**
51253              * @event selectionchange
51254              * Fires when the active selection changes.
51255              * @param {SelectionModel} this
51256              * @param {Object} selection null for no selection or an object (o) with two properties
51257                 <ul>
51258                 <li>o.record: the record object for the row the selection is in</li>
51259                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51260                 </ul>
51261              */
51262             "selectionchange" : true,
51263         /**
51264              * @event tabend
51265              * Fires when the tab (or enter) was pressed on the last editable cell
51266              * You can use this to trigger add new row.
51267              * @param {SelectionModel} this
51268              */
51269             "tabend" : true
51270     });
51271     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51272 };
51273
51274 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51275     
51276     enter_is_tab: false,
51277
51278     /** @ignore */
51279     initEvents : function(){
51280         this.grid.on("mousedown", this.handleMouseDown, this);
51281         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51282         var view = this.grid.view;
51283         view.on("refresh", this.onViewChange, this);
51284         view.on("rowupdated", this.onRowUpdated, this);
51285         view.on("beforerowremoved", this.clearSelections, this);
51286         view.on("beforerowsinserted", this.clearSelections, this);
51287         if(this.grid.isEditor){
51288             this.grid.on("beforeedit", this.beforeEdit,  this);
51289         }
51290     },
51291
51292         //private
51293     beforeEdit : function(e){
51294         this.select(e.row, e.column, false, true, e.record);
51295     },
51296
51297         //private
51298     onRowUpdated : function(v, index, r){
51299         if(this.selection && this.selection.record == r){
51300             v.onCellSelect(index, this.selection.cell[1]);
51301         }
51302     },
51303
51304         //private
51305     onViewChange : function(){
51306         this.clearSelections(true);
51307     },
51308
51309         /**
51310          * Returns the currently selected cell,.
51311          * @return {Array} The selected cell (row, column) or null if none selected.
51312          */
51313     getSelectedCell : function(){
51314         return this.selection ? this.selection.cell : null;
51315     },
51316
51317     /**
51318      * Clears all selections.
51319      * @param {Boolean} true to prevent the gridview from being notified about the change.
51320      */
51321     clearSelections : function(preventNotify){
51322         var s = this.selection;
51323         if(s){
51324             if(preventNotify !== true){
51325                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51326             }
51327             this.selection = null;
51328             this.fireEvent("selectionchange", this, null);
51329         }
51330     },
51331
51332     /**
51333      * Returns true if there is a selection.
51334      * @return {Boolean}
51335      */
51336     hasSelection : function(){
51337         return this.selection ? true : false;
51338     },
51339
51340     /** @ignore */
51341     handleMouseDown : function(e, t){
51342         var v = this.grid.getView();
51343         if(this.isLocked()){
51344             return;
51345         };
51346         var row = v.findRowIndex(t);
51347         var cell = v.findCellIndex(t);
51348         if(row !== false && cell !== false){
51349             this.select(row, cell);
51350         }
51351     },
51352
51353     /**
51354      * Selects a cell.
51355      * @param {Number} rowIndex
51356      * @param {Number} collIndex
51357      */
51358     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51359         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51360             this.clearSelections();
51361             r = r || this.grid.dataSource.getAt(rowIndex);
51362             this.selection = {
51363                 record : r,
51364                 cell : [rowIndex, colIndex]
51365             };
51366             if(!preventViewNotify){
51367                 var v = this.grid.getView();
51368                 v.onCellSelect(rowIndex, colIndex);
51369                 if(preventFocus !== true){
51370                     v.focusCell(rowIndex, colIndex);
51371                 }
51372             }
51373             this.fireEvent("cellselect", this, rowIndex, colIndex);
51374             this.fireEvent("selectionchange", this, this.selection);
51375         }
51376     },
51377
51378         //private
51379     isSelectable : function(rowIndex, colIndex, cm){
51380         return !cm.isHidden(colIndex);
51381     },
51382
51383     /** @ignore */
51384     handleKeyDown : function(e){
51385         //Roo.log('Cell Sel Model handleKeyDown');
51386         if(!e.isNavKeyPress()){
51387             return;
51388         }
51389         var g = this.grid, s = this.selection;
51390         if(!s){
51391             e.stopEvent();
51392             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51393             if(cell){
51394                 this.select(cell[0], cell[1]);
51395             }
51396             return;
51397         }
51398         var sm = this;
51399         var walk = function(row, col, step){
51400             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51401         };
51402         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51403         var newCell;
51404
51405       
51406
51407         switch(k){
51408             case e.TAB:
51409                 // handled by onEditorKey
51410                 if (g.isEditor && g.editing) {
51411                     return;
51412                 }
51413                 if(e.shiftKey) {
51414                     newCell = walk(r, c-1, -1);
51415                 } else {
51416                     newCell = walk(r, c+1, 1);
51417                 }
51418                 break;
51419             
51420             case e.DOWN:
51421                newCell = walk(r+1, c, 1);
51422                 break;
51423             
51424             case e.UP:
51425                 newCell = walk(r-1, c, -1);
51426                 break;
51427             
51428             case e.RIGHT:
51429                 newCell = walk(r, c+1, 1);
51430                 break;
51431             
51432             case e.LEFT:
51433                 newCell = walk(r, c-1, -1);
51434                 break;
51435             
51436             case e.ENTER:
51437                 
51438                 if(g.isEditor && !g.editing){
51439                    g.startEditing(r, c);
51440                    e.stopEvent();
51441                    return;
51442                 }
51443                 
51444                 
51445              break;
51446         };
51447         if(newCell){
51448             this.select(newCell[0], newCell[1]);
51449             e.stopEvent();
51450             
51451         }
51452     },
51453
51454     acceptsNav : function(row, col, cm){
51455         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51456     },
51457     /**
51458      * Selects a cell.
51459      * @param {Number} field (not used) - as it's normally used as a listener
51460      * @param {Number} e - event - fake it by using
51461      *
51462      * var e = Roo.EventObjectImpl.prototype;
51463      * e.keyCode = e.TAB
51464      *
51465      * 
51466      */
51467     onEditorKey : function(field, e){
51468         
51469         var k = e.getKey(),
51470             newCell,
51471             g = this.grid,
51472             ed = g.activeEditor,
51473             forward = false;
51474         ///Roo.log('onEditorKey' + k);
51475         
51476         
51477         if (this.enter_is_tab && k == e.ENTER) {
51478             k = e.TAB;
51479         }
51480         
51481         if(k == e.TAB){
51482             if(e.shiftKey){
51483                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51484             }else{
51485                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51486                 forward = true;
51487             }
51488             
51489             e.stopEvent();
51490             
51491         }else if(k == e.ENTER &&  !e.ctrlKey){
51492             ed.completeEdit();
51493             e.stopEvent();
51494             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51495         }else if(k == e.ESC){
51496             ed.cancelEdit();
51497         }
51498         
51499         
51500         if(newCell){
51501             //Roo.log('next cell after edit');
51502             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51503         } else if (forward) {
51504             // tabbed past last
51505             this.fireEvent.defer(100, this, ['tabend',this]);
51506         }
51507     }
51508 });/*
51509  * Based on:
51510  * Ext JS Library 1.1.1
51511  * Copyright(c) 2006-2007, Ext JS, LLC.
51512  *
51513  * Originally Released Under LGPL - original licence link has changed is not relivant.
51514  *
51515  * Fork - LGPL
51516  * <script type="text/javascript">
51517  */
51518  
51519 /**
51520  * @class Roo.grid.EditorGrid
51521  * @extends Roo.grid.Grid
51522  * Class for creating and editable grid.
51523  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51524  * The container MUST have some type of size defined for the grid to fill. The container will be 
51525  * automatically set to position relative if it isn't already.
51526  * @param {Object} dataSource The data model to bind to
51527  * @param {Object} colModel The column model with info about this grid's columns
51528  */
51529 Roo.grid.EditorGrid = function(container, config){
51530     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51531     this.getGridEl().addClass("xedit-grid");
51532
51533     if(!this.selModel){
51534         this.selModel = new Roo.grid.CellSelectionModel();
51535     }
51536
51537     this.activeEditor = null;
51538
51539         this.addEvents({
51540             /**
51541              * @event beforeedit
51542              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51543              * <ul style="padding:5px;padding-left:16px;">
51544              * <li>grid - This grid</li>
51545              * <li>record - The record being edited</li>
51546              * <li>field - The field name being edited</li>
51547              * <li>value - The value for the field being edited.</li>
51548              * <li>row - The grid row index</li>
51549              * <li>column - The grid column index</li>
51550              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51551              * </ul>
51552              * @param {Object} e An edit event (see above for description)
51553              */
51554             "beforeedit" : true,
51555             /**
51556              * @event afteredit
51557              * Fires after a cell is edited. <br />
51558              * <ul style="padding:5px;padding-left:16px;">
51559              * <li>grid - This grid</li>
51560              * <li>record - The record being edited</li>
51561              * <li>field - The field name being edited</li>
51562              * <li>value - The value being set</li>
51563              * <li>originalValue - The original value for the field, before the edit.</li>
51564              * <li>row - The grid row index</li>
51565              * <li>column - The grid column index</li>
51566              * </ul>
51567              * @param {Object} e An edit event (see above for description)
51568              */
51569             "afteredit" : true,
51570             /**
51571              * @event validateedit
51572              * Fires after a cell is edited, but before the value is set in the record. 
51573          * You can use this to modify the value being set in the field, Return false
51574              * to cancel the change. The edit event object has the following properties <br />
51575              * <ul style="padding:5px;padding-left:16px;">
51576          * <li>editor - This editor</li>
51577              * <li>grid - This grid</li>
51578              * <li>record - The record being edited</li>
51579              * <li>field - The field name being edited</li>
51580              * <li>value - The value being set</li>
51581              * <li>originalValue - The original value for the field, before the edit.</li>
51582              * <li>row - The grid row index</li>
51583              * <li>column - The grid column index</li>
51584              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51585              * </ul>
51586              * @param {Object} e An edit event (see above for description)
51587              */
51588             "validateedit" : true
51589         });
51590     this.on("bodyscroll", this.stopEditing,  this);
51591     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51592 };
51593
51594 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51595     /**
51596      * @cfg {Number} clicksToEdit
51597      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51598      */
51599     clicksToEdit: 2,
51600
51601     // private
51602     isEditor : true,
51603     // private
51604     trackMouseOver: false, // causes very odd FF errors
51605
51606     onCellDblClick : function(g, row, col){
51607         this.startEditing(row, col);
51608     },
51609
51610     onEditComplete : function(ed, value, startValue){
51611         this.editing = false;
51612         this.activeEditor = null;
51613         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51614         var r = ed.record;
51615         var field = this.colModel.getDataIndex(ed.col);
51616         var e = {
51617             grid: this,
51618             record: r,
51619             field: field,
51620             originalValue: startValue,
51621             value: value,
51622             row: ed.row,
51623             column: ed.col,
51624             cancel:false,
51625             editor: ed
51626         };
51627         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51628         cell.show();
51629           
51630         if(String(value) !== String(startValue)){
51631             
51632             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51633                 r.set(field, e.value);
51634                 // if we are dealing with a combo box..
51635                 // then we also set the 'name' colum to be the displayField
51636                 if (ed.field.displayField && ed.field.name) {
51637                     r.set(ed.field.name, ed.field.el.dom.value);
51638                 }
51639                 
51640                 delete e.cancel; //?? why!!!
51641                 this.fireEvent("afteredit", e);
51642             }
51643         } else {
51644             this.fireEvent("afteredit", e); // always fire it!
51645         }
51646         this.view.focusCell(ed.row, ed.col);
51647     },
51648
51649     /**
51650      * Starts editing the specified for the specified row/column
51651      * @param {Number} rowIndex
51652      * @param {Number} colIndex
51653      */
51654     startEditing : function(row, col){
51655         this.stopEditing();
51656         if(this.colModel.isCellEditable(col, row)){
51657             this.view.ensureVisible(row, col, true);
51658           
51659             var r = this.dataSource.getAt(row);
51660             var field = this.colModel.getDataIndex(col);
51661             var cell = Roo.get(this.view.getCell(row,col));
51662             var e = {
51663                 grid: this,
51664                 record: r,
51665                 field: field,
51666                 value: r.data[field],
51667                 row: row,
51668                 column: col,
51669                 cancel:false 
51670             };
51671             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51672                 this.editing = true;
51673                 var ed = this.colModel.getCellEditor(col, row);
51674                 
51675                 if (!ed) {
51676                     return;
51677                 }
51678                 if(!ed.rendered){
51679                     ed.render(ed.parentEl || document.body);
51680                 }
51681                 ed.field.reset();
51682                
51683                 cell.hide();
51684                 
51685                 (function(){ // complex but required for focus issues in safari, ie and opera
51686                     ed.row = row;
51687                     ed.col = col;
51688                     ed.record = r;
51689                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51690                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51691                     this.activeEditor = ed;
51692                     var v = r.data[field];
51693                     ed.startEdit(this.view.getCell(row, col), v);
51694                     // combo's with 'displayField and name set
51695                     if (ed.field.displayField && ed.field.name) {
51696                         ed.field.el.dom.value = r.data[ed.field.name];
51697                     }
51698                     
51699                     
51700                 }).defer(50, this);
51701             }
51702         }
51703     },
51704         
51705     /**
51706      * Stops any active editing
51707      */
51708     stopEditing : function(){
51709         if(this.activeEditor){
51710             this.activeEditor.completeEdit();
51711         }
51712         this.activeEditor = null;
51713     }
51714 });/*
51715  * Based on:
51716  * Ext JS Library 1.1.1
51717  * Copyright(c) 2006-2007, Ext JS, LLC.
51718  *
51719  * Originally Released Under LGPL - original licence link has changed is not relivant.
51720  *
51721  * Fork - LGPL
51722  * <script type="text/javascript">
51723  */
51724
51725 // private - not really -- you end up using it !
51726 // This is a support class used internally by the Grid components
51727
51728 /**
51729  * @class Roo.grid.GridEditor
51730  * @extends Roo.Editor
51731  * Class for creating and editable grid elements.
51732  * @param {Object} config any settings (must include field)
51733  */
51734 Roo.grid.GridEditor = function(field, config){
51735     if (!config && field.field) {
51736         config = field;
51737         field = Roo.factory(config.field, Roo.form);
51738     }
51739     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51740     field.monitorTab = false;
51741 };
51742
51743 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51744     
51745     /**
51746      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51747      */
51748     
51749     alignment: "tl-tl",
51750     autoSize: "width",
51751     hideEl : false,
51752     cls: "x-small-editor x-grid-editor",
51753     shim:false,
51754     shadow:"frame"
51755 });/*
51756  * Based on:
51757  * Ext JS Library 1.1.1
51758  * Copyright(c) 2006-2007, Ext JS, LLC.
51759  *
51760  * Originally Released Under LGPL - original licence link has changed is not relivant.
51761  *
51762  * Fork - LGPL
51763  * <script type="text/javascript">
51764  */
51765   
51766
51767   
51768 Roo.grid.PropertyRecord = Roo.data.Record.create([
51769     {name:'name',type:'string'},  'value'
51770 ]);
51771
51772
51773 Roo.grid.PropertyStore = function(grid, source){
51774     this.grid = grid;
51775     this.store = new Roo.data.Store({
51776         recordType : Roo.grid.PropertyRecord
51777     });
51778     this.store.on('update', this.onUpdate,  this);
51779     if(source){
51780         this.setSource(source);
51781     }
51782     Roo.grid.PropertyStore.superclass.constructor.call(this);
51783 };
51784
51785
51786
51787 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51788     setSource : function(o){
51789         this.source = o;
51790         this.store.removeAll();
51791         var data = [];
51792         for(var k in o){
51793             if(this.isEditableValue(o[k])){
51794                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51795             }
51796         }
51797         this.store.loadRecords({records: data}, {}, true);
51798     },
51799
51800     onUpdate : function(ds, record, type){
51801         if(type == Roo.data.Record.EDIT){
51802             var v = record.data['value'];
51803             var oldValue = record.modified['value'];
51804             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51805                 this.source[record.id] = v;
51806                 record.commit();
51807                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51808             }else{
51809                 record.reject();
51810             }
51811         }
51812     },
51813
51814     getProperty : function(row){
51815        return this.store.getAt(row);
51816     },
51817
51818     isEditableValue: function(val){
51819         if(val && val instanceof Date){
51820             return true;
51821         }else if(typeof val == 'object' || typeof val == 'function'){
51822             return false;
51823         }
51824         return true;
51825     },
51826
51827     setValue : function(prop, value){
51828         this.source[prop] = value;
51829         this.store.getById(prop).set('value', value);
51830     },
51831
51832     getSource : function(){
51833         return this.source;
51834     }
51835 });
51836
51837 Roo.grid.PropertyColumnModel = function(grid, store){
51838     this.grid = grid;
51839     var g = Roo.grid;
51840     g.PropertyColumnModel.superclass.constructor.call(this, [
51841         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51842         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51843     ]);
51844     this.store = store;
51845     this.bselect = Roo.DomHelper.append(document.body, {
51846         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51847             {tag: 'option', value: 'true', html: 'true'},
51848             {tag: 'option', value: 'false', html: 'false'}
51849         ]
51850     });
51851     Roo.id(this.bselect);
51852     var f = Roo.form;
51853     this.editors = {
51854         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51855         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51856         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51857         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51858         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51859     };
51860     this.renderCellDelegate = this.renderCell.createDelegate(this);
51861     this.renderPropDelegate = this.renderProp.createDelegate(this);
51862 };
51863
51864 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51865     
51866     
51867     nameText : 'Name',
51868     valueText : 'Value',
51869     
51870     dateFormat : 'm/j/Y',
51871     
51872     
51873     renderDate : function(dateVal){
51874         return dateVal.dateFormat(this.dateFormat);
51875     },
51876
51877     renderBool : function(bVal){
51878         return bVal ? 'true' : 'false';
51879     },
51880
51881     isCellEditable : function(colIndex, rowIndex){
51882         return colIndex == 1;
51883     },
51884
51885     getRenderer : function(col){
51886         return col == 1 ?
51887             this.renderCellDelegate : this.renderPropDelegate;
51888     },
51889
51890     renderProp : function(v){
51891         return this.getPropertyName(v);
51892     },
51893
51894     renderCell : function(val){
51895         var rv = val;
51896         if(val instanceof Date){
51897             rv = this.renderDate(val);
51898         }else if(typeof val == 'boolean'){
51899             rv = this.renderBool(val);
51900         }
51901         return Roo.util.Format.htmlEncode(rv);
51902     },
51903
51904     getPropertyName : function(name){
51905         var pn = this.grid.propertyNames;
51906         return pn && pn[name] ? pn[name] : name;
51907     },
51908
51909     getCellEditor : function(colIndex, rowIndex){
51910         var p = this.store.getProperty(rowIndex);
51911         var n = p.data['name'], val = p.data['value'];
51912         
51913         if(typeof(this.grid.customEditors[n]) == 'string'){
51914             return this.editors[this.grid.customEditors[n]];
51915         }
51916         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51917             return this.grid.customEditors[n];
51918         }
51919         if(val instanceof Date){
51920             return this.editors['date'];
51921         }else if(typeof val == 'number'){
51922             return this.editors['number'];
51923         }else if(typeof val == 'boolean'){
51924             return this.editors['boolean'];
51925         }else{
51926             return this.editors['string'];
51927         }
51928     }
51929 });
51930
51931 /**
51932  * @class Roo.grid.PropertyGrid
51933  * @extends Roo.grid.EditorGrid
51934  * This class represents the  interface of a component based property grid control.
51935  * <br><br>Usage:<pre><code>
51936  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51937       
51938  });
51939  // set any options
51940  grid.render();
51941  * </code></pre>
51942   
51943  * @constructor
51944  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51945  * The container MUST have some type of size defined for the grid to fill. The container will be
51946  * automatically set to position relative if it isn't already.
51947  * @param {Object} config A config object that sets properties on this grid.
51948  */
51949 Roo.grid.PropertyGrid = function(container, config){
51950     config = config || {};
51951     var store = new Roo.grid.PropertyStore(this);
51952     this.store = store;
51953     var cm = new Roo.grid.PropertyColumnModel(this, store);
51954     store.store.sort('name', 'ASC');
51955     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51956         ds: store.store,
51957         cm: cm,
51958         enableColLock:false,
51959         enableColumnMove:false,
51960         stripeRows:false,
51961         trackMouseOver: false,
51962         clicksToEdit:1
51963     }, config));
51964     this.getGridEl().addClass('x-props-grid');
51965     this.lastEditRow = null;
51966     this.on('columnresize', this.onColumnResize, this);
51967     this.addEvents({
51968          /**
51969              * @event beforepropertychange
51970              * Fires before a property changes (return false to stop?)
51971              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51972              * @param {String} id Record Id
51973              * @param {String} newval New Value
51974          * @param {String} oldval Old Value
51975              */
51976         "beforepropertychange": true,
51977         /**
51978              * @event propertychange
51979              * Fires after a property changes
51980              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51981              * @param {String} id Record Id
51982              * @param {String} newval New Value
51983          * @param {String} oldval Old Value
51984              */
51985         "propertychange": true
51986     });
51987     this.customEditors = this.customEditors || {};
51988 };
51989 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51990     
51991      /**
51992      * @cfg {Object} customEditors map of colnames=> custom editors.
51993      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51994      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51995      * false disables editing of the field.
51996          */
51997     
51998       /**
51999      * @cfg {Object} propertyNames map of property Names to their displayed value
52000          */
52001     
52002     render : function(){
52003         Roo.grid.PropertyGrid.superclass.render.call(this);
52004         this.autoSize.defer(100, this);
52005     },
52006
52007     autoSize : function(){
52008         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52009         if(this.view){
52010             this.view.fitColumns();
52011         }
52012     },
52013
52014     onColumnResize : function(){
52015         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52016         this.autoSize();
52017     },
52018     /**
52019      * Sets the data for the Grid
52020      * accepts a Key => Value object of all the elements avaiable.
52021      * @param {Object} data  to appear in grid.
52022      */
52023     setSource : function(source){
52024         this.store.setSource(source);
52025         //this.autoSize();
52026     },
52027     /**
52028      * Gets all the data from the grid.
52029      * @return {Object} data  data stored in grid
52030      */
52031     getSource : function(){
52032         return this.store.getSource();
52033     }
52034 });/*
52035  * Based on:
52036  * Ext JS Library 1.1.1
52037  * Copyright(c) 2006-2007, Ext JS, LLC.
52038  *
52039  * Originally Released Under LGPL - original licence link has changed is not relivant.
52040  *
52041  * Fork - LGPL
52042  * <script type="text/javascript">
52043  */
52044  
52045 /**
52046  * @class Roo.LoadMask
52047  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52048  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52049  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52050  * element's UpdateManager load indicator and will be destroyed after the initial load.
52051  * @constructor
52052  * Create a new LoadMask
52053  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52054  * @param {Object} config The config object
52055  */
52056 Roo.LoadMask = function(el, config){
52057     this.el = Roo.get(el);
52058     Roo.apply(this, config);
52059     if(this.store){
52060         this.store.on('beforeload', this.onBeforeLoad, this);
52061         this.store.on('load', this.onLoad, this);
52062         this.store.on('loadexception', this.onLoadException, this);
52063         this.removeMask = false;
52064     }else{
52065         var um = this.el.getUpdateManager();
52066         um.showLoadIndicator = false; // disable the default indicator
52067         um.on('beforeupdate', this.onBeforeLoad, this);
52068         um.on('update', this.onLoad, this);
52069         um.on('failure', this.onLoad, this);
52070         this.removeMask = true;
52071     }
52072 };
52073
52074 Roo.LoadMask.prototype = {
52075     /**
52076      * @cfg {Boolean} removeMask
52077      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52078      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52079      */
52080     /**
52081      * @cfg {String} msg
52082      * The text to display in a centered loading message box (defaults to 'Loading...')
52083      */
52084     msg : 'Loading...',
52085     /**
52086      * @cfg {String} msgCls
52087      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52088      */
52089     msgCls : 'x-mask-loading',
52090
52091     /**
52092      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52093      * @type Boolean
52094      */
52095     disabled: false,
52096
52097     /**
52098      * Disables the mask to prevent it from being displayed
52099      */
52100     disable : function(){
52101        this.disabled = true;
52102     },
52103
52104     /**
52105      * Enables the mask so that it can be displayed
52106      */
52107     enable : function(){
52108         this.disabled = false;
52109     },
52110     
52111     onLoadException : function()
52112     {
52113         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52114             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52115         }
52116         this.el.unmask(this.removeMask);
52117     },
52118     // private
52119     onLoad : function()
52120     {
52121         this.el.unmask(this.removeMask);
52122     },
52123
52124     // private
52125     onBeforeLoad : function(){
52126         if(!this.disabled){
52127             this.el.mask(this.msg, this.msgCls);
52128         }
52129     },
52130
52131     // private
52132     destroy : function(){
52133         if(this.store){
52134             this.store.un('beforeload', this.onBeforeLoad, this);
52135             this.store.un('load', this.onLoad, this);
52136             this.store.un('loadexception', this.onLoadException, this);
52137         }else{
52138             var um = this.el.getUpdateManager();
52139             um.un('beforeupdate', this.onBeforeLoad, this);
52140             um.un('update', this.onLoad, this);
52141             um.un('failure', this.onLoad, this);
52142         }
52143     }
52144 };/*
52145  * Based on:
52146  * Ext JS Library 1.1.1
52147  * Copyright(c) 2006-2007, Ext JS, LLC.
52148  *
52149  * Originally Released Under LGPL - original licence link has changed is not relivant.
52150  *
52151  * Fork - LGPL
52152  * <script type="text/javascript">
52153  */
52154 Roo.XTemplate = function(){
52155     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52156     var s = this.html;
52157
52158     s = ['<tpl>', s, '</tpl>'].join('');
52159
52160     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52161
52162     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52163     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52164     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52165     var m, id = 0;
52166     var tpls = [];
52167
52168     while(m = s.match(re)){
52169        var m2 = m[0].match(nameRe);
52170        var m3 = m[0].match(ifRe);
52171        var m4 = m[0].match(execRe);
52172        var exp = null, fn = null, exec = null;
52173        var name = m2 && m2[1] ? m2[1] : '';
52174        if(m3){
52175            exp = m3 && m3[1] ? m3[1] : null;
52176            if(exp){
52177                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52178            }
52179        }
52180        if(m4){
52181            exp = m4 && m4[1] ? m4[1] : null;
52182            if(exp){
52183                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52184            }
52185        }
52186        if(name){
52187            switch(name){
52188                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52189                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52190                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52191            }
52192        }
52193        tpls.push({
52194             id: id,
52195             target: name,
52196             exec: exec,
52197             test: fn,
52198             body: m[1]||''
52199         });
52200        s = s.replace(m[0], '{xtpl'+ id + '}');
52201        ++id;
52202     }
52203     for(var i = tpls.length-1; i >= 0; --i){
52204         this.compileTpl(tpls[i]);
52205     }
52206     this.master = tpls[tpls.length-1];
52207     this.tpls = tpls;
52208 };
52209 Roo.extend(Roo.XTemplate, Roo.Template, {
52210
52211     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52212
52213     applySubTemplate : function(id, values, parent){
52214         var t = this.tpls[id];
52215         if(t.test && !t.test.call(this, values, parent)){
52216             return '';
52217         }
52218         if(t.exec && t.exec.call(this, values, parent)){
52219             return '';
52220         }
52221         var vs = t.target ? t.target.call(this, values, parent) : values;
52222         parent = t.target ? values : parent;
52223         if(t.target && vs instanceof Array){
52224             var buf = [];
52225             for(var i = 0, len = vs.length; i < len; i++){
52226                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52227             }
52228             return buf.join('');
52229         }
52230         return t.compiled.call(this, vs, parent);
52231     },
52232
52233     compileTpl : function(tpl){
52234         var fm = Roo.util.Format;
52235         var useF = this.disableFormats !== true;
52236         var sep = Roo.isGecko ? "+" : ",";
52237         var fn = function(m, name, format, args){
52238             if(name.substr(0, 4) == 'xtpl'){
52239                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52240             }
52241             var v;
52242             if(name.indexOf('.') != -1){
52243                 v = name;
52244             }else{
52245                 v = "values['" + name + "']";
52246             }
52247             if(format && useF){
52248                 args = args ? ',' + args : "";
52249                 if(format.substr(0, 5) != "this."){
52250                     format = "fm." + format + '(';
52251                 }else{
52252                     format = 'this.call("'+ format.substr(5) + '", ';
52253                     args = ", values";
52254                 }
52255             }else{
52256                 args= ''; format = "("+v+" === undefined ? '' : ";
52257             }
52258             return "'"+ sep + format + v + args + ")"+sep+"'";
52259         };
52260         var body;
52261         // branched to use + in gecko and [].join() in others
52262         if(Roo.isGecko){
52263             body = "tpl.compiled = function(values, parent){ return '" +
52264                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52265                     "';};";
52266         }else{
52267             body = ["tpl.compiled = function(values, parent){ return ['"];
52268             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52269             body.push("'].join('');};");
52270             body = body.join('');
52271         }
52272         /** eval:var:zzzzzzz */
52273         eval(body);
52274         return this;
52275     },
52276
52277     applyTemplate : function(values){
52278         return this.master.compiled.call(this, values, {});
52279         var s = this.subs;
52280     },
52281
52282     apply : function(){
52283         return this.applyTemplate.apply(this, arguments);
52284     },
52285
52286     compile : function(){return this;}
52287 });
52288
52289 Roo.XTemplate.from = function(el){
52290     el = Roo.getDom(el);
52291     return new Roo.XTemplate(el.value || el.innerHTML);
52292 };/*
52293  * Original code for Roojs - LGPL
52294  * <script type="text/javascript">
52295  */
52296  
52297 /**
52298  * @class Roo.XComponent
52299  * A delayed Element creator...
52300  * Or a way to group chunks of interface together.
52301  * 
52302  * Mypart.xyx = new Roo.XComponent({
52303
52304     parent : 'Mypart.xyz', // empty == document.element.!!
52305     order : '001',
52306     name : 'xxxx'
52307     region : 'xxxx'
52308     disabled : function() {} 
52309      
52310     tree : function() { // return an tree of xtype declared components
52311         var MODULE = this;
52312         return 
52313         {
52314             xtype : 'NestedLayoutPanel',
52315             // technicall
52316         }
52317      ]
52318  *})
52319  *
52320  *
52321  * It can be used to build a big heiracy, with parent etc.
52322  * or you can just use this to render a single compoent to a dom element
52323  * MYPART.render(Roo.Element | String(id) | dom_element )
52324  * 
52325  * @extends Roo.util.Observable
52326  * @constructor
52327  * @param cfg {Object} configuration of component
52328  * 
52329  */
52330 Roo.XComponent = function(cfg) {
52331     Roo.apply(this, cfg);
52332     this.addEvents({ 
52333         /**
52334              * @event built
52335              * Fires when this the componnt is built
52336              * @param {Roo.XComponent} c the component
52337              */
52338         'built' : true,
52339         /**
52340              * @event buildcomplete
52341              * Fires on the top level element when all elements have been built
52342              * @param {Roo.XComponent} c the top level component.
52343          */
52344         'buildcomplete' : true
52345         
52346     });
52347     this.region = this.region || 'center'; // default..
52348     Roo.XComponent.register(this);
52349     this.modules = false;
52350     this.el = false; // where the layout goes..
52351     
52352     
52353 }
52354 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52355     /**
52356      * @property el
52357      * The created element (with Roo.factory())
52358      * @type {Roo.Layout}
52359      */
52360     el  : false,
52361     
52362     /**
52363      * @property el
52364      * for BC  - use el in new code
52365      * @type {Roo.Layout}
52366      */
52367     panel : false,
52368     
52369     /**
52370      * @property layout
52371      * for BC  - use el in new code
52372      * @type {Roo.Layout}
52373      */
52374     layout : false,
52375     
52376      /**
52377      * @cfg {Function|boolean} disabled
52378      * If this module is disabled by some rule, return true from the funtion
52379      */
52380     disabled : false,
52381     
52382     /**
52383      * @cfg {String} parent 
52384      * Name of parent element which it get xtype added to..
52385      */
52386     parent: false,
52387     
52388     /**
52389      * @cfg {String} order
52390      * Used to set the order in which elements are created (usefull for multiple tabs)
52391      */
52392     
52393     order : false,
52394     /**
52395      * @cfg {String} name
52396      * String to display while loading.
52397      */
52398     name : false,
52399     /**
52400      * @cfg {String} region
52401      * Region to render component to (defaults to center)
52402      */
52403     region : 'center',
52404     
52405     /**
52406      * @cfg {Array} items
52407      * A single item array - the first element is the root of the tree..
52408      * It's done this way to stay compatible with the Xtype system...
52409      */
52410     items : false,
52411     
52412     
52413      /**
52414      * render
52415      * render element to dom or tree
52416      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52417      */
52418     
52419     render : function(el)
52420     {
52421         
52422         el = el || false;
52423         var hp = this.parent ? 1 : 0;
52424         
52425         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52426             // if parent is a '#.....' string, then let's use that..
52427             var ename = this.parent.substr(1)
52428             this.parent = false;
52429             el = Roo.get(ename);
52430             if (!el) {
52431                 Roo.log("Warning - element can not be found :#" + ename );
52432                 return;
52433             }
52434         }
52435         
52436         
52437         if (!this.parent) {
52438             
52439             el = el ? Roo.get(el) : false;
52440             
52441             // it's a top level one..
52442             this.parent =  {
52443                 el : new Roo.BorderLayout(el || document.body, {
52444                 
52445                      center: {
52446                          titlebar: false,
52447                          autoScroll:false,
52448                          closeOnTab: true,
52449                          tabPosition: 'top',
52450                           //resizeTabs: true,
52451                          alwaysShowTabs: el && hp? false :  true,
52452                          hideTabs: el || !hp ? true :  false,
52453                          minTabWidth: 140
52454                      }
52455                  })
52456             }
52457         }
52458         
52459         
52460             
52461         var tree = this.tree();
52462         tree.region = tree.region || this.region;
52463         this.el = this.parent.el.addxtype(tree);
52464         this.fireEvent('built', this);
52465         
52466         this.panel = this.el;
52467         this.layout = this.panel.layout;    
52468          
52469     }
52470     
52471 });
52472
52473 Roo.apply(Roo.XComponent, {
52474     
52475     /**
52476      * @property  buildCompleted
52477      * True when the builder has completed building the interface.
52478      * @type Boolean
52479      */
52480     buildCompleted : false,
52481      
52482     /**
52483      * @property  topModule
52484      * the upper most module - uses document.element as it's constructor.
52485      * @type Object
52486      */
52487      
52488     topModule  : false,
52489       
52490     /**
52491      * @property  modules
52492      * array of modules to be created by registration system.
52493      * @type {Array} of Roo.XComponent
52494      */
52495     
52496     modules : [],
52497     /**
52498      * @property  elmodules
52499      * array of modules to be created by which use #ID 
52500      * @type {Array} of Roo.XComponent
52501      */
52502      
52503     elmodules : [],
52504
52505     
52506     /**
52507      * Register components to be built later.
52508      *
52509      * This solves the following issues
52510      * - Building is not done on page load, but after an authentication process has occured.
52511      * - Interface elements are registered on page load
52512      * - Parent Interface elements may not be loaded before child, so this handles that..
52513      * 
52514      *
52515      * example:
52516      * 
52517      * MyApp.register({
52518           order : '000001',
52519           module : 'Pman.Tab.projectMgr',
52520           region : 'center',
52521           parent : 'Pman.layout',
52522           disabled : false,  // or use a function..
52523         })
52524      
52525      * * @param {Object} details about module
52526      */
52527     register : function(obj) {
52528         this.modules.push(obj);
52529          
52530     },
52531     /**
52532      * convert a string to an object..
52533      * eg. 'AAA.BBB' -> finds AAA.BBB
52534
52535      */
52536     
52537     toObject : function(str)
52538     {
52539         if (!str || typeof(str) == 'object') {
52540             return str;
52541         }
52542         if (str.substring(0,1) == '#') {
52543             return str;
52544         }
52545
52546         var ar = str.split('.');
52547         var rt, o;
52548         rt = ar.shift();
52549             /** eval:var:o */
52550         try {
52551             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52552         } catch (e) {
52553             throw "Module not found : " + str;
52554         }
52555         
52556         if (o === false) {
52557             throw "Module not found : " + str;
52558         }
52559         Roo.each(ar, function(e) {
52560             if (typeof(o[e]) == 'undefined') {
52561                 throw "Module not found : " + str;
52562             }
52563             o = o[e];
52564         });
52565         
52566         return o;
52567         
52568     },
52569     
52570     
52571     /**
52572      * move modules into their correct place in the tree..
52573      * 
52574      */
52575     preBuild : function ()
52576     {
52577         var _t = this;
52578         Roo.each(this.modules , function (obj)
52579         {
52580             var opar = obj.parent;
52581             try { 
52582                 obj.parent = this.toObject(opar);
52583             } catch(e) {
52584                 Roo.log(e.toString());
52585                 return;
52586             }
52587             
52588             if (!obj.parent) {
52589                 this.topModule = obj;
52590                 return;
52591             }
52592             if (typeof(obj.parent) == 'string') {
52593                 this.elmodules.push(obj);
52594                 return;
52595             }
52596             if (obj.parent.constructor != Roo.XComponent) {
52597                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52598             }
52599             if (!obj.parent.modules) {
52600                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52601                     function(o) { return o.order + '' }
52602                 );
52603             }
52604             
52605             obj.parent.modules.add(obj);
52606         }, this);
52607     },
52608     
52609      /**
52610      * make a list of modules to build.
52611      * @return {Array} list of modules. 
52612      */ 
52613     
52614     buildOrder : function()
52615     {
52616         var _this = this;
52617         var cmp = function(a,b) {   
52618             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52619         };
52620         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52621             throw "No top level modules to build";
52622         }
52623         
52624         // make a flat list in order of modules to build.
52625         var mods = this.topModule ? [ this.topModule ] : [];
52626         Roo.each(this.elmodules,function(e) { mods.push(e) });
52627
52628         
52629         // add modules to their parents..
52630         var addMod = function(m) {
52631            // Roo.debug && Roo.log(m.modKey);
52632             
52633             mods.push(m);
52634             if (m.modules) {
52635                 m.modules.keySort('ASC',  cmp );
52636                 m.modules.each(addMod);
52637             }
52638             // not sure if this is used any more..
52639             if (m.finalize) {
52640                 m.finalize.name = m.name + " (clean up) ";
52641                 mods.push(m.finalize);
52642             }
52643             
52644         }
52645         if (this.topModule) { 
52646             this.topModule.modules.keySort('ASC',  cmp );
52647             this.topModule.modules.each(addMod);
52648         }
52649         return mods;
52650     },
52651     
52652      /**
52653      * Build the registered modules.
52654      * @param {Object} parent element.
52655      * @param {Function} optional method to call after module has been added.
52656      * 
52657      */ 
52658    
52659     build : function() 
52660     {
52661         
52662         this.preBuild();
52663         var mods = this.buildOrder();
52664       
52665         //this.allmods = mods;
52666         //Roo.debug && Roo.log(mods);
52667         //return;
52668         if (!mods.length) { // should not happen
52669             throw "NO modules!!!";
52670         }
52671         
52672         
52673         
52674         // flash it up as modal - so we store the mask!?
52675         Roo.MessageBox.show({ title: 'loading' });
52676         Roo.MessageBox.show({
52677            title: "Please wait...",
52678            msg: "Building Interface...",
52679            width:450,
52680            progress:true,
52681            closable:false,
52682            modal: false
52683           
52684         });
52685         var total = mods.length;
52686         
52687         var _this = this;
52688         var progressRun = function() {
52689             if (!mods.length) {
52690                 Roo.debug && Roo.log('hide?');
52691                 Roo.MessageBox.hide();
52692                 if (_this.topModule) { 
52693                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52694                 }
52695                 // THE END...
52696                 return false;   
52697             }
52698             
52699             var m = mods.shift();
52700             
52701             
52702             Roo.debug && Roo.log(m);
52703             // not sure if this is supported any more.. - modules that are are just function
52704             if (typeof(m) == 'function') { 
52705                 m.call(this);
52706                 return progressRun.defer(10, _this);
52707             } 
52708             
52709             
52710             
52711             Roo.MessageBox.updateProgress(
52712                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52713                     " of " + total + 
52714                     (m.name ? (' - ' + m.name) : '')
52715                     );
52716             
52717          
52718             // is the module disabled?
52719             var disabled = (typeof(m.disabled) == 'function') ?
52720                 m.disabled.call(m.module.disabled) : m.disabled;    
52721             
52722             
52723             if (disabled) {
52724                 return progressRun(); // we do not update the display!
52725             }
52726             
52727             // now build 
52728             
52729             m.render();
52730             // it's 10 on top level, and 1 on others??? why...
52731             return progressRun.defer(10, _this);
52732              
52733         }
52734         progressRun.defer(1, _this);
52735      
52736         
52737         
52738     }
52739     
52740      
52741    
52742     
52743     
52744 });
52745  //<script type="text/javascript">
52746
52747
52748 /**
52749  * @class Roo.Login
52750  * @extends Roo.LayoutDialog
52751  * A generic Login Dialog..... - only one needed in theory!?!?
52752  *
52753  * Fires XComponent builder on success...
52754  * 
52755  * Sends 
52756  *    username,password, lang = for login actions.
52757  *    check = 1 for periodic checking that sesion is valid.
52758  *    passwordRequest = email request password
52759  *    logout = 1 = to logout
52760  * 
52761  * Affects: (this id="????" elements)
52762  *   loading  (removed) (used to indicate application is loading)
52763  *   loading-mask (hides) (used to hide application when it's building loading)
52764  *   
52765  * 
52766  * Usage: 
52767  *    
52768  * 
52769  * Myapp.login = Roo.Login({
52770      url: xxxx,
52771    
52772      realm : 'Myapp', 
52773      
52774      
52775      method : 'POST',
52776      
52777      
52778      * 
52779  })
52780  * 
52781  * 
52782  * 
52783  **/
52784  
52785 Roo.Login = function(cfg)
52786 {
52787     this.addEvents({
52788         'refreshed' : true
52789     });
52790     
52791     Roo.apply(this,cfg);
52792     
52793     Roo.onReady(function() {
52794         this.onLoad();
52795     }, this);
52796     // call parent..
52797     
52798    
52799     Roo.Login.superclass.constructor.call(this, this);
52800     //this.addxtype(this.items[0]);
52801     
52802     
52803 }
52804
52805
52806 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52807     
52808     /**
52809      * @cfg {String} method
52810      * Method used to query for login details.
52811      */
52812     
52813     method : 'POST',
52814     /**
52815      * @cfg {String} url
52816      * URL to query login data. - eg. baseURL + '/Login.php'
52817      */
52818     url : '',
52819     
52820     /**
52821      * @property user
52822      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52823      * @type {Object} 
52824      */
52825     user : false,
52826     /**
52827      * @property checkFails
52828      * Number of times we have attempted to get authentication check, and failed.
52829      * @type {Number} 
52830      */
52831     checkFails : 0,
52832       /**
52833      * @property intervalID
52834      * The window interval that does the constant login checking.
52835      * @type {Number} 
52836      */
52837     intervalID : 0,
52838     
52839     
52840     onLoad : function() // called on page load...
52841     {
52842         // load 
52843          
52844         if (Roo.get('loading')) { // clear any loading indicator..
52845             Roo.get('loading').remove();
52846         }
52847         
52848         //this.switchLang('en'); // set the language to english..
52849        
52850         this.check({
52851             success:  function(response, opts)  {  // check successfull...
52852             
52853                 var res = this.processResponse(response);
52854                 this.checkFails =0;
52855                 if (!res.success) { // error!
52856                     this.checkFails = 5;
52857                     //console.log('call failure');
52858                     return this.failure(response,opts);
52859                 }
52860                 
52861                 if (!res.data.id) { // id=0 == login failure.
52862                     return this.show();
52863                 }
52864                 
52865                               
52866                         //console.log(success);
52867                 this.fillAuth(res.data);   
52868                 this.checkFails =0;
52869                 Roo.XComponent.build();
52870             },
52871             failure : this.show
52872         });
52873         
52874     }, 
52875     
52876     
52877     check: function(cfg) // called every so often to refresh cookie etc..
52878     {
52879         if (cfg.again) { // could be undefined..
52880             this.checkFails++;
52881         } else {
52882             this.checkFails = 0;
52883         }
52884         var _this = this;
52885         if (this.sending) {
52886             if ( this.checkFails > 4) {
52887                 Roo.MessageBox.alert("Error",  
52888                     "Error getting authentication status. - try reloading, or wait a while", function() {
52889                         _this.sending = false;
52890                     }); 
52891                 return;
52892             }
52893             cfg.again = true;
52894             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52895             return;
52896         }
52897         this.sending = true;
52898         
52899         Roo.Ajax.request({  
52900             url: this.url,
52901             params: {
52902                 getAuthUser: true
52903             },  
52904             method: this.method,
52905             success:  cfg.success || this.success,
52906             failure : cfg.failure || this.failure,
52907             scope : this,
52908             callCfg : cfg
52909               
52910         });  
52911     }, 
52912     
52913     
52914     logout: function()
52915     {
52916         window.onbeforeunload = function() { }; // false does not work for IE..
52917         this.user = false;
52918         var _this = this;
52919         
52920         Roo.Ajax.request({  
52921             url: this.url,
52922             params: {
52923                 logout: 1
52924             },  
52925             method: 'GET',
52926             failure : function() {
52927                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52928                     document.location = document.location.toString() + '?ts=' + Math.random();
52929                 });
52930                 
52931             },
52932             success : function() {
52933                 _this.user = false;
52934                 this.checkFails =0;
52935                 // fixme..
52936                 document.location = document.location.toString() + '?ts=' + Math.random();
52937             }
52938               
52939               
52940         }); 
52941     },
52942     
52943     processResponse : function (response)
52944     {
52945         var res = '';
52946         try {
52947             res = Roo.decode(response.responseText);
52948             // oops...
52949             if (typeof(res) != 'object') {
52950                 res = { success : false, errorMsg : res, errors : true };
52951             }
52952             if (typeof(res.success) == 'undefined') {
52953                 res.success = false;
52954             }
52955             
52956         } catch(e) {
52957             res = { success : false,  errorMsg : response.responseText, errors : true };
52958         }
52959         return res;
52960     },
52961     
52962     success : function(response, opts)  // check successfull...
52963     {  
52964         this.sending = false;
52965         var res = this.processResponse(response);
52966         if (!res.success) {
52967             return this.failure(response, opts);
52968         }
52969         if (!res.data || !res.data.id) {
52970             return this.failure(response,opts);
52971         }
52972         //console.log(res);
52973         this.fillAuth(res.data);
52974         
52975         this.checkFails =0;
52976         
52977     },
52978     
52979     
52980     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52981     {
52982         this.authUser = -1;
52983         this.sending = false;
52984         var res = this.processResponse(response);
52985         //console.log(res);
52986         if ( this.checkFails > 2) {
52987         
52988             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52989                 "Error getting authentication status. - try reloading"); 
52990             return;
52991         }
52992         opts.callCfg.again = true;
52993         this.check.defer(1000, this, [ opts.callCfg ]);
52994         return;  
52995     },
52996     
52997     
52998     
52999     fillAuth: function(au) {
53000         this.startAuthCheck();
53001         this.authUserId = au.id;
53002         this.authUser = au;
53003         this.lastChecked = new Date();
53004         this.fireEvent('refreshed', au);
53005         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53006         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53007         au.lang = au.lang || 'en';
53008         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53009         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53010         this.switchLang(au.lang );
53011         
53012      
53013         // open system... - -on setyp..
53014         if (this.authUserId  < 0) {
53015             Roo.MessageBox.alert("Warning", 
53016                 "This is an open system - please set up a admin user with a password.");  
53017         }
53018          
53019         //Pman.onload(); // which should do nothing if it's a re-auth result...
53020         
53021              
53022     },
53023     
53024     startAuthCheck : function() // starter for timeout checking..
53025     {
53026         if (this.intervalID) { // timer already in place...
53027             return false;
53028         }
53029         var _this = this;
53030         this.intervalID =  window.setInterval(function() {
53031               _this.check(false);
53032             }, 120000); // every 120 secs = 2mins..
53033         
53034         
53035     },
53036          
53037     
53038     switchLang : function (lang) 
53039     {
53040         _T = typeof(_T) == 'undefined' ? false : _T;
53041           if (!_T || !lang.length) {
53042             return;
53043         }
53044         
53045         if (!_T && lang != 'en') {
53046             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53047             return;
53048         }
53049         
53050         if (typeof(_T.en) == 'undefined') {
53051             _T.en = {};
53052             Roo.apply(_T.en, _T);
53053         }
53054         
53055         if (typeof(_T[lang]) == 'undefined') {
53056             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53057             return;
53058         }
53059         
53060         
53061         Roo.apply(_T, _T[lang]);
53062         // just need to set the text values for everything...
53063         var _this = this;
53064         /* this will not work ...
53065         if (this.form) { 
53066             
53067                
53068             function formLabel(name, val) {
53069                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53070             }
53071             
53072             formLabel('password', "Password"+':');
53073             formLabel('username', "Email Address"+':');
53074             formLabel('lang', "Language"+':');
53075             this.dialog.setTitle("Login");
53076             this.dialog.buttons[0].setText("Forgot Password");
53077             this.dialog.buttons[1].setText("Login");
53078         }
53079         */
53080         
53081         
53082     },
53083     
53084     
53085     title: "Login",
53086     modal: true,
53087     width:  350,
53088     //height: 230,
53089     height: 180,
53090     shadow: true,
53091     minWidth:200,
53092     minHeight:180,
53093     //proxyDrag: true,
53094     closable: false,
53095     draggable: false,
53096     collapsible: false,
53097     resizable: false,
53098     center: {  // needed??
53099         autoScroll:false,
53100         titlebar: false,
53101        // tabPosition: 'top',
53102         hideTabs: true,
53103         closeOnTab: true,
53104         alwaysShowTabs: false
53105     } ,
53106     listeners : {
53107         
53108         show  : function(dlg)
53109         {
53110             //console.log(this);
53111             this.form = this.layout.getRegion('center').activePanel.form;
53112             this.form.dialog = dlg;
53113             this.buttons[0].form = this.form;
53114             this.buttons[0].dialog = dlg;
53115             this.buttons[1].form = this.form;
53116             this.buttons[1].dialog = dlg;
53117            
53118            //this.resizeToLogo.defer(1000,this);
53119             // this is all related to resizing for logos..
53120             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53121            //// if (!sz) {
53122              //   this.resizeToLogo.defer(1000,this);
53123              //   return;
53124            // }
53125             //var w = Ext.lib.Dom.getViewWidth() - 100;
53126             //var h = Ext.lib.Dom.getViewHeight() - 100;
53127             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53128             //this.center();
53129             if (this.disabled) {
53130                 this.hide();
53131                 return;
53132             }
53133             
53134             if (this.user.id < 0) { // used for inital setup situations.
53135                 return;
53136             }
53137             
53138             if (this.intervalID) {
53139                 // remove the timer
53140                 window.clearInterval(this.intervalID);
53141                 this.intervalID = false;
53142             }
53143             
53144             
53145             if (Roo.get('loading')) {
53146                 Roo.get('loading').remove();
53147             }
53148             if (Roo.get('loading-mask')) {
53149                 Roo.get('loading-mask').hide();
53150             }
53151             
53152             //incomming._node = tnode;
53153             this.form.reset();
53154             //this.dialog.modal = !modal;
53155             //this.dialog.show();
53156             this.el.unmask(); 
53157             
53158             
53159             this.form.setValues({
53160                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53161                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53162             });
53163             
53164             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53165             if (this.form.findField('username').getValue().length > 0 ){
53166                 this.form.findField('password').focus();
53167             } else {
53168                this.form.findField('username').focus();
53169             }
53170     
53171         }
53172     },
53173     items : [
53174          {
53175        
53176             xtype : 'ContentPanel',
53177             xns : Roo,
53178             region: 'center',
53179             fitToFrame : true,
53180             
53181             items : [
53182     
53183                 {
53184                
53185                     xtype : 'Form',
53186                     xns : Roo.form,
53187                     labelWidth: 100,
53188                     style : 'margin: 10px;',
53189                     
53190                     listeners : {
53191                         actionfailed : function(f, act) {
53192                             // form can return { errors: .... }
53193                                 
53194                             //act.result.errors // invalid form element list...
53195                             //act.result.errorMsg// invalid form element list...
53196                             
53197                             this.dialog.el.unmask();
53198                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53199                                         "Login failed - communication error - try again.");
53200                                       
53201                         },
53202                         actioncomplete: function(re, act) {
53203                              
53204                             Roo.state.Manager.set(
53205                                 this.dialog.realm + '.username',  
53206                                     this.findField('username').getValue()
53207                             );
53208                             Roo.state.Manager.set(
53209                                 this.dialog.realm + '.lang',  
53210                                 this.findField('lang').getValue() 
53211                             );
53212                             
53213                             this.dialog.fillAuth(act.result.data);
53214                               
53215                             this.dialog.hide();
53216                             
53217                             if (Roo.get('loading-mask')) {
53218                                 Roo.get('loading-mask').show();
53219                             }
53220                             Roo.XComponent.build();
53221                             
53222                              
53223                             
53224                         }
53225                     },
53226                     items : [
53227                         {
53228                             xtype : 'TextField',
53229                             xns : Roo.form,
53230                             fieldLabel: "Email Address",
53231                             name: 'username',
53232                             width:200,
53233                             autoCreate : {tag: "input", type: "text", size: "20"}
53234                         },
53235                         {
53236                             xtype : 'TextField',
53237                             xns : Roo.form,
53238                             fieldLabel: "Password",
53239                             inputType: 'password',
53240                             name: 'password',
53241                             width:200,
53242                             autoCreate : {tag: "input", type: "text", size: "20"},
53243                             listeners : {
53244                                 specialkey : function(e,ev) {
53245                                     if (ev.keyCode == 13) {
53246                                         this.form.dialog.el.mask("Logging in");
53247                                         this.form.doAction('submit', {
53248                                             url: this.form.dialog.url,
53249                                             method: this.form.dialog.method
53250                                         });
53251                                     }
53252                                 }
53253                             }  
53254                         },
53255                         {
53256                             xtype : 'ComboBox',
53257                             xns : Roo.form,
53258                             fieldLabel: "Language",
53259                             name : 'langdisp',
53260                             store: {
53261                                 xtype : 'SimpleStore',
53262                                 fields: ['lang', 'ldisp'],
53263                                 data : [
53264                                     [ 'en', 'English' ],
53265                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53266                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53267                                 ]
53268                             },
53269                             
53270                             valueField : 'lang',
53271                             hiddenName:  'lang',
53272                             width: 200,
53273                             displayField:'ldisp',
53274                             typeAhead: false,
53275                             editable: false,
53276                             mode: 'local',
53277                             triggerAction: 'all',
53278                             emptyText:'Select a Language...',
53279                             selectOnFocus:true,
53280                             listeners : {
53281                                 select :  function(cb, rec, ix) {
53282                                     this.form.switchLang(rec.data.lang);
53283                                 }
53284                             }
53285                         
53286                         }
53287                     ]
53288                 }
53289                   
53290                 
53291             ]
53292         }
53293     ],
53294     buttons : [
53295         {
53296             xtype : 'Button',
53297             xns : 'Roo',
53298             text : "Forgot Password",
53299             listeners : {
53300                 click : function() {
53301                     //console.log(this);
53302                     var n = this.form.findField('username').getValue();
53303                     if (!n.length) {
53304                         Roo.MessageBox.alert("Error", "Fill in your email address");
53305                         return;
53306                     }
53307                     Roo.Ajax.request({
53308                         url: this.dialog.url,
53309                         params: {
53310                             passwordRequest: n
53311                         },
53312                         method: this.dialog.method,
53313                         success:  function(response, opts)  {  // check successfull...
53314                         
53315                             var res = this.dialog.processResponse(response);
53316                             if (!res.success) { // error!
53317                                Roo.MessageBox.alert("Error" ,
53318                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53319                                return;
53320                             }
53321                             Roo.MessageBox.alert("Notice" ,
53322                                 "Please check you email for the Password Reset message");
53323                         },
53324                         failure : function() {
53325                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53326                         }
53327                         
53328                     });
53329                 }
53330             }
53331         },
53332         {
53333             xtype : 'Button',
53334             xns : 'Roo',
53335             text : "Login",
53336             listeners : {
53337                 
53338                 click : function () {
53339                         
53340                     this.dialog.el.mask("Logging in");
53341                     this.form.doAction('submit', {
53342                             url: this.dialog.url,
53343                             method: this.dialog.method
53344                     });
53345                 }
53346             }
53347         }
53348     ]
53349   
53350   
53351 })
53352  
53353
53354
53355