roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls)
8864         {
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     
21137     /**
21138      * All child nodes of this node. @type Array
21139      */
21140     this.childNodes = [];
21141     if(!this.childNodes.indexOf){ // indexOf is a must
21142         this.childNodes.indexOf = function(o){
21143             for(var i = 0, len = this.length; i < len; i++){
21144                 if(this[i] == o) {
21145                     return i;
21146                 }
21147             }
21148             return -1;
21149         };
21150     }
21151     /**
21152      * The parent node for this node. @type Node
21153      */
21154     this.parentNode = null;
21155     /**
21156      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21157      */
21158     this.firstChild = null;
21159     /**
21160      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21161      */
21162     this.lastChild = null;
21163     /**
21164      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21165      */
21166     this.previousSibling = null;
21167     /**
21168      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21169      */
21170     this.nextSibling = null;
21171
21172     this.addEvents({
21173        /**
21174         * @event append
21175         * Fires when a new child node is appended
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The newly appended node
21179         * @param {Number} index The index of the newly appended node
21180         */
21181        "append" : true,
21182        /**
21183         * @event remove
21184         * Fires when a child node is removed
21185         * @param {Tree} tree The owner tree
21186         * @param {Node} this This node
21187         * @param {Node} node The removed node
21188         */
21189        "remove" : true,
21190        /**
21191         * @event move
21192         * Fires when this node is moved to a new location in the tree
21193         * @param {Tree} tree The owner tree
21194         * @param {Node} this This node
21195         * @param {Node} oldParent The old parent of this node
21196         * @param {Node} newParent The new parent of this node
21197         * @param {Number} index The index it was moved to
21198         */
21199        "move" : true,
21200        /**
21201         * @event insert
21202         * Fires when a new child node is inserted.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node inserted
21206         * @param {Node} refNode The child node the node was inserted before
21207         */
21208        "insert" : true,
21209        /**
21210         * @event beforeappend
21211         * Fires before a new child is appended, return false to cancel the append.
21212         * @param {Tree} tree The owner tree
21213         * @param {Node} this This node
21214         * @param {Node} node The child node to be appended
21215         */
21216        "beforeappend" : true,
21217        /**
21218         * @event beforeremove
21219         * Fires before a child is removed, return false to cancel the remove.
21220         * @param {Tree} tree The owner tree
21221         * @param {Node} this This node
21222         * @param {Node} node The child node to be removed
21223         */
21224        "beforeremove" : true,
21225        /**
21226         * @event beforemove
21227         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21228         * @param {Tree} tree The owner tree
21229         * @param {Node} this This node
21230         * @param {Node} oldParent The parent of this node
21231         * @param {Node} newParent The new parent this node is moving to
21232         * @param {Number} index The index it is being moved to
21233         */
21234        "beforemove" : true,
21235        /**
21236         * @event beforeinsert
21237         * Fires before a new child is inserted, return false to cancel the insert.
21238         * @param {Tree} tree The owner tree
21239         * @param {Node} this This node
21240         * @param {Node} node The child node to be inserted
21241         * @param {Node} refNode The child node the node is being inserted before
21242         */
21243        "beforeinsert" : true
21244    });
21245     this.listeners = this.attributes.listeners;
21246     Roo.data.Node.superclass.constructor.call(this);
21247 };
21248
21249 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21250     fireEvent : function(evtName){
21251         // first do standard event for this node
21252         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21253             return false;
21254         }
21255         // then bubble it up to the tree if the event wasn't cancelled
21256         var ot = this.getOwnerTree();
21257         if(ot){
21258             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21259                 return false;
21260             }
21261         }
21262         return true;
21263     },
21264
21265     /**
21266      * Returns true if this node is a leaf
21267      * @return {Boolean}
21268      */
21269     isLeaf : function(){
21270         return this.leaf === true;
21271     },
21272
21273     // private
21274     setFirstChild : function(node){
21275         this.firstChild = node;
21276     },
21277
21278     //private
21279     setLastChild : function(node){
21280         this.lastChild = node;
21281     },
21282
21283
21284     /**
21285      * Returns true if this node is the last child of its parent
21286      * @return {Boolean}
21287      */
21288     isLast : function(){
21289        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21290     },
21291
21292     /**
21293      * Returns true if this node is the first child of its parent
21294      * @return {Boolean}
21295      */
21296     isFirst : function(){
21297        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21298     },
21299
21300     hasChildNodes : function(){
21301         return !this.isLeaf() && this.childNodes.length > 0;
21302     },
21303
21304     /**
21305      * Insert node(s) as the last child node of this node.
21306      * @param {Node/Array} node The node or Array of nodes to append
21307      * @return {Node} The appended node if single append, or null if an array was passed
21308      */
21309     appendChild : function(node){
21310         var multi = false;
21311         if(node instanceof Array){
21312             multi = node;
21313         }else if(arguments.length > 1){
21314             multi = arguments;
21315         }
21316         // if passed an array or multiple args do them one by one
21317         if(multi){
21318             for(var i = 0, len = multi.length; i < len; i++) {
21319                 this.appendChild(multi[i]);
21320             }
21321         }else{
21322             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21323                 return false;
21324             }
21325             var index = this.childNodes.length;
21326             var oldParent = node.parentNode;
21327             // it's a move, make sure we move it cleanly
21328             if(oldParent){
21329                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21330                     return false;
21331                 }
21332                 oldParent.removeChild(node);
21333             }
21334             index = this.childNodes.length;
21335             if(index == 0){
21336                 this.setFirstChild(node);
21337             }
21338             this.childNodes.push(node);
21339             node.parentNode = this;
21340             var ps = this.childNodes[index-1];
21341             if(ps){
21342                 node.previousSibling = ps;
21343                 ps.nextSibling = node;
21344             }else{
21345                 node.previousSibling = null;
21346             }
21347             node.nextSibling = null;
21348             this.setLastChild(node);
21349             node.setOwnerTree(this.getOwnerTree());
21350             this.fireEvent("append", this.ownerTree, this, node, index);
21351             if(oldParent){
21352                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21353             }
21354             return node;
21355         }
21356     },
21357
21358     /**
21359      * Removes a child node from this node.
21360      * @param {Node} node The node to remove
21361      * @return {Node} The removed node
21362      */
21363     removeChild : function(node){
21364         var index = this.childNodes.indexOf(node);
21365         if(index == -1){
21366             return false;
21367         }
21368         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21369             return false;
21370         }
21371
21372         // remove it from childNodes collection
21373         this.childNodes.splice(index, 1);
21374
21375         // update siblings
21376         if(node.previousSibling){
21377             node.previousSibling.nextSibling = node.nextSibling;
21378         }
21379         if(node.nextSibling){
21380             node.nextSibling.previousSibling = node.previousSibling;
21381         }
21382
21383         // update child refs
21384         if(this.firstChild == node){
21385             this.setFirstChild(node.nextSibling);
21386         }
21387         if(this.lastChild == node){
21388             this.setLastChild(node.previousSibling);
21389         }
21390
21391         node.setOwnerTree(null);
21392         // clear any references from the node
21393         node.parentNode = null;
21394         node.previousSibling = null;
21395         node.nextSibling = null;
21396         this.fireEvent("remove", this.ownerTree, this, node);
21397         return node;
21398     },
21399
21400     /**
21401      * Inserts the first node before the second node in this nodes childNodes collection.
21402      * @param {Node} node The node to insert
21403      * @param {Node} refNode The node to insert before (if null the node is appended)
21404      * @return {Node} The inserted node
21405      */
21406     insertBefore : function(node, refNode){
21407         if(!refNode){ // like standard Dom, refNode can be null for append
21408             return this.appendChild(node);
21409         }
21410         // nothing to do
21411         if(node == refNode){
21412             return false;
21413         }
21414
21415         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21416             return false;
21417         }
21418         var index = this.childNodes.indexOf(refNode);
21419         var oldParent = node.parentNode;
21420         var refIndex = index;
21421
21422         // when moving internally, indexes will change after remove
21423         if(oldParent == this && this.childNodes.indexOf(node) < index){
21424             refIndex--;
21425         }
21426
21427         // it's a move, make sure we move it cleanly
21428         if(oldParent){
21429             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21430                 return false;
21431             }
21432             oldParent.removeChild(node);
21433         }
21434         if(refIndex == 0){
21435             this.setFirstChild(node);
21436         }
21437         this.childNodes.splice(refIndex, 0, node);
21438         node.parentNode = this;
21439         var ps = this.childNodes[refIndex-1];
21440         if(ps){
21441             node.previousSibling = ps;
21442             ps.nextSibling = node;
21443         }else{
21444             node.previousSibling = null;
21445         }
21446         node.nextSibling = refNode;
21447         refNode.previousSibling = node;
21448         node.setOwnerTree(this.getOwnerTree());
21449         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21450         if(oldParent){
21451             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21452         }
21453         return node;
21454     },
21455
21456     /**
21457      * Returns the child node at the specified index.
21458      * @param {Number} index
21459      * @return {Node}
21460      */
21461     item : function(index){
21462         return this.childNodes[index];
21463     },
21464
21465     /**
21466      * Replaces one child node in this node with another.
21467      * @param {Node} newChild The replacement node
21468      * @param {Node} oldChild The node to replace
21469      * @return {Node} The replaced node
21470      */
21471     replaceChild : function(newChild, oldChild){
21472         this.insertBefore(newChild, oldChild);
21473         this.removeChild(oldChild);
21474         return oldChild;
21475     },
21476
21477     /**
21478      * Returns the index of a child node
21479      * @param {Node} node
21480      * @return {Number} The index of the node or -1 if it was not found
21481      */
21482     indexOf : function(child){
21483         return this.childNodes.indexOf(child);
21484     },
21485
21486     /**
21487      * Returns the tree this node is in.
21488      * @return {Tree}
21489      */
21490     getOwnerTree : function(){
21491         // if it doesn't have one, look for one
21492         if(!this.ownerTree){
21493             var p = this;
21494             while(p){
21495                 if(p.ownerTree){
21496                     this.ownerTree = p.ownerTree;
21497                     break;
21498                 }
21499                 p = p.parentNode;
21500             }
21501         }
21502         return this.ownerTree;
21503     },
21504
21505     /**
21506      * Returns depth of this node (the root node has a depth of 0)
21507      * @return {Number}
21508      */
21509     getDepth : function(){
21510         var depth = 0;
21511         var p = this;
21512         while(p.parentNode){
21513             ++depth;
21514             p = p.parentNode;
21515         }
21516         return depth;
21517     },
21518
21519     // private
21520     setOwnerTree : function(tree){
21521         // if it's move, we need to update everyone
21522         if(tree != this.ownerTree){
21523             if(this.ownerTree){
21524                 this.ownerTree.unregisterNode(this);
21525             }
21526             this.ownerTree = tree;
21527             var cs = this.childNodes;
21528             for(var i = 0, len = cs.length; i < len; i++) {
21529                 cs[i].setOwnerTree(tree);
21530             }
21531             if(tree){
21532                 tree.registerNode(this);
21533             }
21534         }
21535     },
21536
21537     /**
21538      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21539      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21540      * @return {String} The path
21541      */
21542     getPath : function(attr){
21543         attr = attr || "id";
21544         var p = this.parentNode;
21545         var b = [this.attributes[attr]];
21546         while(p){
21547             b.unshift(p.attributes[attr]);
21548             p = p.parentNode;
21549         }
21550         var sep = this.getOwnerTree().pathSeparator;
21551         return sep + b.join(sep);
21552     },
21553
21554     /**
21555      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21556      * function call will be the scope provided or the current node. The arguments to the function
21557      * will be the args provided or the current node. If the function returns false at any point,
21558      * the bubble is stopped.
21559      * @param {Function} fn The function to call
21560      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21561      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21562      */
21563     bubble : function(fn, scope, args){
21564         var p = this;
21565         while(p){
21566             if(fn.call(scope || p, args || p) === false){
21567                 break;
21568             }
21569             p = p.parentNode;
21570         }
21571     },
21572
21573     /**
21574      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21575      * function call will be the scope provided or the current node. The arguments to the function
21576      * will be the args provided or the current node. If the function returns false at any point,
21577      * the cascade is stopped on that branch.
21578      * @param {Function} fn The function to call
21579      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21580      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21581      */
21582     cascade : function(fn, scope, args){
21583         if(fn.call(scope || this, args || this) !== false){
21584             var cs = this.childNodes;
21585             for(var i = 0, len = cs.length; i < len; i++) {
21586                 cs[i].cascade(fn, scope, args);
21587             }
21588         }
21589     },
21590
21591     /**
21592      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21593      * function call will be the scope provided or the current node. The arguments to the function
21594      * will be the args provided or the current node. If the function returns false at any point,
21595      * the iteration stops.
21596      * @param {Function} fn The function to call
21597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21599      */
21600     eachChild : function(fn, scope, args){
21601         var cs = this.childNodes;
21602         for(var i = 0, len = cs.length; i < len; i++) {
21603                 if(fn.call(scope || this, args || cs[i]) === false){
21604                     break;
21605                 }
21606         }
21607     },
21608
21609     /**
21610      * Finds the first child that has the attribute with the specified value.
21611      * @param {String} attribute The attribute name
21612      * @param {Mixed} value The value to search for
21613      * @return {Node} The found child or null if none was found
21614      */
21615     findChild : function(attribute, value){
21616         var cs = this.childNodes;
21617         for(var i = 0, len = cs.length; i < len; i++) {
21618                 if(cs[i].attributes[attribute] == value){
21619                     return cs[i];
21620                 }
21621         }
21622         return null;
21623     },
21624
21625     /**
21626      * Finds the first child by a custom function. The child matches if the function passed
21627      * returns true.
21628      * @param {Function} fn
21629      * @param {Object} scope (optional)
21630      * @return {Node} The found child or null if none was found
21631      */
21632     findChildBy : function(fn, scope){
21633         var cs = this.childNodes;
21634         for(var i = 0, len = cs.length; i < len; i++) {
21635                 if(fn.call(scope||cs[i], cs[i]) === true){
21636                     return cs[i];
21637                 }
21638         }
21639         return null;
21640     },
21641
21642     /**
21643      * Sorts this nodes children using the supplied sort function
21644      * @param {Function} fn
21645      * @param {Object} scope (optional)
21646      */
21647     sort : function(fn, scope){
21648         var cs = this.childNodes;
21649         var len = cs.length;
21650         if(len > 0){
21651             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21652             cs.sort(sortFn);
21653             for(var i = 0; i < len; i++){
21654                 var n = cs[i];
21655                 n.previousSibling = cs[i-1];
21656                 n.nextSibling = cs[i+1];
21657                 if(i == 0){
21658                     this.setFirstChild(n);
21659                 }
21660                 if(i == len-1){
21661                     this.setLastChild(n);
21662                 }
21663             }
21664         }
21665     },
21666
21667     /**
21668      * Returns true if this node is an ancestor (at any point) of the passed node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     contains : function(node){
21673         return node.isAncestor(this);
21674     },
21675
21676     /**
21677      * Returns true if the passed node is an ancestor (at any point) of this node.
21678      * @param {Node} node
21679      * @return {Boolean}
21680      */
21681     isAncestor : function(node){
21682         var p = this.parentNode;
21683         while(p){
21684             if(p == node){
21685                 return true;
21686             }
21687             p = p.parentNode;
21688         }
21689         return false;
21690     },
21691
21692     toString : function(){
21693         return "[Node"+(this.id?" "+this.id:"")+"]";
21694     }
21695 });/*
21696  * Based on:
21697  * Ext JS Library 1.1.1
21698  * Copyright(c) 2006-2007, Ext JS, LLC.
21699  *
21700  * Originally Released Under LGPL - original licence link has changed is not relivant.
21701  *
21702  * Fork - LGPL
21703  * <script type="text/javascript">
21704  */
21705  
21706
21707 /**
21708  * @class Roo.ComponentMgr
21709  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21710  * @singleton
21711  */
21712 Roo.ComponentMgr = function(){
21713     var all = new Roo.util.MixedCollection();
21714
21715     return {
21716         /**
21717          * Registers a component.
21718          * @param {Roo.Component} c The component
21719          */
21720         register : function(c){
21721             all.add(c);
21722         },
21723
21724         /**
21725          * Unregisters a component.
21726          * @param {Roo.Component} c The component
21727          */
21728         unregister : function(c){
21729             all.remove(c);
21730         },
21731
21732         /**
21733          * Returns a component by id
21734          * @param {String} id The component id
21735          */
21736         get : function(id){
21737             return all.get(id);
21738         },
21739
21740         /**
21741          * Registers a function that will be called when a specified component is added to ComponentMgr
21742          * @param {String} id The component id
21743          * @param {Funtction} fn The callback function
21744          * @param {Object} scope The scope of the callback
21745          */
21746         onAvailable : function(id, fn, scope){
21747             all.on("add", function(index, o){
21748                 if(o.id == id){
21749                     fn.call(scope || o, o);
21750                     all.un("add", fn, scope);
21751                 }
21752             });
21753         }
21754     };
21755 }();/*
21756  * Based on:
21757  * Ext JS Library 1.1.1
21758  * Copyright(c) 2006-2007, Ext JS, LLC.
21759  *
21760  * Originally Released Under LGPL - original licence link has changed is not relivant.
21761  *
21762  * Fork - LGPL
21763  * <script type="text/javascript">
21764  */
21765  
21766 /**
21767  * @class Roo.Component
21768  * @extends Roo.util.Observable
21769  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21770  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21771  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21772  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21773  * All visual components (widgets) that require rendering into a layout should subclass Component.
21774  * @constructor
21775  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21776  * 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
21777  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21778  */
21779 Roo.Component = function(config){
21780     config = config || {};
21781     if(config.tagName || config.dom || typeof config == "string"){ // element object
21782         config = {el: config, id: config.id || config};
21783     }
21784     this.initialConfig = config;
21785
21786     Roo.apply(this, config);
21787     this.addEvents({
21788         /**
21789          * @event disable
21790          * Fires after the component is disabled.
21791              * @param {Roo.Component} this
21792              */
21793         disable : true,
21794         /**
21795          * @event enable
21796          * Fires after the component is enabled.
21797              * @param {Roo.Component} this
21798              */
21799         enable : true,
21800         /**
21801          * @event beforeshow
21802          * Fires before the component is shown.  Return false to stop the show.
21803              * @param {Roo.Component} this
21804              */
21805         beforeshow : true,
21806         /**
21807          * @event show
21808          * Fires after the component is shown.
21809              * @param {Roo.Component} this
21810              */
21811         show : true,
21812         /**
21813          * @event beforehide
21814          * Fires before the component is hidden. Return false to stop the hide.
21815              * @param {Roo.Component} this
21816              */
21817         beforehide : true,
21818         /**
21819          * @event hide
21820          * Fires after the component is hidden.
21821              * @param {Roo.Component} this
21822              */
21823         hide : true,
21824         /**
21825          * @event beforerender
21826          * Fires before the component is rendered. Return false to stop the render.
21827              * @param {Roo.Component} this
21828              */
21829         beforerender : true,
21830         /**
21831          * @event render
21832          * Fires after the component is rendered.
21833              * @param {Roo.Component} this
21834              */
21835         render : true,
21836         /**
21837          * @event beforedestroy
21838          * Fires before the component is destroyed. Return false to stop the destroy.
21839              * @param {Roo.Component} this
21840              */
21841         beforedestroy : true,
21842         /**
21843          * @event destroy
21844          * Fires after the component is destroyed.
21845              * @param {Roo.Component} this
21846              */
21847         destroy : true
21848     });
21849     if(!this.id){
21850         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21851     }
21852     Roo.ComponentMgr.register(this);
21853     Roo.Component.superclass.constructor.call(this);
21854     this.initComponent();
21855     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21856         this.render(this.renderTo);
21857         delete this.renderTo;
21858     }
21859 };
21860
21861 /** @private */
21862 Roo.Component.AUTO_ID = 1000;
21863
21864 Roo.extend(Roo.Component, Roo.util.Observable, {
21865     /**
21866      * @scope Roo.Component.prototype
21867      * @type {Boolean}
21868      * true if this component is hidden. Read-only.
21869      */
21870     hidden : false,
21871     /**
21872      * @type {Boolean}
21873      * true if this component is disabled. Read-only.
21874      */
21875     disabled : false,
21876     /**
21877      * @type {Boolean}
21878      * true if this component has been rendered. Read-only.
21879      */
21880     rendered : false,
21881     
21882     /** @cfg {String} disableClass
21883      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21884      */
21885     disabledClass : "x-item-disabled",
21886         /** @cfg {Boolean} allowDomMove
21887          * Whether the component can move the Dom node when rendering (defaults to true).
21888          */
21889     allowDomMove : true,
21890     /** @cfg {String} hideMode
21891      * How this component should hidden. Supported values are
21892      * "visibility" (css visibility), "offsets" (negative offset position) and
21893      * "display" (css display) - defaults to "display".
21894      */
21895     hideMode: 'display',
21896
21897     /** @private */
21898     ctype : "Roo.Component",
21899
21900     /**
21901      * @cfg {String} actionMode 
21902      * which property holds the element that used for  hide() / show() / disable() / enable()
21903      * default is 'el' 
21904      */
21905     actionMode : "el",
21906
21907     /** @private */
21908     getActionEl : function(){
21909         return this[this.actionMode];
21910     },
21911
21912     initComponent : Roo.emptyFn,
21913     /**
21914      * If this is a lazy rendering component, render it to its container element.
21915      * @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.
21916      */
21917     render : function(container, position){
21918         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21919             if(!container && this.el){
21920                 this.el = Roo.get(this.el);
21921                 container = this.el.dom.parentNode;
21922                 this.allowDomMove = false;
21923             }
21924             this.container = Roo.get(container);
21925             this.rendered = true;
21926             if(position !== undefined){
21927                 if(typeof position == 'number'){
21928                     position = this.container.dom.childNodes[position];
21929                 }else{
21930                     position = Roo.getDom(position);
21931                 }
21932             }
21933             this.onRender(this.container, position || null);
21934             if(this.cls){
21935                 this.el.addClass(this.cls);
21936                 delete this.cls;
21937             }
21938             if(this.style){
21939                 this.el.applyStyles(this.style);
21940                 delete this.style;
21941             }
21942             this.fireEvent("render", this);
21943             this.afterRender(this.container);
21944             if(this.hidden){
21945                 this.hide();
21946             }
21947             if(this.disabled){
21948                 this.disable();
21949             }
21950         }
21951         return this;
21952     },
21953
21954     /** @private */
21955     // default function is not really useful
21956     onRender : function(ct, position){
21957         if(this.el){
21958             this.el = Roo.get(this.el);
21959             if(this.allowDomMove !== false){
21960                 ct.dom.insertBefore(this.el.dom, position);
21961             }
21962         }
21963     },
21964
21965     /** @private */
21966     getAutoCreate : function(){
21967         var cfg = typeof this.autoCreate == "object" ?
21968                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21969         if(this.id && !cfg.id){
21970             cfg.id = this.id;
21971         }
21972         return cfg;
21973     },
21974
21975     /** @private */
21976     afterRender : Roo.emptyFn,
21977
21978     /**
21979      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21980      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21981      */
21982     destroy : function(){
21983         if(this.fireEvent("beforedestroy", this) !== false){
21984             this.purgeListeners();
21985             this.beforeDestroy();
21986             if(this.rendered){
21987                 this.el.removeAllListeners();
21988                 this.el.remove();
21989                 if(this.actionMode == "container"){
21990                     this.container.remove();
21991                 }
21992             }
21993             this.onDestroy();
21994             Roo.ComponentMgr.unregister(this);
21995             this.fireEvent("destroy", this);
21996         }
21997     },
21998
21999         /** @private */
22000     beforeDestroy : function(){
22001
22002     },
22003
22004         /** @private */
22005         onDestroy : function(){
22006
22007     },
22008
22009     /**
22010      * Returns the underlying {@link Roo.Element}.
22011      * @return {Roo.Element} The element
22012      */
22013     getEl : function(){
22014         return this.el;
22015     },
22016
22017     /**
22018      * Returns the id of this component.
22019      * @return {String}
22020      */
22021     getId : function(){
22022         return this.id;
22023     },
22024
22025     /**
22026      * Try to focus this component.
22027      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22028      * @return {Roo.Component} this
22029      */
22030     focus : function(selectText){
22031         if(this.rendered){
22032             this.el.focus();
22033             if(selectText === true){
22034                 this.el.dom.select();
22035             }
22036         }
22037         return this;
22038     },
22039
22040     /** @private */
22041     blur : function(){
22042         if(this.rendered){
22043             this.el.blur();
22044         }
22045         return this;
22046     },
22047
22048     /**
22049      * Disable this component.
22050      * @return {Roo.Component} this
22051      */
22052     disable : function(){
22053         if(this.rendered){
22054             this.onDisable();
22055         }
22056         this.disabled = true;
22057         this.fireEvent("disable", this);
22058         return this;
22059     },
22060
22061         // private
22062     onDisable : function(){
22063         this.getActionEl().addClass(this.disabledClass);
22064         this.el.dom.disabled = true;
22065     },
22066
22067     /**
22068      * Enable this component.
22069      * @return {Roo.Component} this
22070      */
22071     enable : function(){
22072         if(this.rendered){
22073             this.onEnable();
22074         }
22075         this.disabled = false;
22076         this.fireEvent("enable", this);
22077         return this;
22078     },
22079
22080         // private
22081     onEnable : function(){
22082         this.getActionEl().removeClass(this.disabledClass);
22083         this.el.dom.disabled = false;
22084     },
22085
22086     /**
22087      * Convenience function for setting disabled/enabled by boolean.
22088      * @param {Boolean} disabled
22089      */
22090     setDisabled : function(disabled){
22091         this[disabled ? "disable" : "enable"]();
22092     },
22093
22094     /**
22095      * Show this component.
22096      * @return {Roo.Component} this
22097      */
22098     show: function(){
22099         if(this.fireEvent("beforeshow", this) !== false){
22100             this.hidden = false;
22101             if(this.rendered){
22102                 this.onShow();
22103             }
22104             this.fireEvent("show", this);
22105         }
22106         return this;
22107     },
22108
22109     // private
22110     onShow : function(){
22111         var ae = this.getActionEl();
22112         if(this.hideMode == 'visibility'){
22113             ae.dom.style.visibility = "visible";
22114         }else if(this.hideMode == 'offsets'){
22115             ae.removeClass('x-hidden');
22116         }else{
22117             ae.dom.style.display = "";
22118         }
22119     },
22120
22121     /**
22122      * Hide this component.
22123      * @return {Roo.Component} this
22124      */
22125     hide: function(){
22126         if(this.fireEvent("beforehide", this) !== false){
22127             this.hidden = true;
22128             if(this.rendered){
22129                 this.onHide();
22130             }
22131             this.fireEvent("hide", this);
22132         }
22133         return this;
22134     },
22135
22136     // private
22137     onHide : function(){
22138         var ae = this.getActionEl();
22139         if(this.hideMode == 'visibility'){
22140             ae.dom.style.visibility = "hidden";
22141         }else if(this.hideMode == 'offsets'){
22142             ae.addClass('x-hidden');
22143         }else{
22144             ae.dom.style.display = "none";
22145         }
22146     },
22147
22148     /**
22149      * Convenience function to hide or show this component by boolean.
22150      * @param {Boolean} visible True to show, false to hide
22151      * @return {Roo.Component} this
22152      */
22153     setVisible: function(visible){
22154         if(visible) {
22155             this.show();
22156         }else{
22157             this.hide();
22158         }
22159         return this;
22160     },
22161
22162     /**
22163      * Returns true if this component is visible.
22164      */
22165     isVisible : function(){
22166         return this.getActionEl().isVisible();
22167     },
22168
22169     cloneConfig : function(overrides){
22170         overrides = overrides || {};
22171         var id = overrides.id || Roo.id();
22172         var cfg = Roo.applyIf(overrides, this.initialConfig);
22173         cfg.id = id; // prevent dup id
22174         return new this.constructor(cfg);
22175     }
22176 });/*
22177  * Based on:
22178  * Ext JS Library 1.1.1
22179  * Copyright(c) 2006-2007, Ext JS, LLC.
22180  *
22181  * Originally Released Under LGPL - original licence link has changed is not relivant.
22182  *
22183  * Fork - LGPL
22184  * <script type="text/javascript">
22185  */
22186  (function(){ 
22187 /**
22188  * @class Roo.Layer
22189  * @extends Roo.Element
22190  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22191  * automatic maintaining of shadow/shim positions.
22192  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22193  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22194  * you can pass a string with a CSS class name. False turns off the shadow.
22195  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22196  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22197  * @cfg {String} cls CSS class to add to the element
22198  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22199  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22200  * @constructor
22201  * @param {Object} config An object with config options.
22202  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22203  */
22204
22205 Roo.Layer = function(config, existingEl){
22206     config = config || {};
22207     var dh = Roo.DomHelper;
22208     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22209     if(existingEl){
22210         this.dom = Roo.getDom(existingEl);
22211     }
22212     if(!this.dom){
22213         var o = config.dh || {tag: "div", cls: "x-layer"};
22214         this.dom = dh.append(pel, o);
22215     }
22216     if(config.cls){
22217         this.addClass(config.cls);
22218     }
22219     this.constrain = config.constrain !== false;
22220     this.visibilityMode = Roo.Element.VISIBILITY;
22221     if(config.id){
22222         this.id = this.dom.id = config.id;
22223     }else{
22224         this.id = Roo.id(this.dom);
22225     }
22226     this.zindex = config.zindex || this.getZIndex();
22227     this.position("absolute", this.zindex);
22228     if(config.shadow){
22229         this.shadowOffset = config.shadowOffset || 4;
22230         this.shadow = new Roo.Shadow({
22231             offset : this.shadowOffset,
22232             mode : config.shadow
22233         });
22234     }else{
22235         this.shadowOffset = 0;
22236     }
22237     this.useShim = config.shim !== false && Roo.useShims;
22238     this.useDisplay = config.useDisplay;
22239     this.hide();
22240 };
22241
22242 var supr = Roo.Element.prototype;
22243
22244 // shims are shared among layer to keep from having 100 iframes
22245 var shims = [];
22246
22247 Roo.extend(Roo.Layer, Roo.Element, {
22248
22249     getZIndex : function(){
22250         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22251     },
22252
22253     getShim : function(){
22254         if(!this.useShim){
22255             return null;
22256         }
22257         if(this.shim){
22258             return this.shim;
22259         }
22260         var shim = shims.shift();
22261         if(!shim){
22262             shim = this.createShim();
22263             shim.enableDisplayMode('block');
22264             shim.dom.style.display = 'none';
22265             shim.dom.style.visibility = 'visible';
22266         }
22267         var pn = this.dom.parentNode;
22268         if(shim.dom.parentNode != pn){
22269             pn.insertBefore(shim.dom, this.dom);
22270         }
22271         shim.setStyle('z-index', this.getZIndex()-2);
22272         this.shim = shim;
22273         return shim;
22274     },
22275
22276     hideShim : function(){
22277         if(this.shim){
22278             this.shim.setDisplayed(false);
22279             shims.push(this.shim);
22280             delete this.shim;
22281         }
22282     },
22283
22284     disableShadow : function(){
22285         if(this.shadow){
22286             this.shadowDisabled = true;
22287             this.shadow.hide();
22288             this.lastShadowOffset = this.shadowOffset;
22289             this.shadowOffset = 0;
22290         }
22291     },
22292
22293     enableShadow : function(show){
22294         if(this.shadow){
22295             this.shadowDisabled = false;
22296             this.shadowOffset = this.lastShadowOffset;
22297             delete this.lastShadowOffset;
22298             if(show){
22299                 this.sync(true);
22300             }
22301         }
22302     },
22303
22304     // private
22305     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22306     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22307     sync : function(doShow){
22308         var sw = this.shadow;
22309         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22310             var sh = this.getShim();
22311
22312             var w = this.getWidth(),
22313                 h = this.getHeight();
22314
22315             var l = this.getLeft(true),
22316                 t = this.getTop(true);
22317
22318             if(sw && !this.shadowDisabled){
22319                 if(doShow && !sw.isVisible()){
22320                     sw.show(this);
22321                 }else{
22322                     sw.realign(l, t, w, h);
22323                 }
22324                 if(sh){
22325                     if(doShow){
22326                        sh.show();
22327                     }
22328                     // fit the shim behind the shadow, so it is shimmed too
22329                     var a = sw.adjusts, s = sh.dom.style;
22330                     s.left = (Math.min(l, l+a.l))+"px";
22331                     s.top = (Math.min(t, t+a.t))+"px";
22332                     s.width = (w+a.w)+"px";
22333                     s.height = (h+a.h)+"px";
22334                 }
22335             }else if(sh){
22336                 if(doShow){
22337                    sh.show();
22338                 }
22339                 sh.setSize(w, h);
22340                 sh.setLeftTop(l, t);
22341             }
22342             
22343         }
22344     },
22345
22346     // private
22347     destroy : function(){
22348         this.hideShim();
22349         if(this.shadow){
22350             this.shadow.hide();
22351         }
22352         this.removeAllListeners();
22353         var pn = this.dom.parentNode;
22354         if(pn){
22355             pn.removeChild(this.dom);
22356         }
22357         Roo.Element.uncache(this.id);
22358     },
22359
22360     remove : function(){
22361         this.destroy();
22362     },
22363
22364     // private
22365     beginUpdate : function(){
22366         this.updating = true;
22367     },
22368
22369     // private
22370     endUpdate : function(){
22371         this.updating = false;
22372         this.sync(true);
22373     },
22374
22375     // private
22376     hideUnders : function(negOffset){
22377         if(this.shadow){
22378             this.shadow.hide();
22379         }
22380         this.hideShim();
22381     },
22382
22383     // private
22384     constrainXY : function(){
22385         if(this.constrain){
22386             var vw = Roo.lib.Dom.getViewWidth(),
22387                 vh = Roo.lib.Dom.getViewHeight();
22388             var s = Roo.get(document).getScroll();
22389
22390             var xy = this.getXY();
22391             var x = xy[0], y = xy[1];   
22392             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22393             // only move it if it needs it
22394             var moved = false;
22395             // first validate right/bottom
22396             if((x + w) > vw+s.left){
22397                 x = vw - w - this.shadowOffset;
22398                 moved = true;
22399             }
22400             if((y + h) > vh+s.top){
22401                 y = vh - h - this.shadowOffset;
22402                 moved = true;
22403             }
22404             // then make sure top/left isn't negative
22405             if(x < s.left){
22406                 x = s.left;
22407                 moved = true;
22408             }
22409             if(y < s.top){
22410                 y = s.top;
22411                 moved = true;
22412             }
22413             if(moved){
22414                 if(this.avoidY){
22415                     var ay = this.avoidY;
22416                     if(y <= ay && (y+h) >= ay){
22417                         y = ay-h-5;   
22418                     }
22419                 }
22420                 xy = [x, y];
22421                 this.storeXY(xy);
22422                 supr.setXY.call(this, xy);
22423                 this.sync();
22424             }
22425         }
22426     },
22427
22428     isVisible : function(){
22429         return this.visible;    
22430     },
22431
22432     // private
22433     showAction : function(){
22434         this.visible = true; // track visibility to prevent getStyle calls
22435         if(this.useDisplay === true){
22436             this.setDisplayed("");
22437         }else if(this.lastXY){
22438             supr.setXY.call(this, this.lastXY);
22439         }else if(this.lastLT){
22440             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22441         }
22442     },
22443
22444     // private
22445     hideAction : function(){
22446         this.visible = false;
22447         if(this.useDisplay === true){
22448             this.setDisplayed(false);
22449         }else{
22450             this.setLeftTop(-10000,-10000);
22451         }
22452     },
22453
22454     // overridden Element method
22455     setVisible : function(v, a, d, c, e){
22456         if(v){
22457             this.showAction();
22458         }
22459         if(a && v){
22460             var cb = function(){
22461                 this.sync(true);
22462                 if(c){
22463                     c();
22464                 }
22465             }.createDelegate(this);
22466             supr.setVisible.call(this, true, true, d, cb, e);
22467         }else{
22468             if(!v){
22469                 this.hideUnders(true);
22470             }
22471             var cb = c;
22472             if(a){
22473                 cb = function(){
22474                     this.hideAction();
22475                     if(c){
22476                         c();
22477                     }
22478                 }.createDelegate(this);
22479             }
22480             supr.setVisible.call(this, v, a, d, cb, e);
22481             if(v){
22482                 this.sync(true);
22483             }else if(!a){
22484                 this.hideAction();
22485             }
22486         }
22487     },
22488
22489     storeXY : function(xy){
22490         delete this.lastLT;
22491         this.lastXY = xy;
22492     },
22493
22494     storeLeftTop : function(left, top){
22495         delete this.lastXY;
22496         this.lastLT = [left, top];
22497     },
22498
22499     // private
22500     beforeFx : function(){
22501         this.beforeAction();
22502         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22503     },
22504
22505     // private
22506     afterFx : function(){
22507         Roo.Layer.superclass.afterFx.apply(this, arguments);
22508         this.sync(this.isVisible());
22509     },
22510
22511     // private
22512     beforeAction : function(){
22513         if(!this.updating && this.shadow){
22514             this.shadow.hide();
22515         }
22516     },
22517
22518     // overridden Element method
22519     setLeft : function(left){
22520         this.storeLeftTop(left, this.getTop(true));
22521         supr.setLeft.apply(this, arguments);
22522         this.sync();
22523     },
22524
22525     setTop : function(top){
22526         this.storeLeftTop(this.getLeft(true), top);
22527         supr.setTop.apply(this, arguments);
22528         this.sync();
22529     },
22530
22531     setLeftTop : function(left, top){
22532         this.storeLeftTop(left, top);
22533         supr.setLeftTop.apply(this, arguments);
22534         this.sync();
22535     },
22536
22537     setXY : function(xy, a, d, c, e){
22538         this.fixDisplay();
22539         this.beforeAction();
22540         this.storeXY(xy);
22541         var cb = this.createCB(c);
22542         supr.setXY.call(this, xy, a, d, cb, e);
22543         if(!a){
22544             cb();
22545         }
22546     },
22547
22548     // private
22549     createCB : function(c){
22550         var el = this;
22551         return function(){
22552             el.constrainXY();
22553             el.sync(true);
22554             if(c){
22555                 c();
22556             }
22557         };
22558     },
22559
22560     // overridden Element method
22561     setX : function(x, a, d, c, e){
22562         this.setXY([x, this.getY()], a, d, c, e);
22563     },
22564
22565     // overridden Element method
22566     setY : function(y, a, d, c, e){
22567         this.setXY([this.getX(), y], a, d, c, e);
22568     },
22569
22570     // overridden Element method
22571     setSize : function(w, h, a, d, c, e){
22572         this.beforeAction();
22573         var cb = this.createCB(c);
22574         supr.setSize.call(this, w, h, a, d, cb, e);
22575         if(!a){
22576             cb();
22577         }
22578     },
22579
22580     // overridden Element method
22581     setWidth : function(w, a, d, c, e){
22582         this.beforeAction();
22583         var cb = this.createCB(c);
22584         supr.setWidth.call(this, w, a, d, cb, e);
22585         if(!a){
22586             cb();
22587         }
22588     },
22589
22590     // overridden Element method
22591     setHeight : function(h, a, d, c, e){
22592         this.beforeAction();
22593         var cb = this.createCB(c);
22594         supr.setHeight.call(this, h, a, d, cb, e);
22595         if(!a){
22596             cb();
22597         }
22598     },
22599
22600     // overridden Element method
22601     setBounds : function(x, y, w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         if(!a){
22605             this.storeXY([x, y]);
22606             supr.setXY.call(this, [x, y]);
22607             supr.setSize.call(this, w, h, a, d, cb, e);
22608             cb();
22609         }else{
22610             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22611         }
22612         return this;
22613     },
22614     
22615     /**
22616      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22617      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22618      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22619      * @param {Number} zindex The new z-index to set
22620      * @return {this} The Layer
22621      */
22622     setZIndex : function(zindex){
22623         this.zindex = zindex;
22624         this.setStyle("z-index", zindex + 2);
22625         if(this.shadow){
22626             this.shadow.setZIndex(zindex + 1);
22627         }
22628         if(this.shim){
22629             this.shim.setStyle("z-index", zindex);
22630         }
22631     }
22632 });
22633 })();/*
22634  * Based on:
22635  * Ext JS Library 1.1.1
22636  * Copyright(c) 2006-2007, Ext JS, LLC.
22637  *
22638  * Originally Released Under LGPL - original licence link has changed is not relivant.
22639  *
22640  * Fork - LGPL
22641  * <script type="text/javascript">
22642  */
22643
22644
22645 /**
22646  * @class Roo.Shadow
22647  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22648  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22649  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22650  * @constructor
22651  * Create a new Shadow
22652  * @param {Object} config The config object
22653  */
22654 Roo.Shadow = function(config){
22655     Roo.apply(this, config);
22656     if(typeof this.mode != "string"){
22657         this.mode = this.defaultMode;
22658     }
22659     var o = this.offset, a = {h: 0};
22660     var rad = Math.floor(this.offset/2);
22661     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22662         case "drop":
22663             a.w = 0;
22664             a.l = a.t = o;
22665             a.t -= 1;
22666             if(Roo.isIE){
22667                 a.l -= this.offset + rad;
22668                 a.t -= this.offset + rad;
22669                 a.w -= rad;
22670                 a.h -= rad;
22671                 a.t += 1;
22672             }
22673         break;
22674         case "sides":
22675             a.w = (o*2);
22676             a.l = -o;
22677             a.t = o-1;
22678             if(Roo.isIE){
22679                 a.l -= (this.offset - rad);
22680                 a.t -= this.offset + rad;
22681                 a.l += 1;
22682                 a.w -= (this.offset - rad)*2;
22683                 a.w -= rad + 1;
22684                 a.h -= 1;
22685             }
22686         break;
22687         case "frame":
22688             a.w = a.h = (o*2);
22689             a.l = a.t = -o;
22690             a.t += 1;
22691             a.h -= 2;
22692             if(Roo.isIE){
22693                 a.l -= (this.offset - rad);
22694                 a.t -= (this.offset - rad);
22695                 a.l += 1;
22696                 a.w -= (this.offset + rad + 1);
22697                 a.h -= (this.offset + rad);
22698                 a.h += 1;
22699             }
22700         break;
22701     };
22702
22703     this.adjusts = a;
22704 };
22705
22706 Roo.Shadow.prototype = {
22707     /**
22708      * @cfg {String} mode
22709      * The shadow display mode.  Supports the following options:<br />
22710      * sides: Shadow displays on both sides and bottom only<br />
22711      * frame: Shadow displays equally on all four sides<br />
22712      * drop: Traditional bottom-right drop shadow (default)
22713      */
22714     /**
22715      * @cfg {String} offset
22716      * The number of pixels to offset the shadow from the element (defaults to 4)
22717      */
22718     offset: 4,
22719
22720     // private
22721     defaultMode: "drop",
22722
22723     /**
22724      * Displays the shadow under the target element
22725      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22726      */
22727     show : function(target){
22728         target = Roo.get(target);
22729         if(!this.el){
22730             this.el = Roo.Shadow.Pool.pull();
22731             if(this.el.dom.nextSibling != target.dom){
22732                 this.el.insertBefore(target);
22733             }
22734         }
22735         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22736         if(Roo.isIE){
22737             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22738         }
22739         this.realign(
22740             target.getLeft(true),
22741             target.getTop(true),
22742             target.getWidth(),
22743             target.getHeight()
22744         );
22745         this.el.dom.style.display = "block";
22746     },
22747
22748     /**
22749      * Returns true if the shadow is visible, else false
22750      */
22751     isVisible : function(){
22752         return this.el ? true : false;  
22753     },
22754
22755     /**
22756      * Direct alignment when values are already available. Show must be called at least once before
22757      * calling this method to ensure it is initialized.
22758      * @param {Number} left The target element left position
22759      * @param {Number} top The target element top position
22760      * @param {Number} width The target element width
22761      * @param {Number} height The target element height
22762      */
22763     realign : function(l, t, w, h){
22764         if(!this.el){
22765             return;
22766         }
22767         var a = this.adjusts, d = this.el.dom, s = d.style;
22768         var iea = 0;
22769         s.left = (l+a.l)+"px";
22770         s.top = (t+a.t)+"px";
22771         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22772  
22773         if(s.width != sws || s.height != shs){
22774             s.width = sws;
22775             s.height = shs;
22776             if(!Roo.isIE){
22777                 var cn = d.childNodes;
22778                 var sww = Math.max(0, (sw-12))+"px";
22779                 cn[0].childNodes[1].style.width = sww;
22780                 cn[1].childNodes[1].style.width = sww;
22781                 cn[2].childNodes[1].style.width = sww;
22782                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22783             }
22784         }
22785     },
22786
22787     /**
22788      * Hides this shadow
22789      */
22790     hide : function(){
22791         if(this.el){
22792             this.el.dom.style.display = "none";
22793             Roo.Shadow.Pool.push(this.el);
22794             delete this.el;
22795         }
22796     },
22797
22798     /**
22799      * Adjust the z-index of this shadow
22800      * @param {Number} zindex The new z-index
22801      */
22802     setZIndex : function(z){
22803         this.zIndex = z;
22804         if(this.el){
22805             this.el.setStyle("z-index", z);
22806         }
22807     }
22808 };
22809
22810 // Private utility class that manages the internal Shadow cache
22811 Roo.Shadow.Pool = function(){
22812     var p = [];
22813     var markup = Roo.isIE ?
22814                  '<div class="x-ie-shadow"></div>' :
22815                  '<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>';
22816     return {
22817         pull : function(){
22818             var sh = p.shift();
22819             if(!sh){
22820                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22821                 sh.autoBoxAdjust = false;
22822             }
22823             return sh;
22824         },
22825
22826         push : function(sh){
22827             p.push(sh);
22828         }
22829     };
22830 }();/*
22831  * Based on:
22832  * Ext JS Library 1.1.1
22833  * Copyright(c) 2006-2007, Ext JS, LLC.
22834  *
22835  * Originally Released Under LGPL - original licence link has changed is not relivant.
22836  *
22837  * Fork - LGPL
22838  * <script type="text/javascript">
22839  */
22840
22841 /**
22842  * @class Roo.BoxComponent
22843  * @extends Roo.Component
22844  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22845  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22846  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22847  * layout containers.
22848  * @constructor
22849  * @param {Roo.Element/String/Object} config The configuration options.
22850  */
22851 Roo.BoxComponent = function(config){
22852     Roo.Component.call(this, config);
22853     this.addEvents({
22854         /**
22855          * @event resize
22856          * Fires after the component is resized.
22857              * @param {Roo.Component} this
22858              * @param {Number} adjWidth The box-adjusted width that was set
22859              * @param {Number} adjHeight The box-adjusted height that was set
22860              * @param {Number} rawWidth The width that was originally specified
22861              * @param {Number} rawHeight The height that was originally specified
22862              */
22863         resize : true,
22864         /**
22865          * @event move
22866          * Fires after the component is moved.
22867              * @param {Roo.Component} this
22868              * @param {Number} x The new x position
22869              * @param {Number} y The new y position
22870              */
22871         move : true
22872     });
22873 };
22874
22875 Roo.extend(Roo.BoxComponent, Roo.Component, {
22876     // private, set in afterRender to signify that the component has been rendered
22877     boxReady : false,
22878     // private, used to defer height settings to subclasses
22879     deferHeight: false,
22880     /** @cfg {Number} width
22881      * width (optional) size of component
22882      */
22883      /** @cfg {Number} height
22884      * height (optional) size of component
22885      */
22886      
22887     /**
22888      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22889      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22890      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22891      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22892      * @return {Roo.BoxComponent} this
22893      */
22894     setSize : function(w, h){
22895         // support for standard size objects
22896         if(typeof w == 'object'){
22897             h = w.height;
22898             w = w.width;
22899         }
22900         // not rendered
22901         if(!this.boxReady){
22902             this.width = w;
22903             this.height = h;
22904             return this;
22905         }
22906
22907         // prevent recalcs when not needed
22908         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22909             return this;
22910         }
22911         this.lastSize = {width: w, height: h};
22912
22913         var adj = this.adjustSize(w, h);
22914         var aw = adj.width, ah = adj.height;
22915         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22916             var rz = this.getResizeEl();
22917             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22918                 rz.setSize(aw, ah);
22919             }else if(!this.deferHeight && ah !== undefined){
22920                 rz.setHeight(ah);
22921             }else if(aw !== undefined){
22922                 rz.setWidth(aw);
22923             }
22924             this.onResize(aw, ah, w, h);
22925             this.fireEvent('resize', this, aw, ah, w, h);
22926         }
22927         return this;
22928     },
22929
22930     /**
22931      * Gets the current size of the component's underlying element.
22932      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22933      */
22934     getSize : function(){
22935         return this.el.getSize();
22936     },
22937
22938     /**
22939      * Gets the current XY position of the component's underlying element.
22940      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22941      * @return {Array} The XY position of the element (e.g., [100, 200])
22942      */
22943     getPosition : function(local){
22944         if(local === true){
22945             return [this.el.getLeft(true), this.el.getTop(true)];
22946         }
22947         return this.xy || this.el.getXY();
22948     },
22949
22950     /**
22951      * Gets the current box measurements of the component's underlying element.
22952      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22953      * @returns {Object} box An object in the format {x, y, width, height}
22954      */
22955     getBox : function(local){
22956         var s = this.el.getSize();
22957         if(local){
22958             s.x = this.el.getLeft(true);
22959             s.y = this.el.getTop(true);
22960         }else{
22961             var xy = this.xy || this.el.getXY();
22962             s.x = xy[0];
22963             s.y = xy[1];
22964         }
22965         return s;
22966     },
22967
22968     /**
22969      * Sets the current box measurements of the component's underlying element.
22970      * @param {Object} box An object in the format {x, y, width, height}
22971      * @returns {Roo.BoxComponent} this
22972      */
22973     updateBox : function(box){
22974         this.setSize(box.width, box.height);
22975         this.setPagePosition(box.x, box.y);
22976         return this;
22977     },
22978
22979     // protected
22980     getResizeEl : function(){
22981         return this.resizeEl || this.el;
22982     },
22983
22984     // protected
22985     getPositionEl : function(){
22986         return this.positionEl || this.el;
22987     },
22988
22989     /**
22990      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22991      * This method fires the move event.
22992      * @param {Number} left The new left
22993      * @param {Number} top The new top
22994      * @returns {Roo.BoxComponent} this
22995      */
22996     setPosition : function(x, y){
22997         this.x = x;
22998         this.y = y;
22999         if(!this.boxReady){
23000             return this;
23001         }
23002         var adj = this.adjustPosition(x, y);
23003         var ax = adj.x, ay = adj.y;
23004
23005         var el = this.getPositionEl();
23006         if(ax !== undefined || ay !== undefined){
23007             if(ax !== undefined && ay !== undefined){
23008                 el.setLeftTop(ax, ay);
23009             }else if(ax !== undefined){
23010                 el.setLeft(ax);
23011             }else if(ay !== undefined){
23012                 el.setTop(ay);
23013             }
23014             this.onPosition(ax, ay);
23015             this.fireEvent('move', this, ax, ay);
23016         }
23017         return this;
23018     },
23019
23020     /**
23021      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23022      * This method fires the move event.
23023      * @param {Number} x The new x position
23024      * @param {Number} y The new y position
23025      * @returns {Roo.BoxComponent} this
23026      */
23027     setPagePosition : function(x, y){
23028         this.pageX = x;
23029         this.pageY = y;
23030         if(!this.boxReady){
23031             return;
23032         }
23033         if(x === undefined || y === undefined){ // cannot translate undefined points
23034             return;
23035         }
23036         var p = this.el.translatePoints(x, y);
23037         this.setPosition(p.left, p.top);
23038         return this;
23039     },
23040
23041     // private
23042     onRender : function(ct, position){
23043         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23044         if(this.resizeEl){
23045             this.resizeEl = Roo.get(this.resizeEl);
23046         }
23047         if(this.positionEl){
23048             this.positionEl = Roo.get(this.positionEl);
23049         }
23050     },
23051
23052     // private
23053     afterRender : function(){
23054         Roo.BoxComponent.superclass.afterRender.call(this);
23055         this.boxReady = true;
23056         this.setSize(this.width, this.height);
23057         if(this.x || this.y){
23058             this.setPosition(this.x, this.y);
23059         }
23060         if(this.pageX || this.pageY){
23061             this.setPagePosition(this.pageX, this.pageY);
23062         }
23063     },
23064
23065     /**
23066      * Force the component's size to recalculate based on the underlying element's current height and width.
23067      * @returns {Roo.BoxComponent} this
23068      */
23069     syncSize : function(){
23070         delete this.lastSize;
23071         this.setSize(this.el.getWidth(), this.el.getHeight());
23072         return this;
23073     },
23074
23075     /**
23076      * Called after the component is resized, this method is empty by default but can be implemented by any
23077      * subclass that needs to perform custom logic after a resize occurs.
23078      * @param {Number} adjWidth The box-adjusted width that was set
23079      * @param {Number} adjHeight The box-adjusted height that was set
23080      * @param {Number} rawWidth The width that was originally specified
23081      * @param {Number} rawHeight The height that was originally specified
23082      */
23083     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23084
23085     },
23086
23087     /**
23088      * Called after the component is moved, this method is empty by default but can be implemented by any
23089      * subclass that needs to perform custom logic after a move occurs.
23090      * @param {Number} x The new x position
23091      * @param {Number} y The new y position
23092      */
23093     onPosition : function(x, y){
23094
23095     },
23096
23097     // private
23098     adjustSize : function(w, h){
23099         if(this.autoWidth){
23100             w = 'auto';
23101         }
23102         if(this.autoHeight){
23103             h = 'auto';
23104         }
23105         return {width : w, height: h};
23106     },
23107
23108     // private
23109     adjustPosition : function(x, y){
23110         return {x : x, y: y};
23111     }
23112 });/*
23113  * Based on:
23114  * Ext JS Library 1.1.1
23115  * Copyright(c) 2006-2007, Ext JS, LLC.
23116  *
23117  * Originally Released Under LGPL - original licence link has changed is not relivant.
23118  *
23119  * Fork - LGPL
23120  * <script type="text/javascript">
23121  */
23122
23123
23124 /**
23125  * @class Roo.SplitBar
23126  * @extends Roo.util.Observable
23127  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23128  * <br><br>
23129  * Usage:
23130  * <pre><code>
23131 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23132                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23133 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23134 split.minSize = 100;
23135 split.maxSize = 600;
23136 split.animate = true;
23137 split.on('moved', splitterMoved);
23138 </code></pre>
23139  * @constructor
23140  * Create a new SplitBar
23141  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23142  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23143  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23144  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23145                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23146                         position of the SplitBar).
23147  */
23148 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23149     
23150     /** @private */
23151     this.el = Roo.get(dragElement, true);
23152     this.el.dom.unselectable = "on";
23153     /** @private */
23154     this.resizingEl = Roo.get(resizingElement, true);
23155
23156     /**
23157      * @private
23158      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23159      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23160      * @type Number
23161      */
23162     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23163     
23164     /**
23165      * The minimum size of the resizing element. (Defaults to 0)
23166      * @type Number
23167      */
23168     this.minSize = 0;
23169     
23170     /**
23171      * The maximum size of the resizing element. (Defaults to 2000)
23172      * @type Number
23173      */
23174     this.maxSize = 2000;
23175     
23176     /**
23177      * Whether to animate the transition to the new size
23178      * @type Boolean
23179      */
23180     this.animate = false;
23181     
23182     /**
23183      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23184      * @type Boolean
23185      */
23186     this.useShim = false;
23187     
23188     /** @private */
23189     this.shim = null;
23190     
23191     if(!existingProxy){
23192         /** @private */
23193         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23194     }else{
23195         this.proxy = Roo.get(existingProxy).dom;
23196     }
23197     /** @private */
23198     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23199     
23200     /** @private */
23201     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23202     
23203     /** @private */
23204     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23205     
23206     /** @private */
23207     this.dragSpecs = {};
23208     
23209     /**
23210      * @private The adapter to use to positon and resize elements
23211      */
23212     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23213     this.adapter.init(this);
23214     
23215     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23216         /** @private */
23217         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23218         this.el.addClass("x-splitbar-h");
23219     }else{
23220         /** @private */
23221         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23222         this.el.addClass("x-splitbar-v");
23223     }
23224     
23225     this.addEvents({
23226         /**
23227          * @event resize
23228          * Fires when the splitter is moved (alias for {@link #event-moved})
23229          * @param {Roo.SplitBar} this
23230          * @param {Number} newSize the new width or height
23231          */
23232         "resize" : true,
23233         /**
23234          * @event moved
23235          * Fires when the splitter is moved
23236          * @param {Roo.SplitBar} this
23237          * @param {Number} newSize the new width or height
23238          */
23239         "moved" : true,
23240         /**
23241          * @event beforeresize
23242          * Fires before the splitter is dragged
23243          * @param {Roo.SplitBar} this
23244          */
23245         "beforeresize" : true,
23246
23247         "beforeapply" : true
23248     });
23249
23250     Roo.util.Observable.call(this);
23251 };
23252
23253 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23254     onStartProxyDrag : function(x, y){
23255         this.fireEvent("beforeresize", this);
23256         if(!this.overlay){
23257             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23258             o.unselectable();
23259             o.enableDisplayMode("block");
23260             // all splitbars share the same overlay
23261             Roo.SplitBar.prototype.overlay = o;
23262         }
23263         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23264         this.overlay.show();
23265         Roo.get(this.proxy).setDisplayed("block");
23266         var size = this.adapter.getElementSize(this);
23267         this.activeMinSize = this.getMinimumSize();;
23268         this.activeMaxSize = this.getMaximumSize();;
23269         var c1 = size - this.activeMinSize;
23270         var c2 = Math.max(this.activeMaxSize - size, 0);
23271         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23272             this.dd.resetConstraints();
23273             this.dd.setXConstraint(
23274                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23275                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23276             );
23277             this.dd.setYConstraint(0, 0);
23278         }else{
23279             this.dd.resetConstraints();
23280             this.dd.setXConstraint(0, 0);
23281             this.dd.setYConstraint(
23282                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23283                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23284             );
23285          }
23286         this.dragSpecs.startSize = size;
23287         this.dragSpecs.startPoint = [x, y];
23288         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23289     },
23290     
23291     /** 
23292      * @private Called after the drag operation by the DDProxy
23293      */
23294     onEndProxyDrag : function(e){
23295         Roo.get(this.proxy).setDisplayed(false);
23296         var endPoint = Roo.lib.Event.getXY(e);
23297         if(this.overlay){
23298             this.overlay.hide();
23299         }
23300         var newSize;
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             newSize = this.dragSpecs.startSize + 
23303                 (this.placement == Roo.SplitBar.LEFT ?
23304                     endPoint[0] - this.dragSpecs.startPoint[0] :
23305                     this.dragSpecs.startPoint[0] - endPoint[0]
23306                 );
23307         }else{
23308             newSize = this.dragSpecs.startSize + 
23309                 (this.placement == Roo.SplitBar.TOP ?
23310                     endPoint[1] - this.dragSpecs.startPoint[1] :
23311                     this.dragSpecs.startPoint[1] - endPoint[1]
23312                 );
23313         }
23314         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23315         if(newSize != this.dragSpecs.startSize){
23316             if(this.fireEvent('beforeapply', this, newSize) !== false){
23317                 this.adapter.setElementSize(this, newSize);
23318                 this.fireEvent("moved", this, newSize);
23319                 this.fireEvent("resize", this, newSize);
23320             }
23321         }
23322     },
23323     
23324     /**
23325      * Get the adapter this SplitBar uses
23326      * @return The adapter object
23327      */
23328     getAdapter : function(){
23329         return this.adapter;
23330     },
23331     
23332     /**
23333      * Set the adapter this SplitBar uses
23334      * @param {Object} adapter A SplitBar adapter object
23335      */
23336     setAdapter : function(adapter){
23337         this.adapter = adapter;
23338         this.adapter.init(this);
23339     },
23340     
23341     /**
23342      * Gets the minimum size for the resizing element
23343      * @return {Number} The minimum size
23344      */
23345     getMinimumSize : function(){
23346         return this.minSize;
23347     },
23348     
23349     /**
23350      * Sets the minimum size for the resizing element
23351      * @param {Number} minSize The minimum size
23352      */
23353     setMinimumSize : function(minSize){
23354         this.minSize = minSize;
23355     },
23356     
23357     /**
23358      * Gets the maximum size for the resizing element
23359      * @return {Number} The maximum size
23360      */
23361     getMaximumSize : function(){
23362         return this.maxSize;
23363     },
23364     
23365     /**
23366      * Sets the maximum size for the resizing element
23367      * @param {Number} maxSize The maximum size
23368      */
23369     setMaximumSize : function(maxSize){
23370         this.maxSize = maxSize;
23371     },
23372     
23373     /**
23374      * Sets the initialize size for the resizing element
23375      * @param {Number} size The initial size
23376      */
23377     setCurrentSize : function(size){
23378         var oldAnimate = this.animate;
23379         this.animate = false;
23380         this.adapter.setElementSize(this, size);
23381         this.animate = oldAnimate;
23382     },
23383     
23384     /**
23385      * Destroy this splitbar. 
23386      * @param {Boolean} removeEl True to remove the element
23387      */
23388     destroy : function(removeEl){
23389         if(this.shim){
23390             this.shim.remove();
23391         }
23392         this.dd.unreg();
23393         this.proxy.parentNode.removeChild(this.proxy);
23394         if(removeEl){
23395             this.el.remove();
23396         }
23397     }
23398 });
23399
23400 /**
23401  * @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.
23402  */
23403 Roo.SplitBar.createProxy = function(dir){
23404     var proxy = new Roo.Element(document.createElement("div"));
23405     proxy.unselectable();
23406     var cls = 'x-splitbar-proxy';
23407     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23408     document.body.appendChild(proxy.dom);
23409     return proxy.dom;
23410 };
23411
23412 /** 
23413  * @class Roo.SplitBar.BasicLayoutAdapter
23414  * Default Adapter. It assumes the splitter and resizing element are not positioned
23415  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23416  */
23417 Roo.SplitBar.BasicLayoutAdapter = function(){
23418 };
23419
23420 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23421     // do nothing for now
23422     init : function(s){
23423     
23424     },
23425     /**
23426      * Called before drag operations to get the current size of the resizing element. 
23427      * @param {Roo.SplitBar} s The SplitBar using this adapter
23428      */
23429      getElementSize : function(s){
23430         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23431             return s.resizingEl.getWidth();
23432         }else{
23433             return s.resizingEl.getHeight();
23434         }
23435     },
23436     
23437     /**
23438      * Called after drag operations to set the size of the resizing element.
23439      * @param {Roo.SplitBar} s The SplitBar using this adapter
23440      * @param {Number} newSize The new size to set
23441      * @param {Function} onComplete A function to be invoked when resizing is complete
23442      */
23443     setElementSize : function(s, newSize, onComplete){
23444         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23445             if(!s.animate){
23446                 s.resizingEl.setWidth(newSize);
23447                 if(onComplete){
23448                     onComplete(s, newSize);
23449                 }
23450             }else{
23451                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23452             }
23453         }else{
23454             
23455             if(!s.animate){
23456                 s.resizingEl.setHeight(newSize);
23457                 if(onComplete){
23458                     onComplete(s, newSize);
23459                 }
23460             }else{
23461                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23462             }
23463         }
23464     }
23465 };
23466
23467 /** 
23468  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23469  * @extends Roo.SplitBar.BasicLayoutAdapter
23470  * Adapter that  moves the splitter element to align with the resized sizing element. 
23471  * Used with an absolute positioned SplitBar.
23472  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23473  * document.body, make sure you assign an id to the body element.
23474  */
23475 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23476     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23477     this.container = Roo.get(container);
23478 };
23479
23480 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23481     init : function(s){
23482         this.basic.init(s);
23483     },
23484     
23485     getElementSize : function(s){
23486         return this.basic.getElementSize(s);
23487     },
23488     
23489     setElementSize : function(s, newSize, onComplete){
23490         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23491     },
23492     
23493     moveSplitter : function(s){
23494         var yes = Roo.SplitBar;
23495         switch(s.placement){
23496             case yes.LEFT:
23497                 s.el.setX(s.resizingEl.getRight());
23498                 break;
23499             case yes.RIGHT:
23500                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23501                 break;
23502             case yes.TOP:
23503                 s.el.setY(s.resizingEl.getBottom());
23504                 break;
23505             case yes.BOTTOM:
23506                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23507                 break;
23508         }
23509     }
23510 };
23511
23512 /**
23513  * Orientation constant - Create a vertical SplitBar
23514  * @static
23515  * @type Number
23516  */
23517 Roo.SplitBar.VERTICAL = 1;
23518
23519 /**
23520  * Orientation constant - Create a horizontal SplitBar
23521  * @static
23522  * @type Number
23523  */
23524 Roo.SplitBar.HORIZONTAL = 2;
23525
23526 /**
23527  * Placement constant - The resizing element is to the left of the splitter element
23528  * @static
23529  * @type Number
23530  */
23531 Roo.SplitBar.LEFT = 1;
23532
23533 /**
23534  * Placement constant - The resizing element is to the right of the splitter element
23535  * @static
23536  * @type Number
23537  */
23538 Roo.SplitBar.RIGHT = 2;
23539
23540 /**
23541  * Placement constant - The resizing element is positioned above the splitter element
23542  * @static
23543  * @type Number
23544  */
23545 Roo.SplitBar.TOP = 3;
23546
23547 /**
23548  * Placement constant - The resizing element is positioned under splitter element
23549  * @static
23550  * @type Number
23551  */
23552 Roo.SplitBar.BOTTOM = 4;
23553 /*
23554  * Based on:
23555  * Ext JS Library 1.1.1
23556  * Copyright(c) 2006-2007, Ext JS, LLC.
23557  *
23558  * Originally Released Under LGPL - original licence link has changed is not relivant.
23559  *
23560  * Fork - LGPL
23561  * <script type="text/javascript">
23562  */
23563
23564 /**
23565  * @class Roo.View
23566  * @extends Roo.util.Observable
23567  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23568  * This class also supports single and multi selection modes. <br>
23569  * Create a data model bound view:
23570  <pre><code>
23571  var store = new Roo.data.Store(...);
23572
23573  var view = new Roo.View({
23574     el : "my-element",
23575     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23576  
23577     singleSelect: true,
23578     selectedClass: "ydataview-selected",
23579     store: store
23580  });
23581
23582  // listen for node click?
23583  view.on("click", function(vw, index, node, e){
23584  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23585  });
23586
23587  // load XML data
23588  dataModel.load("foobar.xml");
23589  </code></pre>
23590  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23591  * <br><br>
23592  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23593  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23594  * 
23595  * Note: old style constructor is still suported (container, template, config)
23596  * 
23597  * @constructor
23598  * Create a new View
23599  * @param {Object} config The config object
23600  * 
23601  */
23602 Roo.View = function(config, depreciated_tpl, depreciated_config){
23603     
23604     if (typeof(depreciated_tpl) == 'undefined') {
23605         // new way.. - universal constructor.
23606         Roo.apply(this, config);
23607         this.el  = Roo.get(this.el);
23608     } else {
23609         // old format..
23610         this.el  = Roo.get(config);
23611         this.tpl = depreciated_tpl;
23612         Roo.apply(this, depreciated_config);
23613     }
23614      
23615     
23616     if(typeof(this.tpl) == "string"){
23617         this.tpl = new Roo.Template(this.tpl);
23618     } else {
23619         // support xtype ctors..
23620         this.tpl = new Roo.factory(this.tpl, Roo);
23621     }
23622     
23623     
23624     this.tpl.compile();
23625    
23626
23627      
23628     /** @private */
23629     this.addEvents({
23630         /**
23631          * @event beforeclick
23632          * Fires before a click is processed. Returns false to cancel the default action.
23633          * @param {Roo.View} this
23634          * @param {Number} index The index of the target node
23635          * @param {HTMLElement} node The target node
23636          * @param {Roo.EventObject} e The raw event object
23637          */
23638             "beforeclick" : true,
23639         /**
23640          * @event click
23641          * Fires when a template node is clicked.
23642          * @param {Roo.View} this
23643          * @param {Number} index The index of the target node
23644          * @param {HTMLElement} node The target node
23645          * @param {Roo.EventObject} e The raw event object
23646          */
23647             "click" : true,
23648         /**
23649          * @event dblclick
23650          * Fires when a template node is double clicked.
23651          * @param {Roo.View} this
23652          * @param {Number} index The index of the target node
23653          * @param {HTMLElement} node The target node
23654          * @param {Roo.EventObject} e The raw event object
23655          */
23656             "dblclick" : true,
23657         /**
23658          * @event contextmenu
23659          * Fires when a template node is right clicked.
23660          * @param {Roo.View} this
23661          * @param {Number} index The index of the target node
23662          * @param {HTMLElement} node The target node
23663          * @param {Roo.EventObject} e The raw event object
23664          */
23665             "contextmenu" : true,
23666         /**
23667          * @event selectionchange
23668          * Fires when the selected nodes change.
23669          * @param {Roo.View} this
23670          * @param {Array} selections Array of the selected nodes
23671          */
23672             "selectionchange" : true,
23673     
23674         /**
23675          * @event beforeselect
23676          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23677          * @param {Roo.View} this
23678          * @param {HTMLElement} node The node to be selected
23679          * @param {Array} selections Array of currently selected nodes
23680          */
23681             "beforeselect" : true,
23682         /**
23683          * @event preparedata
23684          * Fires on every row to render, to allow you to change the data.
23685          * @param {Roo.View} this
23686          * @param {Object} data to be rendered (change this)
23687          */
23688           "preparedata" : true
23689         });
23690
23691     this.el.on({
23692         "click": this.onClick,
23693         "dblclick": this.onDblClick,
23694         "contextmenu": this.onContextMenu,
23695         scope:this
23696     });
23697
23698     this.selections = [];
23699     this.nodes = [];
23700     this.cmp = new Roo.CompositeElementLite([]);
23701     if(this.store){
23702         this.store = Roo.factory(this.store, Roo.data);
23703         this.setStore(this.store, true);
23704     }
23705     Roo.View.superclass.constructor.call(this);
23706 };
23707
23708 Roo.extend(Roo.View, Roo.util.Observable, {
23709     
23710      /**
23711      * @cfg {Roo.data.Store} store Data store to load data from.
23712      */
23713     store : false,
23714     
23715     /**
23716      * @cfg {String|Roo.Element} el The container element.
23717      */
23718     el : '',
23719     
23720     /**
23721      * @cfg {String|Roo.Template} tpl The template used by this View 
23722      */
23723     tpl : false,
23724     
23725     /**
23726      * @cfg {String} selectedClass The css class to add to selected nodes
23727      */
23728     selectedClass : "x-view-selected",
23729      /**
23730      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23731      */
23732     emptyText : "",
23733     /**
23734      * @cfg {Boolean} multiSelect Allow multiple selection
23735      */
23736     multiSelect : false,
23737     /**
23738      * @cfg {Boolean} singleSelect Allow single selection
23739      */
23740     singleSelect:  false,
23741     
23742     /**
23743      * @cfg {Boolean} toggleSelect - selecting 
23744      */
23745     toggleSelect : false,
23746     
23747     /**
23748      * Returns the element this view is bound to.
23749      * @return {Roo.Element}
23750      */
23751     getEl : function(){
23752         return this.el;
23753     },
23754
23755     /**
23756      * Refreshes the view.
23757      */
23758     refresh : function(){
23759         var t = this.tpl;
23760         this.clearSelections();
23761         this.el.update("");
23762         var html = [];
23763         var records = this.store.getRange();
23764         if(records.length < 1){
23765             this.el.update(this.emptyText);
23766             return;
23767         }
23768         for(var i = 0, len = records.length; i < len; i++){
23769             var data = this.prepareData(records[i].data, i, records[i]);
23770             this.fireEvent("preparedata", this, data, i, records[i]);
23771             html[html.length] = t.apply(data);
23772         }
23773         this.el.update(html.join(""));
23774         this.nodes = this.el.dom.childNodes;
23775         this.updateIndexes(0);
23776     },
23777
23778     /**
23779      * Function to override to reformat the data that is sent to
23780      * the template for each node.
23781      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23782      * a JSON object for an UpdateManager bound view).
23783      */
23784     prepareData : function(data){
23785         return data;
23786     },
23787
23788     onUpdate : function(ds, record){
23789         this.clearSelections();
23790         var index = this.store.indexOf(record);
23791         var n = this.nodes[index];
23792         this.tpl.insertBefore(n, this.prepareData(record.data));
23793         n.parentNode.removeChild(n);
23794         this.updateIndexes(index, index);
23795     },
23796
23797     onAdd : function(ds, records, index){
23798         this.clearSelections();
23799         if(this.nodes.length == 0){
23800             this.refresh();
23801             return;
23802         }
23803         var n = this.nodes[index];
23804         for(var i = 0, len = records.length; i < len; i++){
23805             var d = this.prepareData(records[i].data);
23806             if(n){
23807                 this.tpl.insertBefore(n, d);
23808             }else{
23809                 this.tpl.append(this.el, d);
23810             }
23811         }
23812         this.updateIndexes(index);
23813     },
23814
23815     onRemove : function(ds, record, index){
23816         this.clearSelections();
23817         this.el.dom.removeChild(this.nodes[index]);
23818         this.updateIndexes(index);
23819     },
23820
23821     /**
23822      * Refresh an individual node.
23823      * @param {Number} index
23824      */
23825     refreshNode : function(index){
23826         this.onUpdate(this.store, this.store.getAt(index));
23827     },
23828
23829     updateIndexes : function(startIndex, endIndex){
23830         var ns = this.nodes;
23831         startIndex = startIndex || 0;
23832         endIndex = endIndex || ns.length - 1;
23833         for(var i = startIndex; i <= endIndex; i++){
23834             ns[i].nodeIndex = i;
23835         }
23836     },
23837
23838     /**
23839      * Changes the data store this view uses and refresh the view.
23840      * @param {Store} store
23841      */
23842     setStore : function(store, initial){
23843         if(!initial && this.store){
23844             this.store.un("datachanged", this.refresh);
23845             this.store.un("add", this.onAdd);
23846             this.store.un("remove", this.onRemove);
23847             this.store.un("update", this.onUpdate);
23848             this.store.un("clear", this.refresh);
23849         }
23850         if(store){
23851           
23852             store.on("datachanged", this.refresh, this);
23853             store.on("add", this.onAdd, this);
23854             store.on("remove", this.onRemove, this);
23855             store.on("update", this.onUpdate, this);
23856             store.on("clear", this.refresh, this);
23857         }
23858         
23859         if(store){
23860             this.refresh();
23861         }
23862     },
23863
23864     /**
23865      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23866      * @param {HTMLElement} node
23867      * @return {HTMLElement} The template node
23868      */
23869     findItemFromChild : function(node){
23870         var el = this.el.dom;
23871         if(!node || node.parentNode == el){
23872                     return node;
23873             }
23874             var p = node.parentNode;
23875             while(p && p != el){
23876             if(p.parentNode == el){
23877                 return p;
23878             }
23879             p = p.parentNode;
23880         }
23881             return null;
23882     },
23883
23884     /** @ignore */
23885     onClick : function(e){
23886         var item = this.findItemFromChild(e.getTarget());
23887         if(item){
23888             var index = this.indexOf(item);
23889             if(this.onItemClick(item, index, e) !== false){
23890                 this.fireEvent("click", this, index, item, e);
23891             }
23892         }else{
23893             this.clearSelections();
23894         }
23895     },
23896
23897     /** @ignore */
23898     onContextMenu : function(e){
23899         var item = this.findItemFromChild(e.getTarget());
23900         if(item){
23901             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23902         }
23903     },
23904
23905     /** @ignore */
23906     onDblClick : function(e){
23907         var item = this.findItemFromChild(e.getTarget());
23908         if(item){
23909             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23910         }
23911     },
23912
23913     onItemClick : function(item, index, e)
23914     {
23915         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23916             return false;
23917         }
23918         if (this.toggleSelect) {
23919             var m = this.isSelected(item) ? 'unselect' : 'select';
23920             Roo.log(m);
23921             var _t = this;
23922             _t[m](item, true, false);
23923             return true;
23924         }
23925         if(this.multiSelect || this.singleSelect){
23926             if(this.multiSelect && e.shiftKey && this.lastSelection){
23927                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23928             }else{
23929                 this.select(item, this.multiSelect && e.ctrlKey);
23930                 this.lastSelection = item;
23931             }
23932             e.preventDefault();
23933         }
23934         return true;
23935     },
23936
23937     /**
23938      * Get the number of selected nodes.
23939      * @return {Number}
23940      */
23941     getSelectionCount : function(){
23942         return this.selections.length;
23943     },
23944
23945     /**
23946      * Get the currently selected nodes.
23947      * @return {Array} An array of HTMLElements
23948      */
23949     getSelectedNodes : function(){
23950         return this.selections;
23951     },
23952
23953     /**
23954      * Get the indexes of the selected nodes.
23955      * @return {Array}
23956      */
23957     getSelectedIndexes : function(){
23958         var indexes = [], s = this.selections;
23959         for(var i = 0, len = s.length; i < len; i++){
23960             indexes.push(s[i].nodeIndex);
23961         }
23962         return indexes;
23963     },
23964
23965     /**
23966      * Clear all selections
23967      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23968      */
23969     clearSelections : function(suppressEvent){
23970         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23971             this.cmp.elements = this.selections;
23972             this.cmp.removeClass(this.selectedClass);
23973             this.selections = [];
23974             if(!suppressEvent){
23975                 this.fireEvent("selectionchange", this, this.selections);
23976             }
23977         }
23978     },
23979
23980     /**
23981      * Returns true if the passed node is selected
23982      * @param {HTMLElement/Number} node The node or node index
23983      * @return {Boolean}
23984      */
23985     isSelected : function(node){
23986         var s = this.selections;
23987         if(s.length < 1){
23988             return false;
23989         }
23990         node = this.getNode(node);
23991         return s.indexOf(node) !== -1;
23992     },
23993
23994     /**
23995      * Selects nodes.
23996      * @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
23997      * @param {Boolean} keepExisting (optional) true to keep existing selections
23998      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23999      */
24000     select : function(nodeInfo, keepExisting, suppressEvent){
24001         if(nodeInfo instanceof Array){
24002             if(!keepExisting){
24003                 this.clearSelections(true);
24004             }
24005             for(var i = 0, len = nodeInfo.length; i < len; i++){
24006                 this.select(nodeInfo[i], true, true);
24007             }
24008             return;
24009         } 
24010         var node = this.getNode(nodeInfo);
24011         if(!node || this.isSelected(node)){
24012             return; // already selected.
24013         }
24014         if(!keepExisting){
24015             this.clearSelections(true);
24016         }
24017         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24018             Roo.fly(node).addClass(this.selectedClass);
24019             this.selections.push(node);
24020             if(!suppressEvent){
24021                 this.fireEvent("selectionchange", this, this.selections);
24022             }
24023         }
24024         
24025         
24026     },
24027       /**
24028      * Unselects nodes.
24029      * @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
24030      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24031      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24032      */
24033     unselect : function(nodeInfo, keepExisting, suppressEvent)
24034     {
24035         if(nodeInfo instanceof Array){
24036             Roo.each(this.selections, function(s) {
24037                 this.unselect(s, nodeInfo);
24038             }, this);
24039             return;
24040         }
24041         var node = this.getNode(nodeInfo);
24042         if(!node || !this.isSelected(node)){
24043             Roo.log("not selected");
24044             return; // not selected.
24045         }
24046         // fireevent???
24047         var ns = [];
24048         Roo.each(this.selections, function(s) {
24049             if (s == node ) {
24050                 Roo.fly(node).removeClass(this.selectedClass);
24051
24052                 return;
24053             }
24054             ns.push(s);
24055         },this);
24056         
24057         this.selections= ns;
24058         this.fireEvent("selectionchange", this, this.selections);
24059     },
24060
24061     /**
24062      * Gets a template node.
24063      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24064      * @return {HTMLElement} The node or null if it wasn't found
24065      */
24066     getNode : function(nodeInfo){
24067         if(typeof nodeInfo == "string"){
24068             return document.getElementById(nodeInfo);
24069         }else if(typeof nodeInfo == "number"){
24070             return this.nodes[nodeInfo];
24071         }
24072         return nodeInfo;
24073     },
24074
24075     /**
24076      * Gets a range template nodes.
24077      * @param {Number} startIndex
24078      * @param {Number} endIndex
24079      * @return {Array} An array of nodes
24080      */
24081     getNodes : function(start, end){
24082         var ns = this.nodes;
24083         start = start || 0;
24084         end = typeof end == "undefined" ? ns.length - 1 : end;
24085         var nodes = [];
24086         if(start <= end){
24087             for(var i = start; i <= end; i++){
24088                 nodes.push(ns[i]);
24089             }
24090         } else{
24091             for(var i = start; i >= end; i--){
24092                 nodes.push(ns[i]);
24093             }
24094         }
24095         return nodes;
24096     },
24097
24098     /**
24099      * Finds the index of the passed node
24100      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24101      * @return {Number} The index of the node or -1
24102      */
24103     indexOf : function(node){
24104         node = this.getNode(node);
24105         if(typeof node.nodeIndex == "number"){
24106             return node.nodeIndex;
24107         }
24108         var ns = this.nodes;
24109         for(var i = 0, len = ns.length; i < len; i++){
24110             if(ns[i] == node){
24111                 return i;
24112             }
24113         }
24114         return -1;
24115     }
24116 });
24117 /*
24118  * Based on:
24119  * Ext JS Library 1.1.1
24120  * Copyright(c) 2006-2007, Ext JS, LLC.
24121  *
24122  * Originally Released Under LGPL - original licence link has changed is not relivant.
24123  *
24124  * Fork - LGPL
24125  * <script type="text/javascript">
24126  */
24127
24128 /**
24129  * @class Roo.JsonView
24130  * @extends Roo.View
24131  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24132 <pre><code>
24133 var view = new Roo.JsonView({
24134     container: "my-element",
24135     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24136     multiSelect: true, 
24137     jsonRoot: "data" 
24138 });
24139
24140 // listen for node click?
24141 view.on("click", function(vw, index, node, e){
24142     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24143 });
24144
24145 // direct load of JSON data
24146 view.load("foobar.php");
24147
24148 // Example from my blog list
24149 var tpl = new Roo.Template(
24150     '&lt;div class="entry"&gt;' +
24151     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24152     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24153     "&lt;/div&gt;&lt;hr /&gt;"
24154 );
24155
24156 var moreView = new Roo.JsonView({
24157     container :  "entry-list", 
24158     template : tpl,
24159     jsonRoot: "posts"
24160 });
24161 moreView.on("beforerender", this.sortEntries, this);
24162 moreView.load({
24163     url: "/blog/get-posts.php",
24164     params: "allposts=true",
24165     text: "Loading Blog Entries..."
24166 });
24167 </code></pre>
24168
24169 * Note: old code is supported with arguments : (container, template, config)
24170
24171
24172  * @constructor
24173  * Create a new JsonView
24174  * 
24175  * @param {Object} config The config object
24176  * 
24177  */
24178 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24179     
24180     
24181     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24182
24183     var um = this.el.getUpdateManager();
24184     um.setRenderer(this);
24185     um.on("update", this.onLoad, this);
24186     um.on("failure", this.onLoadException, this);
24187
24188     /**
24189      * @event beforerender
24190      * Fires before rendering of the downloaded JSON data.
24191      * @param {Roo.JsonView} this
24192      * @param {Object} data The JSON data loaded
24193      */
24194     /**
24195      * @event load
24196      * Fires when data is loaded.
24197      * @param {Roo.JsonView} this
24198      * @param {Object} data The JSON data loaded
24199      * @param {Object} response The raw Connect response object
24200      */
24201     /**
24202      * @event loadexception
24203      * Fires when loading fails.
24204      * @param {Roo.JsonView} this
24205      * @param {Object} response The raw Connect response object
24206      */
24207     this.addEvents({
24208         'beforerender' : true,
24209         'load' : true,
24210         'loadexception' : true
24211     });
24212 };
24213 Roo.extend(Roo.JsonView, Roo.View, {
24214     /**
24215      * @type {String} The root property in the loaded JSON object that contains the data
24216      */
24217     jsonRoot : "",
24218
24219     /**
24220      * Refreshes the view.
24221      */
24222     refresh : function(){
24223         this.clearSelections();
24224         this.el.update("");
24225         var html = [];
24226         var o = this.jsonData;
24227         if(o && o.length > 0){
24228             for(var i = 0, len = o.length; i < len; i++){
24229                 var data = this.prepareData(o[i], i, o);
24230                 html[html.length] = this.tpl.apply(data);
24231             }
24232         }else{
24233             html.push(this.emptyText);
24234         }
24235         this.el.update(html.join(""));
24236         this.nodes = this.el.dom.childNodes;
24237         this.updateIndexes(0);
24238     },
24239
24240     /**
24241      * 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.
24242      * @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:
24243      <pre><code>
24244      view.load({
24245          url: "your-url.php",
24246          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24247          callback: yourFunction,
24248          scope: yourObject, //(optional scope)
24249          discardUrl: false,
24250          nocache: false,
24251          text: "Loading...",
24252          timeout: 30,
24253          scripts: false
24254      });
24255      </code></pre>
24256      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24257      * 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.
24258      * @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}
24259      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24260      * @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.
24261      */
24262     load : function(){
24263         var um = this.el.getUpdateManager();
24264         um.update.apply(um, arguments);
24265     },
24266
24267     render : function(el, response){
24268         this.clearSelections();
24269         this.el.update("");
24270         var o;
24271         try{
24272             o = Roo.util.JSON.decode(response.responseText);
24273             if(this.jsonRoot){
24274                 
24275                 o = o[this.jsonRoot];
24276             }
24277         } catch(e){
24278         }
24279         /**
24280          * The current JSON data or null
24281          */
24282         this.jsonData = o;
24283         this.beforeRender();
24284         this.refresh();
24285     },
24286
24287 /**
24288  * Get the number of records in the current JSON dataset
24289  * @return {Number}
24290  */
24291     getCount : function(){
24292         return this.jsonData ? this.jsonData.length : 0;
24293     },
24294
24295 /**
24296  * Returns the JSON object for the specified node(s)
24297  * @param {HTMLElement/Array} node The node or an array of nodes
24298  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24299  * you get the JSON object for the node
24300  */
24301     getNodeData : function(node){
24302         if(node instanceof Array){
24303             var data = [];
24304             for(var i = 0, len = node.length; i < len; i++){
24305                 data.push(this.getNodeData(node[i]));
24306             }
24307             return data;
24308         }
24309         return this.jsonData[this.indexOf(node)] || null;
24310     },
24311
24312     beforeRender : function(){
24313         this.snapshot = this.jsonData;
24314         if(this.sortInfo){
24315             this.sort.apply(this, this.sortInfo);
24316         }
24317         this.fireEvent("beforerender", this, this.jsonData);
24318     },
24319
24320     onLoad : function(el, o){
24321         this.fireEvent("load", this, this.jsonData, o);
24322     },
24323
24324     onLoadException : function(el, o){
24325         this.fireEvent("loadexception", this, o);
24326     },
24327
24328 /**
24329  * Filter the data by a specific property.
24330  * @param {String} property A property on your JSON objects
24331  * @param {String/RegExp} value Either string that the property values
24332  * should start with, or a RegExp to test against the property
24333  */
24334     filter : function(property, value){
24335         if(this.jsonData){
24336             var data = [];
24337             var ss = this.snapshot;
24338             if(typeof value == "string"){
24339                 var vlen = value.length;
24340                 if(vlen == 0){
24341                     this.clearFilter();
24342                     return;
24343                 }
24344                 value = value.toLowerCase();
24345                 for(var i = 0, len = ss.length; i < len; i++){
24346                     var o = ss[i];
24347                     if(o[property].substr(0, vlen).toLowerCase() == value){
24348                         data.push(o);
24349                     }
24350                 }
24351             } else if(value.exec){ // regex?
24352                 for(var i = 0, len = ss.length; i < len; i++){
24353                     var o = ss[i];
24354                     if(value.test(o[property])){
24355                         data.push(o);
24356                     }
24357                 }
24358             } else{
24359                 return;
24360             }
24361             this.jsonData = data;
24362             this.refresh();
24363         }
24364     },
24365
24366 /**
24367  * Filter by a function. The passed function will be called with each
24368  * object in the current dataset. If the function returns true the value is kept,
24369  * otherwise it is filtered.
24370  * @param {Function} fn
24371  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24372  */
24373     filterBy : function(fn, scope){
24374         if(this.jsonData){
24375             var data = [];
24376             var ss = this.snapshot;
24377             for(var i = 0, len = ss.length; i < len; i++){
24378                 var o = ss[i];
24379                 if(fn.call(scope || this, o)){
24380                     data.push(o);
24381                 }
24382             }
24383             this.jsonData = data;
24384             this.refresh();
24385         }
24386     },
24387
24388 /**
24389  * Clears the current filter.
24390  */
24391     clearFilter : function(){
24392         if(this.snapshot && this.jsonData != this.snapshot){
24393             this.jsonData = this.snapshot;
24394             this.refresh();
24395         }
24396     },
24397
24398
24399 /**
24400  * Sorts the data for this view and refreshes it.
24401  * @param {String} property A property on your JSON objects to sort on
24402  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24403  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24404  */
24405     sort : function(property, dir, sortType){
24406         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24407         if(this.jsonData){
24408             var p = property;
24409             var dsc = dir && dir.toLowerCase() == "desc";
24410             var f = function(o1, o2){
24411                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24412                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24413                 ;
24414                 if(v1 < v2){
24415                     return dsc ? +1 : -1;
24416                 } else if(v1 > v2){
24417                     return dsc ? -1 : +1;
24418                 } else{
24419                     return 0;
24420                 }
24421             };
24422             this.jsonData.sort(f);
24423             this.refresh();
24424             if(this.jsonData != this.snapshot){
24425                 this.snapshot.sort(f);
24426             }
24427         }
24428     }
24429 });/*
24430  * Based on:
24431  * Ext JS Library 1.1.1
24432  * Copyright(c) 2006-2007, Ext JS, LLC.
24433  *
24434  * Originally Released Under LGPL - original licence link has changed is not relivant.
24435  *
24436  * Fork - LGPL
24437  * <script type="text/javascript">
24438  */
24439  
24440
24441 /**
24442  * @class Roo.ColorPalette
24443  * @extends Roo.Component
24444  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24445  * Here's an example of typical usage:
24446  * <pre><code>
24447 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24448 cp.render('my-div');
24449
24450 cp.on('select', function(palette, selColor){
24451     // do something with selColor
24452 });
24453 </code></pre>
24454  * @constructor
24455  * Create a new ColorPalette
24456  * @param {Object} config The config object
24457  */
24458 Roo.ColorPalette = function(config){
24459     Roo.ColorPalette.superclass.constructor.call(this, config);
24460     this.addEvents({
24461         /**
24462              * @event select
24463              * Fires when a color is selected
24464              * @param {ColorPalette} this
24465              * @param {String} color The 6-digit color hex code (without the # symbol)
24466              */
24467         select: true
24468     });
24469
24470     if(this.handler){
24471         this.on("select", this.handler, this.scope, true);
24472     }
24473 };
24474 Roo.extend(Roo.ColorPalette, Roo.Component, {
24475     /**
24476      * @cfg {String} itemCls
24477      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24478      */
24479     itemCls : "x-color-palette",
24480     /**
24481      * @cfg {String} value
24482      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24483      * the hex codes are case-sensitive.
24484      */
24485     value : null,
24486     clickEvent:'click',
24487     // private
24488     ctype: "Roo.ColorPalette",
24489
24490     /**
24491      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24492      */
24493     allowReselect : false,
24494
24495     /**
24496      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24497      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24498      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24499      * of colors with the width setting until the box is symmetrical.</p>
24500      * <p>You can override individual colors if needed:</p>
24501      * <pre><code>
24502 var cp = new Roo.ColorPalette();
24503 cp.colors[0] = "FF0000";  // change the first box to red
24504 </code></pre>
24505
24506 Or you can provide a custom array of your own for complete control:
24507 <pre><code>
24508 var cp = new Roo.ColorPalette();
24509 cp.colors = ["000000", "993300", "333300"];
24510 </code></pre>
24511      * @type Array
24512      */
24513     colors : [
24514         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24515         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24516         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24517         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24518         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24519     ],
24520
24521     // private
24522     onRender : function(container, position){
24523         var t = new Roo.MasterTemplate(
24524             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24525         );
24526         var c = this.colors;
24527         for(var i = 0, len = c.length; i < len; i++){
24528             t.add([c[i]]);
24529         }
24530         var el = document.createElement("div");
24531         el.className = this.itemCls;
24532         t.overwrite(el);
24533         container.dom.insertBefore(el, position);
24534         this.el = Roo.get(el);
24535         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24536         if(this.clickEvent != 'click'){
24537             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24538         }
24539     },
24540
24541     // private
24542     afterRender : function(){
24543         Roo.ColorPalette.superclass.afterRender.call(this);
24544         if(this.value){
24545             var s = this.value;
24546             this.value = null;
24547             this.select(s);
24548         }
24549     },
24550
24551     // private
24552     handleClick : function(e, t){
24553         e.preventDefault();
24554         if(!this.disabled){
24555             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24556             this.select(c.toUpperCase());
24557         }
24558     },
24559
24560     /**
24561      * Selects the specified color in the palette (fires the select event)
24562      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24563      */
24564     select : function(color){
24565         color = color.replace("#", "");
24566         if(color != this.value || this.allowReselect){
24567             var el = this.el;
24568             if(this.value){
24569                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24570             }
24571             el.child("a.color-"+color).addClass("x-color-palette-sel");
24572             this.value = color;
24573             this.fireEvent("select", this, color);
24574         }
24575     }
24576 });/*
24577  * Based on:
24578  * Ext JS Library 1.1.1
24579  * Copyright(c) 2006-2007, Ext JS, LLC.
24580  *
24581  * Originally Released Under LGPL - original licence link has changed is not relivant.
24582  *
24583  * Fork - LGPL
24584  * <script type="text/javascript">
24585  */
24586  
24587 /**
24588  * @class Roo.DatePicker
24589  * @extends Roo.Component
24590  * Simple date picker class.
24591  * @constructor
24592  * Create a new DatePicker
24593  * @param {Object} config The config object
24594  */
24595 Roo.DatePicker = function(config){
24596     Roo.DatePicker.superclass.constructor.call(this, config);
24597
24598     this.value = config && config.value ?
24599                  config.value.clearTime() : new Date().clearTime();
24600
24601     this.addEvents({
24602         /**
24603              * @event select
24604              * Fires when a date is selected
24605              * @param {DatePicker} this
24606              * @param {Date} date The selected date
24607              */
24608         'select': true,
24609         /**
24610              * @event monthchange
24611              * Fires when the displayed month changes 
24612              * @param {DatePicker} this
24613              * @param {Date} date The selected month
24614              */
24615         'monthchange': true
24616     });
24617
24618     if(this.handler){
24619         this.on("select", this.handler,  this.scope || this);
24620     }
24621     // build the disabledDatesRE
24622     if(!this.disabledDatesRE && this.disabledDates){
24623         var dd = this.disabledDates;
24624         var re = "(?:";
24625         for(var i = 0; i < dd.length; i++){
24626             re += dd[i];
24627             if(i != dd.length-1) re += "|";
24628         }
24629         this.disabledDatesRE = new RegExp(re + ")");
24630     }
24631 };
24632
24633 Roo.extend(Roo.DatePicker, Roo.Component, {
24634     /**
24635      * @cfg {String} todayText
24636      * The text to display on the button that selects the current date (defaults to "Today")
24637      */
24638     todayText : "Today",
24639     /**
24640      * @cfg {String} okText
24641      * The text to display on the ok button
24642      */
24643     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24644     /**
24645      * @cfg {String} cancelText
24646      * The text to display on the cancel button
24647      */
24648     cancelText : "Cancel",
24649     /**
24650      * @cfg {String} todayTip
24651      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24652      */
24653     todayTip : "{0} (Spacebar)",
24654     /**
24655      * @cfg {Date} minDate
24656      * Minimum allowable date (JavaScript date object, defaults to null)
24657      */
24658     minDate : null,
24659     /**
24660      * @cfg {Date} maxDate
24661      * Maximum allowable date (JavaScript date object, defaults to null)
24662      */
24663     maxDate : null,
24664     /**
24665      * @cfg {String} minText
24666      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24667      */
24668     minText : "This date is before the minimum date",
24669     /**
24670      * @cfg {String} maxText
24671      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24672      */
24673     maxText : "This date is after the maximum date",
24674     /**
24675      * @cfg {String} format
24676      * The default date format string which can be overriden for localization support.  The format must be
24677      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24678      */
24679     format : "m/d/y",
24680     /**
24681      * @cfg {Array} disabledDays
24682      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24683      */
24684     disabledDays : null,
24685     /**
24686      * @cfg {String} disabledDaysText
24687      * The tooltip to display when the date falls on a disabled day (defaults to "")
24688      */
24689     disabledDaysText : "",
24690     /**
24691      * @cfg {RegExp} disabledDatesRE
24692      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24693      */
24694     disabledDatesRE : null,
24695     /**
24696      * @cfg {String} disabledDatesText
24697      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24698      */
24699     disabledDatesText : "",
24700     /**
24701      * @cfg {Boolean} constrainToViewport
24702      * True to constrain the date picker to the viewport (defaults to true)
24703      */
24704     constrainToViewport : true,
24705     /**
24706      * @cfg {Array} monthNames
24707      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24708      */
24709     monthNames : Date.monthNames,
24710     /**
24711      * @cfg {Array} dayNames
24712      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24713      */
24714     dayNames : Date.dayNames,
24715     /**
24716      * @cfg {String} nextText
24717      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24718      */
24719     nextText: 'Next Month (Control+Right)',
24720     /**
24721      * @cfg {String} prevText
24722      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24723      */
24724     prevText: 'Previous Month (Control+Left)',
24725     /**
24726      * @cfg {String} monthYearText
24727      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24728      */
24729     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24730     /**
24731      * @cfg {Number} startDay
24732      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24733      */
24734     startDay : 0,
24735     /**
24736      * @cfg {Bool} showClear
24737      * Show a clear button (usefull for date form elements that can be blank.)
24738      */
24739     
24740     showClear: false,
24741     
24742     /**
24743      * Sets the value of the date field
24744      * @param {Date} value The date to set
24745      */
24746     setValue : function(value){
24747         var old = this.value;
24748         this.value = value.clearTime(true);
24749         if(this.el){
24750             this.update(this.value);
24751         }
24752     },
24753
24754     /**
24755      * Gets the current selected value of the date field
24756      * @return {Date} The selected date
24757      */
24758     getValue : function(){
24759         return this.value;
24760     },
24761
24762     // private
24763     focus : function(){
24764         if(this.el){
24765             this.update(this.activeDate);
24766         }
24767     },
24768
24769     // private
24770     onRender : function(container, position){
24771         var m = [
24772              '<table cellspacing="0">',
24773                 '<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>',
24774                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24775         var dn = this.dayNames;
24776         for(var i = 0; i < 7; i++){
24777             var d = this.startDay+i;
24778             if(d > 6){
24779                 d = d-7;
24780             }
24781             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24782         }
24783         m[m.length] = "</tr></thead><tbody><tr>";
24784         for(var i = 0; i < 42; i++) {
24785             if(i % 7 == 0 && i != 0){
24786                 m[m.length] = "</tr><tr>";
24787             }
24788             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24789         }
24790         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24791             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24792
24793         var el = document.createElement("div");
24794         el.className = "x-date-picker";
24795         el.innerHTML = m.join("");
24796
24797         container.dom.insertBefore(el, position);
24798
24799         this.el = Roo.get(el);
24800         this.eventEl = Roo.get(el.firstChild);
24801
24802         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24803             handler: this.showPrevMonth,
24804             scope: this,
24805             preventDefault:true,
24806             stopDefault:true
24807         });
24808
24809         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24810             handler: this.showNextMonth,
24811             scope: this,
24812             preventDefault:true,
24813             stopDefault:true
24814         });
24815
24816         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24817
24818         this.monthPicker = this.el.down('div.x-date-mp');
24819         this.monthPicker.enableDisplayMode('block');
24820         
24821         var kn = new Roo.KeyNav(this.eventEl, {
24822             "left" : function(e){
24823                 e.ctrlKey ?
24824                     this.showPrevMonth() :
24825                     this.update(this.activeDate.add("d", -1));
24826             },
24827
24828             "right" : function(e){
24829                 e.ctrlKey ?
24830                     this.showNextMonth() :
24831                     this.update(this.activeDate.add("d", 1));
24832             },
24833
24834             "up" : function(e){
24835                 e.ctrlKey ?
24836                     this.showNextYear() :
24837                     this.update(this.activeDate.add("d", -7));
24838             },
24839
24840             "down" : function(e){
24841                 e.ctrlKey ?
24842                     this.showPrevYear() :
24843                     this.update(this.activeDate.add("d", 7));
24844             },
24845
24846             "pageUp" : function(e){
24847                 this.showNextMonth();
24848             },
24849
24850             "pageDown" : function(e){
24851                 this.showPrevMonth();
24852             },
24853
24854             "enter" : function(e){
24855                 e.stopPropagation();
24856                 return true;
24857             },
24858
24859             scope : this
24860         });
24861
24862         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24863
24864         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24865
24866         this.el.unselectable();
24867         
24868         this.cells = this.el.select("table.x-date-inner tbody td");
24869         this.textNodes = this.el.query("table.x-date-inner tbody span");
24870
24871         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24872             text: "&#160;",
24873             tooltip: this.monthYearText
24874         });
24875
24876         this.mbtn.on('click', this.showMonthPicker, this);
24877         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24878
24879
24880         var today = (new Date()).dateFormat(this.format);
24881         
24882         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24883         if (this.showClear) {
24884             baseTb.add( new Roo.Toolbar.Fill());
24885         }
24886         baseTb.add({
24887             text: String.format(this.todayText, today),
24888             tooltip: String.format(this.todayTip, today),
24889             handler: this.selectToday,
24890             scope: this
24891         });
24892         
24893         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24894             
24895         //});
24896         if (this.showClear) {
24897             
24898             baseTb.add( new Roo.Toolbar.Fill());
24899             baseTb.add({
24900                 text: '&#160;',
24901                 cls: 'x-btn-icon x-btn-clear',
24902                 handler: function() {
24903                     //this.value = '';
24904                     this.fireEvent("select", this, '');
24905                 },
24906                 scope: this
24907             });
24908         }
24909         
24910         
24911         if(Roo.isIE){
24912             this.el.repaint();
24913         }
24914         this.update(this.value);
24915     },
24916
24917     createMonthPicker : function(){
24918         if(!this.monthPicker.dom.firstChild){
24919             var buf = ['<table border="0" cellspacing="0">'];
24920             for(var i = 0; i < 6; i++){
24921                 buf.push(
24922                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24923                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24924                     i == 0 ?
24925                     '<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>' :
24926                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24927                 );
24928             }
24929             buf.push(
24930                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24931                     this.okText,
24932                     '</button><button type="button" class="x-date-mp-cancel">',
24933                     this.cancelText,
24934                     '</button></td></tr>',
24935                 '</table>'
24936             );
24937             this.monthPicker.update(buf.join(''));
24938             this.monthPicker.on('click', this.onMonthClick, this);
24939             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24940
24941             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24942             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24943
24944             this.mpMonths.each(function(m, a, i){
24945                 i += 1;
24946                 if((i%2) == 0){
24947                     m.dom.xmonth = 5 + Math.round(i * .5);
24948                 }else{
24949                     m.dom.xmonth = Math.round((i-1) * .5);
24950                 }
24951             });
24952         }
24953     },
24954
24955     showMonthPicker : function(){
24956         this.createMonthPicker();
24957         var size = this.el.getSize();
24958         this.monthPicker.setSize(size);
24959         this.monthPicker.child('table').setSize(size);
24960
24961         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24962         this.updateMPMonth(this.mpSelMonth);
24963         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24964         this.updateMPYear(this.mpSelYear);
24965
24966         this.monthPicker.slideIn('t', {duration:.2});
24967     },
24968
24969     updateMPYear : function(y){
24970         this.mpyear = y;
24971         var ys = this.mpYears.elements;
24972         for(var i = 1; i <= 10; i++){
24973             var td = ys[i-1], y2;
24974             if((i%2) == 0){
24975                 y2 = y + Math.round(i * .5);
24976                 td.firstChild.innerHTML = y2;
24977                 td.xyear = y2;
24978             }else{
24979                 y2 = y - (5-Math.round(i * .5));
24980                 td.firstChild.innerHTML = y2;
24981                 td.xyear = y2;
24982             }
24983             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24984         }
24985     },
24986
24987     updateMPMonth : function(sm){
24988         this.mpMonths.each(function(m, a, i){
24989             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24990         });
24991     },
24992
24993     selectMPMonth: function(m){
24994         
24995     },
24996
24997     onMonthClick : function(e, t){
24998         e.stopEvent();
24999         var el = new Roo.Element(t), pn;
25000         if(el.is('button.x-date-mp-cancel')){
25001             this.hideMonthPicker();
25002         }
25003         else if(el.is('button.x-date-mp-ok')){
25004             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25005             this.hideMonthPicker();
25006         }
25007         else if(pn = el.up('td.x-date-mp-month', 2)){
25008             this.mpMonths.removeClass('x-date-mp-sel');
25009             pn.addClass('x-date-mp-sel');
25010             this.mpSelMonth = pn.dom.xmonth;
25011         }
25012         else if(pn = el.up('td.x-date-mp-year', 2)){
25013             this.mpYears.removeClass('x-date-mp-sel');
25014             pn.addClass('x-date-mp-sel');
25015             this.mpSelYear = pn.dom.xyear;
25016         }
25017         else if(el.is('a.x-date-mp-prev')){
25018             this.updateMPYear(this.mpyear-10);
25019         }
25020         else if(el.is('a.x-date-mp-next')){
25021             this.updateMPYear(this.mpyear+10);
25022         }
25023     },
25024
25025     onMonthDblClick : function(e, t){
25026         e.stopEvent();
25027         var el = new Roo.Element(t), pn;
25028         if(pn = el.up('td.x-date-mp-month', 2)){
25029             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25030             this.hideMonthPicker();
25031         }
25032         else if(pn = el.up('td.x-date-mp-year', 2)){
25033             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25034             this.hideMonthPicker();
25035         }
25036     },
25037
25038     hideMonthPicker : function(disableAnim){
25039         if(this.monthPicker){
25040             if(disableAnim === true){
25041                 this.monthPicker.hide();
25042             }else{
25043                 this.monthPicker.slideOut('t', {duration:.2});
25044             }
25045         }
25046     },
25047
25048     // private
25049     showPrevMonth : function(e){
25050         this.update(this.activeDate.add("mo", -1));
25051     },
25052
25053     // private
25054     showNextMonth : function(e){
25055         this.update(this.activeDate.add("mo", 1));
25056     },
25057
25058     // private
25059     showPrevYear : function(){
25060         this.update(this.activeDate.add("y", -1));
25061     },
25062
25063     // private
25064     showNextYear : function(){
25065         this.update(this.activeDate.add("y", 1));
25066     },
25067
25068     // private
25069     handleMouseWheel : function(e){
25070         var delta = e.getWheelDelta();
25071         if(delta > 0){
25072             this.showPrevMonth();
25073             e.stopEvent();
25074         } else if(delta < 0){
25075             this.showNextMonth();
25076             e.stopEvent();
25077         }
25078     },
25079
25080     // private
25081     handleDateClick : function(e, t){
25082         e.stopEvent();
25083         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25084             this.setValue(new Date(t.dateValue));
25085             this.fireEvent("select", this, this.value);
25086         }
25087     },
25088
25089     // private
25090     selectToday : function(){
25091         this.setValue(new Date().clearTime());
25092         this.fireEvent("select", this, this.value);
25093     },
25094
25095     // private
25096     update : function(date)
25097     {
25098         var vd = this.activeDate;
25099         this.activeDate = date;
25100         if(vd && this.el){
25101             var t = date.getTime();
25102             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25103                 this.cells.removeClass("x-date-selected");
25104                 this.cells.each(function(c){
25105                    if(c.dom.firstChild.dateValue == t){
25106                        c.addClass("x-date-selected");
25107                        setTimeout(function(){
25108                             try{c.dom.firstChild.focus();}catch(e){}
25109                        }, 50);
25110                        return false;
25111                    }
25112                 });
25113                 return;
25114             }
25115         }
25116         
25117         var days = date.getDaysInMonth();
25118         var firstOfMonth = date.getFirstDateOfMonth();
25119         var startingPos = firstOfMonth.getDay()-this.startDay;
25120
25121         if(startingPos <= this.startDay){
25122             startingPos += 7;
25123         }
25124
25125         var pm = date.add("mo", -1);
25126         var prevStart = pm.getDaysInMonth()-startingPos;
25127
25128         var cells = this.cells.elements;
25129         var textEls = this.textNodes;
25130         days += startingPos;
25131
25132         // convert everything to numbers so it's fast
25133         var day = 86400000;
25134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25135         var today = new Date().clearTime().getTime();
25136         var sel = date.clearTime().getTime();
25137         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25138         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25139         var ddMatch = this.disabledDatesRE;
25140         var ddText = this.disabledDatesText;
25141         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25142         var ddaysText = this.disabledDaysText;
25143         var format = this.format;
25144
25145         var setCellClass = function(cal, cell){
25146             cell.title = "";
25147             var t = d.getTime();
25148             cell.firstChild.dateValue = t;
25149             if(t == today){
25150                 cell.className += " x-date-today";
25151                 cell.title = cal.todayText;
25152             }
25153             if(t == sel){
25154                 cell.className += " x-date-selected";
25155                 setTimeout(function(){
25156                     try{cell.firstChild.focus();}catch(e){}
25157                 }, 50);
25158             }
25159             // disabling
25160             if(t < min) {
25161                 cell.className = " x-date-disabled";
25162                 cell.title = cal.minText;
25163                 return;
25164             }
25165             if(t > max) {
25166                 cell.className = " x-date-disabled";
25167                 cell.title = cal.maxText;
25168                 return;
25169             }
25170             if(ddays){
25171                 if(ddays.indexOf(d.getDay()) != -1){
25172                     cell.title = ddaysText;
25173                     cell.className = " x-date-disabled";
25174                 }
25175             }
25176             if(ddMatch && format){
25177                 var fvalue = d.dateFormat(format);
25178                 if(ddMatch.test(fvalue)){
25179                     cell.title = ddText.replace("%0", fvalue);
25180                     cell.className = " x-date-disabled";
25181                 }
25182             }
25183         };
25184
25185         var i = 0;
25186         for(; i < startingPos; i++) {
25187             textEls[i].innerHTML = (++prevStart);
25188             d.setDate(d.getDate()+1);
25189             cells[i].className = "x-date-prevday";
25190             setCellClass(this, cells[i]);
25191         }
25192         for(; i < days; i++){
25193             intDay = i - startingPos + 1;
25194             textEls[i].innerHTML = (intDay);
25195             d.setDate(d.getDate()+1);
25196             cells[i].className = "x-date-active";
25197             setCellClass(this, cells[i]);
25198         }
25199         var extraDays = 0;
25200         for(; i < 42; i++) {
25201              textEls[i].innerHTML = (++extraDays);
25202              d.setDate(d.getDate()+1);
25203              cells[i].className = "x-date-nextday";
25204              setCellClass(this, cells[i]);
25205         }
25206
25207         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25208         this.fireEvent('monthchange', this, date);
25209         
25210         if(!this.internalRender){
25211             var main = this.el.dom.firstChild;
25212             var w = main.offsetWidth;
25213             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25214             Roo.fly(main).setWidth(w);
25215             this.internalRender = true;
25216             // opera does not respect the auto grow header center column
25217             // then, after it gets a width opera refuses to recalculate
25218             // without a second pass
25219             if(Roo.isOpera && !this.secondPass){
25220                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25221                 this.secondPass = true;
25222                 this.update.defer(10, this, [date]);
25223             }
25224         }
25225         
25226         
25227     }
25228 });        /*
25229  * Based on:
25230  * Ext JS Library 1.1.1
25231  * Copyright(c) 2006-2007, Ext JS, LLC.
25232  *
25233  * Originally Released Under LGPL - original licence link has changed is not relivant.
25234  *
25235  * Fork - LGPL
25236  * <script type="text/javascript">
25237  */
25238 /**
25239  * @class Roo.TabPanel
25240  * @extends Roo.util.Observable
25241  * A lightweight tab container.
25242  * <br><br>
25243  * Usage:
25244  * <pre><code>
25245 // basic tabs 1, built from existing content
25246 var tabs = new Roo.TabPanel("tabs1");
25247 tabs.addTab("script", "View Script");
25248 tabs.addTab("markup", "View Markup");
25249 tabs.activate("script");
25250
25251 // more advanced tabs, built from javascript
25252 var jtabs = new Roo.TabPanel("jtabs");
25253 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25254
25255 // set up the UpdateManager
25256 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25257 var updater = tab2.getUpdateManager();
25258 updater.setDefaultUrl("ajax1.htm");
25259 tab2.on('activate', updater.refresh, updater, true);
25260
25261 // Use setUrl for Ajax loading
25262 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25263 tab3.setUrl("ajax2.htm", null, true);
25264
25265 // Disabled tab
25266 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25267 tab4.disable();
25268
25269 jtabs.activate("jtabs-1");
25270  * </code></pre>
25271  * @constructor
25272  * Create a new TabPanel.
25273  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25274  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25275  */
25276 Roo.TabPanel = function(container, config){
25277     /**
25278     * The container element for this TabPanel.
25279     * @type Roo.Element
25280     */
25281     this.el = Roo.get(container, true);
25282     if(config){
25283         if(typeof config == "boolean"){
25284             this.tabPosition = config ? "bottom" : "top";
25285         }else{
25286             Roo.apply(this, config);
25287         }
25288     }
25289     if(this.tabPosition == "bottom"){
25290         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25291         this.el.addClass("x-tabs-bottom");
25292     }
25293     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25294     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25295     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25296     if(Roo.isIE){
25297         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25298     }
25299     if(this.tabPosition != "bottom"){
25300         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25301          * @type Roo.Element
25302          */
25303         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25304         this.el.addClass("x-tabs-top");
25305     }
25306     this.items = [];
25307
25308     this.bodyEl.setStyle("position", "relative");
25309
25310     this.active = null;
25311     this.activateDelegate = this.activate.createDelegate(this);
25312
25313     this.addEvents({
25314         /**
25315          * @event tabchange
25316          * Fires when the active tab changes
25317          * @param {Roo.TabPanel} this
25318          * @param {Roo.TabPanelItem} activePanel The new active tab
25319          */
25320         "tabchange": true,
25321         /**
25322          * @event beforetabchange
25323          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25324          * @param {Roo.TabPanel} this
25325          * @param {Object} e Set cancel to true on this object to cancel the tab change
25326          * @param {Roo.TabPanelItem} tab The tab being changed to
25327          */
25328         "beforetabchange" : true
25329     });
25330
25331     Roo.EventManager.onWindowResize(this.onResize, this);
25332     this.cpad = this.el.getPadding("lr");
25333     this.hiddenCount = 0;
25334
25335
25336     // toolbar on the tabbar support...
25337     if (this.toolbar) {
25338         var tcfg = this.toolbar;
25339         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25340         this.toolbar = new Roo.Toolbar(tcfg);
25341         if (Roo.isSafari) {
25342             var tbl = tcfg.container.child('table', true);
25343             tbl.setAttribute('width', '100%');
25344         }
25345         
25346     }
25347    
25348
25349
25350     Roo.TabPanel.superclass.constructor.call(this);
25351 };
25352
25353 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25354     /*
25355      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25356      */
25357     tabPosition : "top",
25358     /*
25359      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25360      */
25361     currentTabWidth : 0,
25362     /*
25363      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25364      */
25365     minTabWidth : 40,
25366     /*
25367      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25368      */
25369     maxTabWidth : 250,
25370     /*
25371      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25372      */
25373     preferredTabWidth : 175,
25374     /*
25375      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25376      */
25377     resizeTabs : false,
25378     /*
25379      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25380      */
25381     monitorResize : true,
25382     /*
25383      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25384      */
25385     toolbar : false,
25386
25387     /**
25388      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25389      * @param {String} id The id of the div to use <b>or create</b>
25390      * @param {String} text The text for the tab
25391      * @param {String} content (optional) Content to put in the TabPanelItem body
25392      * @param {Boolean} closable (optional) True to create a close icon on the tab
25393      * @return {Roo.TabPanelItem} The created TabPanelItem
25394      */
25395     addTab : function(id, text, content, closable){
25396         var item = new Roo.TabPanelItem(this, id, text, closable);
25397         this.addTabItem(item);
25398         if(content){
25399             item.setContent(content);
25400         }
25401         return item;
25402     },
25403
25404     /**
25405      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25406      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25407      * @return {Roo.TabPanelItem}
25408      */
25409     getTab : function(id){
25410         return this.items[id];
25411     },
25412
25413     /**
25414      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25415      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25416      */
25417     hideTab : function(id){
25418         var t = this.items[id];
25419         if(!t.isHidden()){
25420            t.setHidden(true);
25421            this.hiddenCount++;
25422            this.autoSizeTabs();
25423         }
25424     },
25425
25426     /**
25427      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25428      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25429      */
25430     unhideTab : function(id){
25431         var t = this.items[id];
25432         if(t.isHidden()){
25433            t.setHidden(false);
25434            this.hiddenCount--;
25435            this.autoSizeTabs();
25436         }
25437     },
25438
25439     /**
25440      * Adds an existing {@link Roo.TabPanelItem}.
25441      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25442      */
25443     addTabItem : function(item){
25444         this.items[item.id] = item;
25445         this.items.push(item);
25446         if(this.resizeTabs){
25447            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25448            this.autoSizeTabs();
25449         }else{
25450             item.autoSize();
25451         }
25452     },
25453
25454     /**
25455      * Removes a {@link Roo.TabPanelItem}.
25456      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25457      */
25458     removeTab : function(id){
25459         var items = this.items;
25460         var tab = items[id];
25461         if(!tab) { return; }
25462         var index = items.indexOf(tab);
25463         if(this.active == tab && items.length > 1){
25464             var newTab = this.getNextAvailable(index);
25465             if(newTab) {
25466                 newTab.activate();
25467             }
25468         }
25469         this.stripEl.dom.removeChild(tab.pnode.dom);
25470         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25471             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25472         }
25473         items.splice(index, 1);
25474         delete this.items[tab.id];
25475         tab.fireEvent("close", tab);
25476         tab.purgeListeners();
25477         this.autoSizeTabs();
25478     },
25479
25480     getNextAvailable : function(start){
25481         var items = this.items;
25482         var index = start;
25483         // look for a next tab that will slide over to
25484         // replace the one being removed
25485         while(index < items.length){
25486             var item = items[++index];
25487             if(item && !item.isHidden()){
25488                 return item;
25489             }
25490         }
25491         // if one isn't found select the previous tab (on the left)
25492         index = start;
25493         while(index >= 0){
25494             var item = items[--index];
25495             if(item && !item.isHidden()){
25496                 return item;
25497             }
25498         }
25499         return null;
25500     },
25501
25502     /**
25503      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25504      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25505      */
25506     disableTab : function(id){
25507         var tab = this.items[id];
25508         if(tab && this.active != tab){
25509             tab.disable();
25510         }
25511     },
25512
25513     /**
25514      * Enables a {@link Roo.TabPanelItem} that is disabled.
25515      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25516      */
25517     enableTab : function(id){
25518         var tab = this.items[id];
25519         tab.enable();
25520     },
25521
25522     /**
25523      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25524      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25525      * @return {Roo.TabPanelItem} The TabPanelItem.
25526      */
25527     activate : function(id){
25528         var tab = this.items[id];
25529         if(!tab){
25530             return null;
25531         }
25532         if(tab == this.active || tab.disabled){
25533             return tab;
25534         }
25535         var e = {};
25536         this.fireEvent("beforetabchange", this, e, tab);
25537         if(e.cancel !== true && !tab.disabled){
25538             if(this.active){
25539                 this.active.hide();
25540             }
25541             this.active = this.items[id];
25542             this.active.show();
25543             this.fireEvent("tabchange", this, this.active);
25544         }
25545         return tab;
25546     },
25547
25548     /**
25549      * Gets the active {@link Roo.TabPanelItem}.
25550      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25551      */
25552     getActiveTab : function(){
25553         return this.active;
25554     },
25555
25556     /**
25557      * Updates the tab body element to fit the height of the container element
25558      * for overflow scrolling
25559      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25560      */
25561     syncHeight : function(targetHeight){
25562         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25563         var bm = this.bodyEl.getMargins();
25564         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25565         this.bodyEl.setHeight(newHeight);
25566         return newHeight;
25567     },
25568
25569     onResize : function(){
25570         if(this.monitorResize){
25571             this.autoSizeTabs();
25572         }
25573     },
25574
25575     /**
25576      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25577      */
25578     beginUpdate : function(){
25579         this.updating = true;
25580     },
25581
25582     /**
25583      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25584      */
25585     endUpdate : function(){
25586         this.updating = false;
25587         this.autoSizeTabs();
25588     },
25589
25590     /**
25591      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25592      */
25593     autoSizeTabs : function(){
25594         var count = this.items.length;
25595         var vcount = count - this.hiddenCount;
25596         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25597         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25598         var availWidth = Math.floor(w / vcount);
25599         var b = this.stripBody;
25600         if(b.getWidth() > w){
25601             var tabs = this.items;
25602             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25603             if(availWidth < this.minTabWidth){
25604                 /*if(!this.sleft){    // incomplete scrolling code
25605                     this.createScrollButtons();
25606                 }
25607                 this.showScroll();
25608                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25609             }
25610         }else{
25611             if(this.currentTabWidth < this.preferredTabWidth){
25612                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25613             }
25614         }
25615     },
25616
25617     /**
25618      * Returns the number of tabs in this TabPanel.
25619      * @return {Number}
25620      */
25621      getCount : function(){
25622          return this.items.length;
25623      },
25624
25625     /**
25626      * Resizes all the tabs to the passed width
25627      * @param {Number} The new width
25628      */
25629     setTabWidth : function(width){
25630         this.currentTabWidth = width;
25631         for(var i = 0, len = this.items.length; i < len; i++) {
25632                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25633         }
25634     },
25635
25636     /**
25637      * Destroys this TabPanel
25638      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25639      */
25640     destroy : function(removeEl){
25641         Roo.EventManager.removeResizeListener(this.onResize, this);
25642         for(var i = 0, len = this.items.length; i < len; i++){
25643             this.items[i].purgeListeners();
25644         }
25645         if(removeEl === true){
25646             this.el.update("");
25647             this.el.remove();
25648         }
25649     }
25650 });
25651
25652 /**
25653  * @class Roo.TabPanelItem
25654  * @extends Roo.util.Observable
25655  * Represents an individual item (tab plus body) in a TabPanel.
25656  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25657  * @param {String} id The id of this TabPanelItem
25658  * @param {String} text The text for the tab of this TabPanelItem
25659  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25660  */
25661 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25662     /**
25663      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25664      * @type Roo.TabPanel
25665      */
25666     this.tabPanel = tabPanel;
25667     /**
25668      * The id for this TabPanelItem
25669      * @type String
25670      */
25671     this.id = id;
25672     /** @private */
25673     this.disabled = false;
25674     /** @private */
25675     this.text = text;
25676     /** @private */
25677     this.loaded = false;
25678     this.closable = closable;
25679
25680     /**
25681      * The body element for this TabPanelItem.
25682      * @type Roo.Element
25683      */
25684     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25685     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25686     this.bodyEl.setStyle("display", "block");
25687     this.bodyEl.setStyle("zoom", "1");
25688     this.hideAction();
25689
25690     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25691     /** @private */
25692     this.el = Roo.get(els.el, true);
25693     this.inner = Roo.get(els.inner, true);
25694     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25695     this.pnode = Roo.get(els.el.parentNode, true);
25696     this.el.on("mousedown", this.onTabMouseDown, this);
25697     this.el.on("click", this.onTabClick, this);
25698     /** @private */
25699     if(closable){
25700         var c = Roo.get(els.close, true);
25701         c.dom.title = this.closeText;
25702         c.addClassOnOver("close-over");
25703         c.on("click", this.closeClick, this);
25704      }
25705
25706     this.addEvents({
25707          /**
25708          * @event activate
25709          * Fires when this tab becomes the active tab.
25710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25711          * @param {Roo.TabPanelItem} this
25712          */
25713         "activate": true,
25714         /**
25715          * @event beforeclose
25716          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25717          * @param {Roo.TabPanelItem} this
25718          * @param {Object} e Set cancel to true on this object to cancel the close.
25719          */
25720         "beforeclose": true,
25721         /**
25722          * @event close
25723          * Fires when this tab is closed.
25724          * @param {Roo.TabPanelItem} this
25725          */
25726          "close": true,
25727         /**
25728          * @event deactivate
25729          * Fires when this tab is no longer the active tab.
25730          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25731          * @param {Roo.TabPanelItem} this
25732          */
25733          "deactivate" : true
25734     });
25735     this.hidden = false;
25736
25737     Roo.TabPanelItem.superclass.constructor.call(this);
25738 };
25739
25740 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25741     purgeListeners : function(){
25742        Roo.util.Observable.prototype.purgeListeners.call(this);
25743        this.el.removeAllListeners();
25744     },
25745     /**
25746      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25747      */
25748     show : function(){
25749         this.pnode.addClass("on");
25750         this.showAction();
25751         if(Roo.isOpera){
25752             this.tabPanel.stripWrap.repaint();
25753         }
25754         this.fireEvent("activate", this.tabPanel, this);
25755     },
25756
25757     /**
25758      * Returns true if this tab is the active tab.
25759      * @return {Boolean}
25760      */
25761     isActive : function(){
25762         return this.tabPanel.getActiveTab() == this;
25763     },
25764
25765     /**
25766      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25767      */
25768     hide : function(){
25769         this.pnode.removeClass("on");
25770         this.hideAction();
25771         this.fireEvent("deactivate", this.tabPanel, this);
25772     },
25773
25774     hideAction : function(){
25775         this.bodyEl.hide();
25776         this.bodyEl.setStyle("position", "absolute");
25777         this.bodyEl.setLeft("-20000px");
25778         this.bodyEl.setTop("-20000px");
25779     },
25780
25781     showAction : function(){
25782         this.bodyEl.setStyle("position", "relative");
25783         this.bodyEl.setTop("");
25784         this.bodyEl.setLeft("");
25785         this.bodyEl.show();
25786     },
25787
25788     /**
25789      * Set the tooltip for the tab.
25790      * @param {String} tooltip The tab's tooltip
25791      */
25792     setTooltip : function(text){
25793         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25794             this.textEl.dom.qtip = text;
25795             this.textEl.dom.removeAttribute('title');
25796         }else{
25797             this.textEl.dom.title = text;
25798         }
25799     },
25800
25801     onTabClick : function(e){
25802         e.preventDefault();
25803         this.tabPanel.activate(this.id);
25804     },
25805
25806     onTabMouseDown : function(e){
25807         e.preventDefault();
25808         this.tabPanel.activate(this.id);
25809     },
25810
25811     getWidth : function(){
25812         return this.inner.getWidth();
25813     },
25814
25815     setWidth : function(width){
25816         var iwidth = width - this.pnode.getPadding("lr");
25817         this.inner.setWidth(iwidth);
25818         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25819         this.pnode.setWidth(width);
25820     },
25821
25822     /**
25823      * Show or hide the tab
25824      * @param {Boolean} hidden True to hide or false to show.
25825      */
25826     setHidden : function(hidden){
25827         this.hidden = hidden;
25828         this.pnode.setStyle("display", hidden ? "none" : "");
25829     },
25830
25831     /**
25832      * Returns true if this tab is "hidden"
25833      * @return {Boolean}
25834      */
25835     isHidden : function(){
25836         return this.hidden;
25837     },
25838
25839     /**
25840      * Returns the text for this tab
25841      * @return {String}
25842      */
25843     getText : function(){
25844         return this.text;
25845     },
25846
25847     autoSize : function(){
25848         //this.el.beginMeasure();
25849         this.textEl.setWidth(1);
25850         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25851         //this.el.endMeasure();
25852     },
25853
25854     /**
25855      * Sets the text for the tab (Note: this also sets the tooltip text)
25856      * @param {String} text The tab's text and tooltip
25857      */
25858     setText : function(text){
25859         this.text = text;
25860         this.textEl.update(text);
25861         this.setTooltip(text);
25862         if(!this.tabPanel.resizeTabs){
25863             this.autoSize();
25864         }
25865     },
25866     /**
25867      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25868      */
25869     activate : function(){
25870         this.tabPanel.activate(this.id);
25871     },
25872
25873     /**
25874      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25875      */
25876     disable : function(){
25877         if(this.tabPanel.active != this){
25878             this.disabled = true;
25879             this.pnode.addClass("disabled");
25880         }
25881     },
25882
25883     /**
25884      * Enables this TabPanelItem if it was previously disabled.
25885      */
25886     enable : function(){
25887         this.disabled = false;
25888         this.pnode.removeClass("disabled");
25889     },
25890
25891     /**
25892      * Sets the content for this TabPanelItem.
25893      * @param {String} content The content
25894      * @param {Boolean} loadScripts true to look for and load scripts
25895      */
25896     setContent : function(content, loadScripts){
25897         this.bodyEl.update(content, loadScripts);
25898     },
25899
25900     /**
25901      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25902      * @return {Roo.UpdateManager} The UpdateManager
25903      */
25904     getUpdateManager : function(){
25905         return this.bodyEl.getUpdateManager();
25906     },
25907
25908     /**
25909      * Set a URL to be used to load the content for this TabPanelItem.
25910      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25911      * @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)
25912      * @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)
25913      * @return {Roo.UpdateManager} The UpdateManager
25914      */
25915     setUrl : function(url, params, loadOnce){
25916         if(this.refreshDelegate){
25917             this.un('activate', this.refreshDelegate);
25918         }
25919         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25920         this.on("activate", this.refreshDelegate);
25921         return this.bodyEl.getUpdateManager();
25922     },
25923
25924     /** @private */
25925     _handleRefresh : function(url, params, loadOnce){
25926         if(!loadOnce || !this.loaded){
25927             var updater = this.bodyEl.getUpdateManager();
25928             updater.update(url, params, this._setLoaded.createDelegate(this));
25929         }
25930     },
25931
25932     /**
25933      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25934      *   Will fail silently if the setUrl method has not been called.
25935      *   This does not activate the panel, just updates its content.
25936      */
25937     refresh : function(){
25938         if(this.refreshDelegate){
25939            this.loaded = false;
25940            this.refreshDelegate();
25941         }
25942     },
25943
25944     /** @private */
25945     _setLoaded : function(){
25946         this.loaded = true;
25947     },
25948
25949     /** @private */
25950     closeClick : function(e){
25951         var o = {};
25952         e.stopEvent();
25953         this.fireEvent("beforeclose", this, o);
25954         if(o.cancel !== true){
25955             this.tabPanel.removeTab(this.id);
25956         }
25957     },
25958     /**
25959      * The text displayed in the tooltip for the close icon.
25960      * @type String
25961      */
25962     closeText : "Close this tab"
25963 });
25964
25965 /** @private */
25966 Roo.TabPanel.prototype.createStrip = function(container){
25967     var strip = document.createElement("div");
25968     strip.className = "x-tabs-wrap";
25969     container.appendChild(strip);
25970     return strip;
25971 };
25972 /** @private */
25973 Roo.TabPanel.prototype.createStripList = function(strip){
25974     // div wrapper for retard IE
25975     // returns the "tr" element.
25976     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25977         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25978         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25979     return strip.firstChild.firstChild.firstChild.firstChild;
25980 };
25981 /** @private */
25982 Roo.TabPanel.prototype.createBody = function(container){
25983     var body = document.createElement("div");
25984     Roo.id(body, "tab-body");
25985     Roo.fly(body).addClass("x-tabs-body");
25986     container.appendChild(body);
25987     return body;
25988 };
25989 /** @private */
25990 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25991     var body = Roo.getDom(id);
25992     if(!body){
25993         body = document.createElement("div");
25994         body.id = id;
25995     }
25996     Roo.fly(body).addClass("x-tabs-item-body");
25997     bodyEl.insertBefore(body, bodyEl.firstChild);
25998     return body;
25999 };
26000 /** @private */
26001 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26002     var td = document.createElement("td");
26003     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26004     //stripEl.appendChild(td);
26005     if(closable){
26006         td.className = "x-tabs-closable";
26007         if(!this.closeTpl){
26008             this.closeTpl = new Roo.Template(
26009                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26010                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26011                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26012             );
26013         }
26014         var el = this.closeTpl.overwrite(td, {"text": text});
26015         var close = el.getElementsByTagName("div")[0];
26016         var inner = el.getElementsByTagName("em")[0];
26017         return {"el": el, "close": close, "inner": inner};
26018     } else {
26019         if(!this.tabTpl){
26020             this.tabTpl = new Roo.Template(
26021                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26022                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26023             );
26024         }
26025         var el = this.tabTpl.overwrite(td, {"text": text});
26026         var inner = el.getElementsByTagName("em")[0];
26027         return {"el": el, "inner": inner};
26028     }
26029 };/*
26030  * Based on:
26031  * Ext JS Library 1.1.1
26032  * Copyright(c) 2006-2007, Ext JS, LLC.
26033  *
26034  * Originally Released Under LGPL - original licence link has changed is not relivant.
26035  *
26036  * Fork - LGPL
26037  * <script type="text/javascript">
26038  */
26039
26040 /**
26041  * @class Roo.Button
26042  * @extends Roo.util.Observable
26043  * Simple Button class
26044  * @cfg {String} text The button text
26045  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26046  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26047  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26048  * @cfg {Object} scope The scope of the handler
26049  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26050  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26051  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26052  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26053  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26054  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26055    applies if enableToggle = true)
26056  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26057  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26058   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26059  * @constructor
26060  * Create a new button
26061  * @param {Object} config The config object
26062  */
26063 Roo.Button = function(renderTo, config)
26064 {
26065     if (!config) {
26066         config = renderTo;
26067         renderTo = config.renderTo || false;
26068     }
26069     
26070     Roo.apply(this, config);
26071     this.addEvents({
26072         /**
26073              * @event click
26074              * Fires when this button is clicked
26075              * @param {Button} this
26076              * @param {EventObject} e The click event
26077              */
26078             "click" : true,
26079         /**
26080              * @event toggle
26081              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26082              * @param {Button} this
26083              * @param {Boolean} pressed
26084              */
26085             "toggle" : true,
26086         /**
26087              * @event mouseover
26088              * Fires when the mouse hovers over the button
26089              * @param {Button} this
26090              * @param {Event} e The event object
26091              */
26092         'mouseover' : true,
26093         /**
26094              * @event mouseout
26095              * Fires when the mouse exits the button
26096              * @param {Button} this
26097              * @param {Event} e The event object
26098              */
26099         'mouseout': true,
26100          /**
26101              * @event render
26102              * Fires when the button is rendered
26103              * @param {Button} this
26104              */
26105         'render': true
26106     });
26107     if(this.menu){
26108         this.menu = Roo.menu.MenuMgr.get(this.menu);
26109     }
26110     // register listeners first!!  - so render can be captured..
26111     Roo.util.Observable.call(this);
26112     if(renderTo){
26113         this.render(renderTo);
26114     }
26115     
26116   
26117 };
26118
26119 Roo.extend(Roo.Button, Roo.util.Observable, {
26120     /**
26121      * 
26122      */
26123     
26124     /**
26125      * Read-only. True if this button is hidden
26126      * @type Boolean
26127      */
26128     hidden : false,
26129     /**
26130      * Read-only. True if this button is disabled
26131      * @type Boolean
26132      */
26133     disabled : false,
26134     /**
26135      * Read-only. True if this button is pressed (only if enableToggle = true)
26136      * @type Boolean
26137      */
26138     pressed : false,
26139
26140     /**
26141      * @cfg {Number} tabIndex 
26142      * The DOM tabIndex for this button (defaults to undefined)
26143      */
26144     tabIndex : undefined,
26145
26146     /**
26147      * @cfg {Boolean} enableToggle
26148      * True to enable pressed/not pressed toggling (defaults to false)
26149      */
26150     enableToggle: false,
26151     /**
26152      * @cfg {Mixed} menu
26153      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26154      */
26155     menu : undefined,
26156     /**
26157      * @cfg {String} menuAlign
26158      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26159      */
26160     menuAlign : "tl-bl?",
26161
26162     /**
26163      * @cfg {String} iconCls
26164      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26165      */
26166     iconCls : undefined,
26167     /**
26168      * @cfg {String} type
26169      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26170      */
26171     type : 'button',
26172
26173     // private
26174     menuClassTarget: 'tr',
26175
26176     /**
26177      * @cfg {String} clickEvent
26178      * The type of event to map to the button's event handler (defaults to 'click')
26179      */
26180     clickEvent : 'click',
26181
26182     /**
26183      * @cfg {Boolean} handleMouseEvents
26184      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26185      */
26186     handleMouseEvents : true,
26187
26188     /**
26189      * @cfg {String} tooltipType
26190      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26191      */
26192     tooltipType : 'qtip',
26193
26194     /**
26195      * @cfg {String} cls
26196      * A CSS class to apply to the button's main element.
26197      */
26198     
26199     /**
26200      * @cfg {Roo.Template} template (Optional)
26201      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26202      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26203      * require code modifications if required elements (e.g. a button) aren't present.
26204      */
26205
26206     // private
26207     render : function(renderTo){
26208         var btn;
26209         if(this.hideParent){
26210             this.parentEl = Roo.get(renderTo);
26211         }
26212         if(!this.dhconfig){
26213             if(!this.template){
26214                 if(!Roo.Button.buttonTemplate){
26215                     // hideous table template
26216                     Roo.Button.buttonTemplate = new Roo.Template(
26217                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26218                         '<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>',
26219                         "</tr></tbody></table>");
26220                 }
26221                 this.template = Roo.Button.buttonTemplate;
26222             }
26223             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26224             var btnEl = btn.child("button:first");
26225             btnEl.on('focus', this.onFocus, this);
26226             btnEl.on('blur', this.onBlur, this);
26227             if(this.cls){
26228                 btn.addClass(this.cls);
26229             }
26230             if(this.icon){
26231                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26232             }
26233             if(this.iconCls){
26234                 btnEl.addClass(this.iconCls);
26235                 if(!this.cls){
26236                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26237                 }
26238             }
26239             if(this.tabIndex !== undefined){
26240                 btnEl.dom.tabIndex = this.tabIndex;
26241             }
26242             if(this.tooltip){
26243                 if(typeof this.tooltip == 'object'){
26244                     Roo.QuickTips.tips(Roo.apply({
26245                           target: btnEl.id
26246                     }, this.tooltip));
26247                 } else {
26248                     btnEl.dom[this.tooltipType] = this.tooltip;
26249                 }
26250             }
26251         }else{
26252             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26253         }
26254         this.el = btn;
26255         if(this.id){
26256             this.el.dom.id = this.el.id = this.id;
26257         }
26258         if(this.menu){
26259             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26260             this.menu.on("show", this.onMenuShow, this);
26261             this.menu.on("hide", this.onMenuHide, this);
26262         }
26263         btn.addClass("x-btn");
26264         if(Roo.isIE && !Roo.isIE7){
26265             this.autoWidth.defer(1, this);
26266         }else{
26267             this.autoWidth();
26268         }
26269         if(this.handleMouseEvents){
26270             btn.on("mouseover", this.onMouseOver, this);
26271             btn.on("mouseout", this.onMouseOut, this);
26272             btn.on("mousedown", this.onMouseDown, this);
26273         }
26274         btn.on(this.clickEvent, this.onClick, this);
26275         //btn.on("mouseup", this.onMouseUp, this);
26276         if(this.hidden){
26277             this.hide();
26278         }
26279         if(this.disabled){
26280             this.disable();
26281         }
26282         Roo.ButtonToggleMgr.register(this);
26283         if(this.pressed){
26284             this.el.addClass("x-btn-pressed");
26285         }
26286         if(this.repeat){
26287             var repeater = new Roo.util.ClickRepeater(btn,
26288                 typeof this.repeat == "object" ? this.repeat : {}
26289             );
26290             repeater.on("click", this.onClick,  this);
26291         }
26292         
26293         this.fireEvent('render', this);
26294         
26295     },
26296     /**
26297      * Returns the button's underlying element
26298      * @return {Roo.Element} The element
26299      */
26300     getEl : function(){
26301         return this.el;  
26302     },
26303     
26304     /**
26305      * Destroys this Button and removes any listeners.
26306      */
26307     destroy : function(){
26308         Roo.ButtonToggleMgr.unregister(this);
26309         this.el.removeAllListeners();
26310         this.purgeListeners();
26311         this.el.remove();
26312     },
26313
26314     // private
26315     autoWidth : function(){
26316         if(this.el){
26317             this.el.setWidth("auto");
26318             if(Roo.isIE7 && Roo.isStrict){
26319                 var ib = this.el.child('button');
26320                 if(ib && ib.getWidth() > 20){
26321                     ib.clip();
26322                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26323                 }
26324             }
26325             if(this.minWidth){
26326                 if(this.hidden){
26327                     this.el.beginMeasure();
26328                 }
26329                 if(this.el.getWidth() < this.minWidth){
26330                     this.el.setWidth(this.minWidth);
26331                 }
26332                 if(this.hidden){
26333                     this.el.endMeasure();
26334                 }
26335             }
26336         }
26337     },
26338
26339     /**
26340      * Assigns this button's click handler
26341      * @param {Function} handler The function to call when the button is clicked
26342      * @param {Object} scope (optional) Scope for the function passed in
26343      */
26344     setHandler : function(handler, scope){
26345         this.handler = handler;
26346         this.scope = scope;  
26347     },
26348     
26349     /**
26350      * Sets this button's text
26351      * @param {String} text The button text
26352      */
26353     setText : function(text){
26354         this.text = text;
26355         if(this.el){
26356             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26357         }
26358         this.autoWidth();
26359     },
26360     
26361     /**
26362      * Gets the text for this button
26363      * @return {String} The button text
26364      */
26365     getText : function(){
26366         return this.text;  
26367     },
26368     
26369     /**
26370      * Show this button
26371      */
26372     show: function(){
26373         this.hidden = false;
26374         if(this.el){
26375             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26376         }
26377     },
26378     
26379     /**
26380      * Hide this button
26381      */
26382     hide: function(){
26383         this.hidden = true;
26384         if(this.el){
26385             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26386         }
26387     },
26388     
26389     /**
26390      * Convenience function for boolean show/hide
26391      * @param {Boolean} visible True to show, false to hide
26392      */
26393     setVisible: function(visible){
26394         if(visible) {
26395             this.show();
26396         }else{
26397             this.hide();
26398         }
26399     },
26400     
26401     /**
26402      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26403      * @param {Boolean} state (optional) Force a particular state
26404      */
26405     toggle : function(state){
26406         state = state === undefined ? !this.pressed : state;
26407         if(state != this.pressed){
26408             if(state){
26409                 this.el.addClass("x-btn-pressed");
26410                 this.pressed = true;
26411                 this.fireEvent("toggle", this, true);
26412             }else{
26413                 this.el.removeClass("x-btn-pressed");
26414                 this.pressed = false;
26415                 this.fireEvent("toggle", this, false);
26416             }
26417             if(this.toggleHandler){
26418                 this.toggleHandler.call(this.scope || this, this, state);
26419             }
26420         }
26421     },
26422     
26423     /**
26424      * Focus the button
26425      */
26426     focus : function(){
26427         this.el.child('button:first').focus();
26428     },
26429     
26430     /**
26431      * Disable this button
26432      */
26433     disable : function(){
26434         if(this.el){
26435             this.el.addClass("x-btn-disabled");
26436         }
26437         this.disabled = true;
26438     },
26439     
26440     /**
26441      * Enable this button
26442      */
26443     enable : function(){
26444         if(this.el){
26445             this.el.removeClass("x-btn-disabled");
26446         }
26447         this.disabled = false;
26448     },
26449
26450     /**
26451      * Convenience function for boolean enable/disable
26452      * @param {Boolean} enabled True to enable, false to disable
26453      */
26454     setDisabled : function(v){
26455         this[v !== true ? "enable" : "disable"]();
26456     },
26457
26458     // private
26459     onClick : function(e){
26460         if(e){
26461             e.preventDefault();
26462         }
26463         if(e.button != 0){
26464             return;
26465         }
26466         if(!this.disabled){
26467             if(this.enableToggle){
26468                 this.toggle();
26469             }
26470             if(this.menu && !this.menu.isVisible()){
26471                 this.menu.show(this.el, this.menuAlign);
26472             }
26473             this.fireEvent("click", this, e);
26474             if(this.handler){
26475                 this.el.removeClass("x-btn-over");
26476                 this.handler.call(this.scope || this, this, e);
26477             }
26478         }
26479     },
26480     // private
26481     onMouseOver : function(e){
26482         if(!this.disabled){
26483             this.el.addClass("x-btn-over");
26484             this.fireEvent('mouseover', this, e);
26485         }
26486     },
26487     // private
26488     onMouseOut : function(e){
26489         if(!e.within(this.el,  true)){
26490             this.el.removeClass("x-btn-over");
26491             this.fireEvent('mouseout', this, e);
26492         }
26493     },
26494     // private
26495     onFocus : function(e){
26496         if(!this.disabled){
26497             this.el.addClass("x-btn-focus");
26498         }
26499     },
26500     // private
26501     onBlur : function(e){
26502         this.el.removeClass("x-btn-focus");
26503     },
26504     // private
26505     onMouseDown : function(e){
26506         if(!this.disabled && e.button == 0){
26507             this.el.addClass("x-btn-click");
26508             Roo.get(document).on('mouseup', this.onMouseUp, this);
26509         }
26510     },
26511     // private
26512     onMouseUp : function(e){
26513         if(e.button == 0){
26514             this.el.removeClass("x-btn-click");
26515             Roo.get(document).un('mouseup', this.onMouseUp, this);
26516         }
26517     },
26518     // private
26519     onMenuShow : function(e){
26520         this.el.addClass("x-btn-menu-active");
26521     },
26522     // private
26523     onMenuHide : function(e){
26524         this.el.removeClass("x-btn-menu-active");
26525     }   
26526 });
26527
26528 // Private utility class used by Button
26529 Roo.ButtonToggleMgr = function(){
26530    var groups = {};
26531    
26532    function toggleGroup(btn, state){
26533        if(state){
26534            var g = groups[btn.toggleGroup];
26535            for(var i = 0, l = g.length; i < l; i++){
26536                if(g[i] != btn){
26537                    g[i].toggle(false);
26538                }
26539            }
26540        }
26541    }
26542    
26543    return {
26544        register : function(btn){
26545            if(!btn.toggleGroup){
26546                return;
26547            }
26548            var g = groups[btn.toggleGroup];
26549            if(!g){
26550                g = groups[btn.toggleGroup] = [];
26551            }
26552            g.push(btn);
26553            btn.on("toggle", toggleGroup);
26554        },
26555        
26556        unregister : function(btn){
26557            if(!btn.toggleGroup){
26558                return;
26559            }
26560            var g = groups[btn.toggleGroup];
26561            if(g){
26562                g.remove(btn);
26563                btn.un("toggle", toggleGroup);
26564            }
26565        }
26566    };
26567 }();/*
26568  * Based on:
26569  * Ext JS Library 1.1.1
26570  * Copyright(c) 2006-2007, Ext JS, LLC.
26571  *
26572  * Originally Released Under LGPL - original licence link has changed is not relivant.
26573  *
26574  * Fork - LGPL
26575  * <script type="text/javascript">
26576  */
26577  
26578 /**
26579  * @class Roo.SplitButton
26580  * @extends Roo.Button
26581  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26582  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26583  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26584  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26585  * @cfg {String} arrowTooltip The title attribute of the arrow
26586  * @constructor
26587  * Create a new menu button
26588  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26589  * @param {Object} config The config object
26590  */
26591 Roo.SplitButton = function(renderTo, config){
26592     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26593     /**
26594      * @event arrowclick
26595      * Fires when this button's arrow is clicked
26596      * @param {SplitButton} this
26597      * @param {EventObject} e The click event
26598      */
26599     this.addEvents({"arrowclick":true});
26600 };
26601
26602 Roo.extend(Roo.SplitButton, Roo.Button, {
26603     render : function(renderTo){
26604         // this is one sweet looking template!
26605         var tpl = new Roo.Template(
26606             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26607             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26608             '<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>',
26609             "</tbody></table></td><td>",
26610             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26611             '<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>',
26612             "</tbody></table></td></tr></table>"
26613         );
26614         var btn = tpl.append(renderTo, [this.text, this.type], true);
26615         var btnEl = btn.child("button");
26616         if(this.cls){
26617             btn.addClass(this.cls);
26618         }
26619         if(this.icon){
26620             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26621         }
26622         if(this.iconCls){
26623             btnEl.addClass(this.iconCls);
26624             if(!this.cls){
26625                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26626             }
26627         }
26628         this.el = btn;
26629         if(this.handleMouseEvents){
26630             btn.on("mouseover", this.onMouseOver, this);
26631             btn.on("mouseout", this.onMouseOut, this);
26632             btn.on("mousedown", this.onMouseDown, this);
26633             btn.on("mouseup", this.onMouseUp, this);
26634         }
26635         btn.on(this.clickEvent, this.onClick, this);
26636         if(this.tooltip){
26637             if(typeof this.tooltip == 'object'){
26638                 Roo.QuickTips.tips(Roo.apply({
26639                       target: btnEl.id
26640                 }, this.tooltip));
26641             } else {
26642                 btnEl.dom[this.tooltipType] = this.tooltip;
26643             }
26644         }
26645         if(this.arrowTooltip){
26646             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26647         }
26648         if(this.hidden){
26649             this.hide();
26650         }
26651         if(this.disabled){
26652             this.disable();
26653         }
26654         if(this.pressed){
26655             this.el.addClass("x-btn-pressed");
26656         }
26657         if(Roo.isIE && !Roo.isIE7){
26658             this.autoWidth.defer(1, this);
26659         }else{
26660             this.autoWidth();
26661         }
26662         if(this.menu){
26663             this.menu.on("show", this.onMenuShow, this);
26664             this.menu.on("hide", this.onMenuHide, this);
26665         }
26666         this.fireEvent('render', this);
26667     },
26668
26669     // private
26670     autoWidth : function(){
26671         if(this.el){
26672             var tbl = this.el.child("table:first");
26673             var tbl2 = this.el.child("table:last");
26674             this.el.setWidth("auto");
26675             tbl.setWidth("auto");
26676             if(Roo.isIE7 && Roo.isStrict){
26677                 var ib = this.el.child('button:first');
26678                 if(ib && ib.getWidth() > 20){
26679                     ib.clip();
26680                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26681                 }
26682             }
26683             if(this.minWidth){
26684                 if(this.hidden){
26685                     this.el.beginMeasure();
26686                 }
26687                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26688                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26689                 }
26690                 if(this.hidden){
26691                     this.el.endMeasure();
26692                 }
26693             }
26694             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26695         } 
26696     },
26697     /**
26698      * Sets this button's click handler
26699      * @param {Function} handler The function to call when the button is clicked
26700      * @param {Object} scope (optional) Scope for the function passed above
26701      */
26702     setHandler : function(handler, scope){
26703         this.handler = handler;
26704         this.scope = scope;  
26705     },
26706     
26707     /**
26708      * Sets this button's arrow click handler
26709      * @param {Function} handler The function to call when the arrow is clicked
26710      * @param {Object} scope (optional) Scope for the function passed above
26711      */
26712     setArrowHandler : function(handler, scope){
26713         this.arrowHandler = handler;
26714         this.scope = scope;  
26715     },
26716     
26717     /**
26718      * Focus the button
26719      */
26720     focus : function(){
26721         if(this.el){
26722             this.el.child("button:first").focus();
26723         }
26724     },
26725
26726     // private
26727     onClick : function(e){
26728         e.preventDefault();
26729         if(!this.disabled){
26730             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26731                 if(this.menu && !this.menu.isVisible()){
26732                     this.menu.show(this.el, this.menuAlign);
26733                 }
26734                 this.fireEvent("arrowclick", this, e);
26735                 if(this.arrowHandler){
26736                     this.arrowHandler.call(this.scope || this, this, e);
26737                 }
26738             }else{
26739                 this.fireEvent("click", this, e);
26740                 if(this.handler){
26741                     this.handler.call(this.scope || this, this, e);
26742                 }
26743             }
26744         }
26745     },
26746     // private
26747     onMouseDown : function(e){
26748         if(!this.disabled){
26749             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26750         }
26751     },
26752     // private
26753     onMouseUp : function(e){
26754         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26755     }   
26756 });
26757
26758
26759 // backwards compat
26760 Roo.MenuButton = Roo.SplitButton;/*
26761  * Based on:
26762  * Ext JS Library 1.1.1
26763  * Copyright(c) 2006-2007, Ext JS, LLC.
26764  *
26765  * Originally Released Under LGPL - original licence link has changed is not relivant.
26766  *
26767  * Fork - LGPL
26768  * <script type="text/javascript">
26769  */
26770
26771 /**
26772  * @class Roo.Toolbar
26773  * Basic Toolbar class.
26774  * @constructor
26775  * Creates a new Toolbar
26776  * @param {Object} container The config object
26777  */ 
26778 Roo.Toolbar = function(container, buttons, config)
26779 {
26780     /// old consturctor format still supported..
26781     if(container instanceof Array){ // omit the container for later rendering
26782         buttons = container;
26783         config = buttons;
26784         container = null;
26785     }
26786     if (typeof(container) == 'object' && container.xtype) {
26787         config = container;
26788         container = config.container;
26789         buttons = config.buttons || []; // not really - use items!!
26790     }
26791     var xitems = [];
26792     if (config && config.items) {
26793         xitems = config.items;
26794         delete config.items;
26795     }
26796     Roo.apply(this, config);
26797     this.buttons = buttons;
26798     
26799     if(container){
26800         this.render(container);
26801     }
26802     this.xitems = xitems;
26803     Roo.each(xitems, function(b) {
26804         this.add(b);
26805     }, this);
26806     
26807 };
26808
26809 Roo.Toolbar.prototype = {
26810     /**
26811      * @cfg {Array} items
26812      * array of button configs or elements to add (will be converted to a MixedCollection)
26813      */
26814     
26815     /**
26816      * @cfg {String/HTMLElement/Element} container
26817      * The id or element that will contain the toolbar
26818      */
26819     // private
26820     render : function(ct){
26821         this.el = Roo.get(ct);
26822         if(this.cls){
26823             this.el.addClass(this.cls);
26824         }
26825         // using a table allows for vertical alignment
26826         // 100% width is needed by Safari...
26827         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26828         this.tr = this.el.child("tr", true);
26829         var autoId = 0;
26830         this.items = new Roo.util.MixedCollection(false, function(o){
26831             return o.id || ("item" + (++autoId));
26832         });
26833         if(this.buttons){
26834             this.add.apply(this, this.buttons);
26835             delete this.buttons;
26836         }
26837     },
26838
26839     /**
26840      * Adds element(s) to the toolbar -- this function takes a variable number of 
26841      * arguments of mixed type and adds them to the toolbar.
26842      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26843      * <ul>
26844      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26845      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26846      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26847      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26848      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26849      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26850      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26851      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26852      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26853      * </ul>
26854      * @param {Mixed} arg2
26855      * @param {Mixed} etc.
26856      */
26857     add : function(){
26858         var a = arguments, l = a.length;
26859         for(var i = 0; i < l; i++){
26860             this._add(a[i]);
26861         }
26862     },
26863     // private..
26864     _add : function(el) {
26865         
26866         if (el.xtype) {
26867             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26868         }
26869         
26870         if (el.applyTo){ // some kind of form field
26871             return this.addField(el);
26872         } 
26873         if (el.render){ // some kind of Toolbar.Item
26874             return this.addItem(el);
26875         }
26876         if (typeof el == "string"){ // string
26877             if(el == "separator" || el == "-"){
26878                 return this.addSeparator();
26879             }
26880             if (el == " "){
26881                 return this.addSpacer();
26882             }
26883             if(el == "->"){
26884                 return this.addFill();
26885             }
26886             return this.addText(el);
26887             
26888         }
26889         if(el.tagName){ // element
26890             return this.addElement(el);
26891         }
26892         if(typeof el == "object"){ // must be button config?
26893             return this.addButton(el);
26894         }
26895         // and now what?!?!
26896         return false;
26897         
26898     },
26899     
26900     /**
26901      * Add an Xtype element
26902      * @param {Object} xtype Xtype Object
26903      * @return {Object} created Object
26904      */
26905     addxtype : function(e){
26906         return this.add(e);  
26907     },
26908     
26909     /**
26910      * Returns the Element for this toolbar.
26911      * @return {Roo.Element}
26912      */
26913     getEl : function(){
26914         return this.el;  
26915     },
26916     
26917     /**
26918      * Adds a separator
26919      * @return {Roo.Toolbar.Item} The separator item
26920      */
26921     addSeparator : function(){
26922         return this.addItem(new Roo.Toolbar.Separator());
26923     },
26924
26925     /**
26926      * Adds a spacer element
26927      * @return {Roo.Toolbar.Spacer} The spacer item
26928      */
26929     addSpacer : function(){
26930         return this.addItem(new Roo.Toolbar.Spacer());
26931     },
26932
26933     /**
26934      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26935      * @return {Roo.Toolbar.Fill} The fill item
26936      */
26937     addFill : function(){
26938         return this.addItem(new Roo.Toolbar.Fill());
26939     },
26940
26941     /**
26942      * Adds any standard HTML element to the toolbar
26943      * @param {String/HTMLElement/Element} el The element or id of the element to add
26944      * @return {Roo.Toolbar.Item} The element's item
26945      */
26946     addElement : function(el){
26947         return this.addItem(new Roo.Toolbar.Item(el));
26948     },
26949     /**
26950      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26951      * @type Roo.util.MixedCollection  
26952      */
26953     items : false,
26954      
26955     /**
26956      * Adds any Toolbar.Item or subclass
26957      * @param {Roo.Toolbar.Item} item
26958      * @return {Roo.Toolbar.Item} The item
26959      */
26960     addItem : function(item){
26961         var td = this.nextBlock();
26962         item.render(td);
26963         this.items.add(item);
26964         return item;
26965     },
26966     
26967     /**
26968      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26969      * @param {Object/Array} config A button config or array of configs
26970      * @return {Roo.Toolbar.Button/Array}
26971      */
26972     addButton : function(config){
26973         if(config instanceof Array){
26974             var buttons = [];
26975             for(var i = 0, len = config.length; i < len; i++) {
26976                 buttons.push(this.addButton(config[i]));
26977             }
26978             return buttons;
26979         }
26980         var b = config;
26981         if(!(config instanceof Roo.Toolbar.Button)){
26982             b = config.split ?
26983                 new Roo.Toolbar.SplitButton(config) :
26984                 new Roo.Toolbar.Button(config);
26985         }
26986         var td = this.nextBlock();
26987         b.render(td);
26988         this.items.add(b);
26989         return b;
26990     },
26991     
26992     /**
26993      * Adds text to the toolbar
26994      * @param {String} text The text to add
26995      * @return {Roo.Toolbar.Item} The element's item
26996      */
26997     addText : function(text){
26998         return this.addItem(new Roo.Toolbar.TextItem(text));
26999     },
27000     
27001     /**
27002      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27003      * @param {Number} index The index where the item is to be inserted
27004      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27005      * @return {Roo.Toolbar.Button/Item}
27006      */
27007     insertButton : function(index, item){
27008         if(item instanceof Array){
27009             var buttons = [];
27010             for(var i = 0, len = item.length; i < len; i++) {
27011                buttons.push(this.insertButton(index + i, item[i]));
27012             }
27013             return buttons;
27014         }
27015         if (!(item instanceof Roo.Toolbar.Button)){
27016            item = new Roo.Toolbar.Button(item);
27017         }
27018         var td = document.createElement("td");
27019         this.tr.insertBefore(td, this.tr.childNodes[index]);
27020         item.render(td);
27021         this.items.insert(index, item);
27022         return item;
27023     },
27024     
27025     /**
27026      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27027      * @param {Object} config
27028      * @return {Roo.Toolbar.Item} The element's item
27029      */
27030     addDom : function(config, returnEl){
27031         var td = this.nextBlock();
27032         Roo.DomHelper.overwrite(td, config);
27033         var ti = new Roo.Toolbar.Item(td.firstChild);
27034         ti.render(td);
27035         this.items.add(ti);
27036         return ti;
27037     },
27038
27039     /**
27040      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27041      * @type Roo.util.MixedCollection  
27042      */
27043     fields : false,
27044     
27045     /**
27046      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27047      * Note: the field should not have been rendered yet. For a field that has already been
27048      * rendered, use {@link #addElement}.
27049      * @param {Roo.form.Field} field
27050      * @return {Roo.ToolbarItem}
27051      */
27052      
27053       
27054     addField : function(field) {
27055         if (!this.fields) {
27056             var autoId = 0;
27057             this.fields = new Roo.util.MixedCollection(false, function(o){
27058                 return o.id || ("item" + (++autoId));
27059             });
27060
27061         }
27062         
27063         var td = this.nextBlock();
27064         field.render(td);
27065         var ti = new Roo.Toolbar.Item(td.firstChild);
27066         ti.render(td);
27067         this.items.add(ti);
27068         this.fields.add(field);
27069         return ti;
27070     },
27071     /**
27072      * Hide the toolbar
27073      * @method hide
27074      */
27075      
27076       
27077     hide : function()
27078     {
27079         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27080         this.el.child('div').hide();
27081     },
27082     /**
27083      * Show the toolbar
27084      * @method show
27085      */
27086     show : function()
27087     {
27088         this.el.child('div').show();
27089     },
27090       
27091     // private
27092     nextBlock : function(){
27093         var td = document.createElement("td");
27094         this.tr.appendChild(td);
27095         return td;
27096     },
27097
27098     // private
27099     destroy : function(){
27100         if(this.items){ // rendered?
27101             Roo.destroy.apply(Roo, this.items.items);
27102         }
27103         if(this.fields){ // rendered?
27104             Roo.destroy.apply(Roo, this.fields.items);
27105         }
27106         Roo.Element.uncache(this.el, this.tr);
27107     }
27108 };
27109
27110 /**
27111  * @class Roo.Toolbar.Item
27112  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27113  * @constructor
27114  * Creates a new Item
27115  * @param {HTMLElement} el 
27116  */
27117 Roo.Toolbar.Item = function(el){
27118     this.el = Roo.getDom(el);
27119     this.id = Roo.id(this.el);
27120     this.hidden = false;
27121 };
27122
27123 Roo.Toolbar.Item.prototype = {
27124     
27125     /**
27126      * Get this item's HTML Element
27127      * @return {HTMLElement}
27128      */
27129     getEl : function(){
27130        return this.el;  
27131     },
27132
27133     // private
27134     render : function(td){
27135         this.td = td;
27136         td.appendChild(this.el);
27137     },
27138     
27139     /**
27140      * Removes and destroys this item.
27141      */
27142     destroy : function(){
27143         this.td.parentNode.removeChild(this.td);
27144     },
27145     
27146     /**
27147      * Shows this item.
27148      */
27149     show: function(){
27150         this.hidden = false;
27151         this.td.style.display = "";
27152     },
27153     
27154     /**
27155      * Hides this item.
27156      */
27157     hide: function(){
27158         this.hidden = true;
27159         this.td.style.display = "none";
27160     },
27161     
27162     /**
27163      * Convenience function for boolean show/hide.
27164      * @param {Boolean} visible true to show/false to hide
27165      */
27166     setVisible: function(visible){
27167         if(visible) {
27168             this.show();
27169         }else{
27170             this.hide();
27171         }
27172     },
27173     
27174     /**
27175      * Try to focus this item.
27176      */
27177     focus : function(){
27178         Roo.fly(this.el).focus();
27179     },
27180     
27181     /**
27182      * Disables this item.
27183      */
27184     disable : function(){
27185         Roo.fly(this.td).addClass("x-item-disabled");
27186         this.disabled = true;
27187         this.el.disabled = true;
27188     },
27189     
27190     /**
27191      * Enables this item.
27192      */
27193     enable : function(){
27194         Roo.fly(this.td).removeClass("x-item-disabled");
27195         this.disabled = false;
27196         this.el.disabled = false;
27197     }
27198 };
27199
27200
27201 /**
27202  * @class Roo.Toolbar.Separator
27203  * @extends Roo.Toolbar.Item
27204  * A simple toolbar separator class
27205  * @constructor
27206  * Creates a new Separator
27207  */
27208 Roo.Toolbar.Separator = function(){
27209     var s = document.createElement("span");
27210     s.className = "ytb-sep";
27211     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27212 };
27213 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27214     enable:Roo.emptyFn,
27215     disable:Roo.emptyFn,
27216     focus:Roo.emptyFn
27217 });
27218
27219 /**
27220  * @class Roo.Toolbar.Spacer
27221  * @extends Roo.Toolbar.Item
27222  * A simple element that adds extra horizontal space to a toolbar.
27223  * @constructor
27224  * Creates a new Spacer
27225  */
27226 Roo.Toolbar.Spacer = function(){
27227     var s = document.createElement("div");
27228     s.className = "ytb-spacer";
27229     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27230 };
27231 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27232     enable:Roo.emptyFn,
27233     disable:Roo.emptyFn,
27234     focus:Roo.emptyFn
27235 });
27236
27237 /**
27238  * @class Roo.Toolbar.Fill
27239  * @extends Roo.Toolbar.Spacer
27240  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27241  * @constructor
27242  * Creates a new Spacer
27243  */
27244 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27245     // private
27246     render : function(td){
27247         td.style.width = '100%';
27248         Roo.Toolbar.Fill.superclass.render.call(this, td);
27249     }
27250 });
27251
27252 /**
27253  * @class Roo.Toolbar.TextItem
27254  * @extends Roo.Toolbar.Item
27255  * A simple class that renders text directly into a toolbar.
27256  * @constructor
27257  * Creates a new TextItem
27258  * @param {String} text
27259  */
27260 Roo.Toolbar.TextItem = function(text){
27261     if (typeof(text) == 'object') {
27262         text = text.text;
27263     }
27264     var s = document.createElement("span");
27265     s.className = "ytb-text";
27266     s.innerHTML = text;
27267     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27268 };
27269 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27270     enable:Roo.emptyFn,
27271     disable:Roo.emptyFn,
27272     focus:Roo.emptyFn
27273 });
27274
27275 /**
27276  * @class Roo.Toolbar.Button
27277  * @extends Roo.Button
27278  * A button that renders into a toolbar.
27279  * @constructor
27280  * Creates a new Button
27281  * @param {Object} config A standard {@link Roo.Button} config object
27282  */
27283 Roo.Toolbar.Button = function(config){
27284     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27285 };
27286 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27287     render : function(td){
27288         this.td = td;
27289         Roo.Toolbar.Button.superclass.render.call(this, td);
27290     },
27291     
27292     /**
27293      * Removes and destroys this button
27294      */
27295     destroy : function(){
27296         Roo.Toolbar.Button.superclass.destroy.call(this);
27297         this.td.parentNode.removeChild(this.td);
27298     },
27299     
27300     /**
27301      * Shows this button
27302      */
27303     show: function(){
27304         this.hidden = false;
27305         this.td.style.display = "";
27306     },
27307     
27308     /**
27309      * Hides this button
27310      */
27311     hide: function(){
27312         this.hidden = true;
27313         this.td.style.display = "none";
27314     },
27315
27316     /**
27317      * Disables this item
27318      */
27319     disable : function(){
27320         Roo.fly(this.td).addClass("x-item-disabled");
27321         this.disabled = true;
27322     },
27323
27324     /**
27325      * Enables this item
27326      */
27327     enable : function(){
27328         Roo.fly(this.td).removeClass("x-item-disabled");
27329         this.disabled = false;
27330     }
27331 });
27332 // backwards compat
27333 Roo.ToolbarButton = Roo.Toolbar.Button;
27334
27335 /**
27336  * @class Roo.Toolbar.SplitButton
27337  * @extends Roo.SplitButton
27338  * A menu button that renders into a toolbar.
27339  * @constructor
27340  * Creates a new SplitButton
27341  * @param {Object} config A standard {@link Roo.SplitButton} config object
27342  */
27343 Roo.Toolbar.SplitButton = function(config){
27344     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27345 };
27346 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27347     render : function(td){
27348         this.td = td;
27349         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27350     },
27351     
27352     /**
27353      * Removes and destroys this button
27354      */
27355     destroy : function(){
27356         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27357         this.td.parentNode.removeChild(this.td);
27358     },
27359     
27360     /**
27361      * Shows this button
27362      */
27363     show: function(){
27364         this.hidden = false;
27365         this.td.style.display = "";
27366     },
27367     
27368     /**
27369      * Hides this button
27370      */
27371     hide: function(){
27372         this.hidden = true;
27373         this.td.style.display = "none";
27374     }
27375 });
27376
27377 // backwards compat
27378 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27379  * Based on:
27380  * Ext JS Library 1.1.1
27381  * Copyright(c) 2006-2007, Ext JS, LLC.
27382  *
27383  * Originally Released Under LGPL - original licence link has changed is not relivant.
27384  *
27385  * Fork - LGPL
27386  * <script type="text/javascript">
27387  */
27388  
27389 /**
27390  * @class Roo.PagingToolbar
27391  * @extends Roo.Toolbar
27392  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27393  * @constructor
27394  * Create a new PagingToolbar
27395  * @param {Object} config The config object
27396  */
27397 Roo.PagingToolbar = function(el, ds, config)
27398 {
27399     // old args format still supported... - xtype is prefered..
27400     if (typeof(el) == 'object' && el.xtype) {
27401         // created from xtype...
27402         config = el;
27403         ds = el.dataSource;
27404         el = config.container;
27405     }
27406     var items = [];
27407     if (config.items) {
27408         items = config.items;
27409         config.items = [];
27410     }
27411     
27412     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27413     this.ds = ds;
27414     this.cursor = 0;
27415     this.renderButtons(this.el);
27416     this.bind(ds);
27417     
27418     // supprot items array.
27419    
27420     Roo.each(items, function(e) {
27421         this.add(Roo.factory(e));
27422     },this);
27423     
27424 };
27425
27426 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27427     /**
27428      * @cfg {Roo.data.Store} dataSource
27429      * The underlying data store providing the paged data
27430      */
27431     /**
27432      * @cfg {String/HTMLElement/Element} container
27433      * container The id or element that will contain the toolbar
27434      */
27435     /**
27436      * @cfg {Boolean} displayInfo
27437      * True to display the displayMsg (defaults to false)
27438      */
27439     /**
27440      * @cfg {Number} pageSize
27441      * The number of records to display per page (defaults to 20)
27442      */
27443     pageSize: 20,
27444     /**
27445      * @cfg {String} displayMsg
27446      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27447      */
27448     displayMsg : 'Displaying {0} - {1} of {2}',
27449     /**
27450      * @cfg {String} emptyMsg
27451      * The message to display when no records are found (defaults to "No data to display")
27452      */
27453     emptyMsg : 'No data to display',
27454     /**
27455      * Customizable piece of the default paging text (defaults to "Page")
27456      * @type String
27457      */
27458     beforePageText : "Page",
27459     /**
27460      * Customizable piece of the default paging text (defaults to "of %0")
27461      * @type String
27462      */
27463     afterPageText : "of {0}",
27464     /**
27465      * Customizable piece of the default paging text (defaults to "First Page")
27466      * @type String
27467      */
27468     firstText : "First Page",
27469     /**
27470      * Customizable piece of the default paging text (defaults to "Previous Page")
27471      * @type String
27472      */
27473     prevText : "Previous Page",
27474     /**
27475      * Customizable piece of the default paging text (defaults to "Next Page")
27476      * @type String
27477      */
27478     nextText : "Next Page",
27479     /**
27480      * Customizable piece of the default paging text (defaults to "Last Page")
27481      * @type String
27482      */
27483     lastText : "Last Page",
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Refresh")
27486      * @type String
27487      */
27488     refreshText : "Refresh",
27489
27490     // private
27491     renderButtons : function(el){
27492         Roo.PagingToolbar.superclass.render.call(this, el);
27493         this.first = this.addButton({
27494             tooltip: this.firstText,
27495             cls: "x-btn-icon x-grid-page-first",
27496             disabled: true,
27497             handler: this.onClick.createDelegate(this, ["first"])
27498         });
27499         this.prev = this.addButton({
27500             tooltip: this.prevText,
27501             cls: "x-btn-icon x-grid-page-prev",
27502             disabled: true,
27503             handler: this.onClick.createDelegate(this, ["prev"])
27504         });
27505         //this.addSeparator();
27506         this.add(this.beforePageText);
27507         this.field = Roo.get(this.addDom({
27508            tag: "input",
27509            type: "text",
27510            size: "3",
27511            value: "1",
27512            cls: "x-grid-page-number"
27513         }).el);
27514         this.field.on("keydown", this.onPagingKeydown, this);
27515         this.field.on("focus", function(){this.dom.select();});
27516         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27517         this.field.setHeight(18);
27518         //this.addSeparator();
27519         this.next = this.addButton({
27520             tooltip: this.nextText,
27521             cls: "x-btn-icon x-grid-page-next",
27522             disabled: true,
27523             handler: this.onClick.createDelegate(this, ["next"])
27524         });
27525         this.last = this.addButton({
27526             tooltip: this.lastText,
27527             cls: "x-btn-icon x-grid-page-last",
27528             disabled: true,
27529             handler: this.onClick.createDelegate(this, ["last"])
27530         });
27531         //this.addSeparator();
27532         this.loading = this.addButton({
27533             tooltip: this.refreshText,
27534             cls: "x-btn-icon x-grid-loading",
27535             handler: this.onClick.createDelegate(this, ["refresh"])
27536         });
27537
27538         if(this.displayInfo){
27539             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27540         }
27541     },
27542
27543     // private
27544     updateInfo : function(){
27545         if(this.displayEl){
27546             var count = this.ds.getCount();
27547             var msg = count == 0 ?
27548                 this.emptyMsg :
27549                 String.format(
27550                     this.displayMsg,
27551                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27552                 );
27553             this.displayEl.update(msg);
27554         }
27555     },
27556
27557     // private
27558     onLoad : function(ds, r, o){
27559        this.cursor = o.params ? o.params.start : 0;
27560        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27561
27562        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27563        this.field.dom.value = ap;
27564        this.first.setDisabled(ap == 1);
27565        this.prev.setDisabled(ap == 1);
27566        this.next.setDisabled(ap == ps);
27567        this.last.setDisabled(ap == ps);
27568        this.loading.enable();
27569        this.updateInfo();
27570     },
27571
27572     // private
27573     getPageData : function(){
27574         var total = this.ds.getTotalCount();
27575         return {
27576             total : total,
27577             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27578             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27579         };
27580     },
27581
27582     // private
27583     onLoadError : function(){
27584         this.loading.enable();
27585     },
27586
27587     // private
27588     onPagingKeydown : function(e){
27589         var k = e.getKey();
27590         var d = this.getPageData();
27591         if(k == e.RETURN){
27592             var v = this.field.dom.value, pageNum;
27593             if(!v || isNaN(pageNum = parseInt(v, 10))){
27594                 this.field.dom.value = d.activePage;
27595                 return;
27596             }
27597             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27598             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27599             e.stopEvent();
27600         }
27601         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))
27602         {
27603           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27604           this.field.dom.value = pageNum;
27605           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27606           e.stopEvent();
27607         }
27608         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27609         {
27610           var v = this.field.dom.value, pageNum; 
27611           var increment = (e.shiftKey) ? 10 : 1;
27612           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27613             increment *= -1;
27614           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27615             this.field.dom.value = d.activePage;
27616             return;
27617           }
27618           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27619           {
27620             this.field.dom.value = parseInt(v, 10) + increment;
27621             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623           }
27624           e.stopEvent();
27625         }
27626     },
27627
27628     // private
27629     beforeLoad : function(){
27630         if(this.loading){
27631             this.loading.disable();
27632         }
27633     },
27634
27635     // private
27636     onClick : function(which){
27637         var ds = this.ds;
27638         switch(which){
27639             case "first":
27640                 ds.load({params:{start: 0, limit: this.pageSize}});
27641             break;
27642             case "prev":
27643                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27644             break;
27645             case "next":
27646                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27647             break;
27648             case "last":
27649                 var total = ds.getTotalCount();
27650                 var extra = total % this.pageSize;
27651                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27652                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27653             break;
27654             case "refresh":
27655                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27656             break;
27657         }
27658     },
27659
27660     /**
27661      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27662      * @param {Roo.data.Store} store The data store to unbind
27663      */
27664     unbind : function(ds){
27665         ds.un("beforeload", this.beforeLoad, this);
27666         ds.un("load", this.onLoad, this);
27667         ds.un("loadexception", this.onLoadError, this);
27668         ds.un("remove", this.updateInfo, this);
27669         ds.un("add", this.updateInfo, this);
27670         this.ds = undefined;
27671     },
27672
27673     /**
27674      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27675      * @param {Roo.data.Store} store The data store to bind
27676      */
27677     bind : function(ds){
27678         ds.on("beforeload", this.beforeLoad, this);
27679         ds.on("load", this.onLoad, this);
27680         ds.on("loadexception", this.onLoadError, this);
27681         ds.on("remove", this.updateInfo, this);
27682         ds.on("add", this.updateInfo, this);
27683         this.ds = ds;
27684     }
27685 });/*
27686  * Based on:
27687  * Ext JS Library 1.1.1
27688  * Copyright(c) 2006-2007, Ext JS, LLC.
27689  *
27690  * Originally Released Under LGPL - original licence link has changed is not relivant.
27691  *
27692  * Fork - LGPL
27693  * <script type="text/javascript">
27694  */
27695
27696 /**
27697  * @class Roo.Resizable
27698  * @extends Roo.util.Observable
27699  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27700  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27701  * 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
27702  * the element will be wrapped for you automatically.</p>
27703  * <p>Here is the list of valid resize handles:</p>
27704  * <pre>
27705 Value   Description
27706 ------  -------------------
27707  'n'     north
27708  's'     south
27709  'e'     east
27710  'w'     west
27711  'nw'    northwest
27712  'sw'    southwest
27713  'se'    southeast
27714  'ne'    northeast
27715  'hd'    horizontal drag
27716  'all'   all
27717 </pre>
27718  * <p>Here's an example showing the creation of a typical Resizable:</p>
27719  * <pre><code>
27720 var resizer = new Roo.Resizable("element-id", {
27721     handles: 'all',
27722     minWidth: 200,
27723     minHeight: 100,
27724     maxWidth: 500,
27725     maxHeight: 400,
27726     pinned: true
27727 });
27728 resizer.on("resize", myHandler);
27729 </code></pre>
27730  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27731  * resizer.east.setDisplayed(false);</p>
27732  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27733  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27734  * resize operation's new size (defaults to [0, 0])
27735  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27736  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27737  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27738  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27739  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27740  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27741  * @cfg {Number} width The width of the element in pixels (defaults to null)
27742  * @cfg {Number} height The height of the element in pixels (defaults to null)
27743  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27744  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27745  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27746  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27747  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27748  * in favor of the handles config option (defaults to false)
27749  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27750  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27751  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27752  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27753  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27754  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27755  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27756  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27757  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27758  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27759  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27760  * @constructor
27761  * Create a new resizable component
27762  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27763  * @param {Object} config configuration options
27764   */
27765 Roo.Resizable = function(el, config)
27766 {
27767     this.el = Roo.get(el);
27768
27769     if(config && config.wrap){
27770         config.resizeChild = this.el;
27771         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27772         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27773         this.el.setStyle("overflow", "hidden");
27774         this.el.setPositioning(config.resizeChild.getPositioning());
27775         config.resizeChild.clearPositioning();
27776         if(!config.width || !config.height){
27777             var csize = config.resizeChild.getSize();
27778             this.el.setSize(csize.width, csize.height);
27779         }
27780         if(config.pinned && !config.adjustments){
27781             config.adjustments = "auto";
27782         }
27783     }
27784
27785     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27786     this.proxy.unselectable();
27787     this.proxy.enableDisplayMode('block');
27788
27789     Roo.apply(this, config);
27790
27791     if(this.pinned){
27792         this.disableTrackOver = true;
27793         this.el.addClass("x-resizable-pinned");
27794     }
27795     // if the element isn't positioned, make it relative
27796     var position = this.el.getStyle("position");
27797     if(position != "absolute" && position != "fixed"){
27798         this.el.setStyle("position", "relative");
27799     }
27800     if(!this.handles){ // no handles passed, must be legacy style
27801         this.handles = 's,e,se';
27802         if(this.multiDirectional){
27803             this.handles += ',n,w';
27804         }
27805     }
27806     if(this.handles == "all"){
27807         this.handles = "n s e w ne nw se sw";
27808     }
27809     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27810     var ps = Roo.Resizable.positions;
27811     for(var i = 0, len = hs.length; i < len; i++){
27812         if(hs[i] && ps[hs[i]]){
27813             var pos = ps[hs[i]];
27814             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27815         }
27816     }
27817     // legacy
27818     this.corner = this.southeast;
27819     
27820     // updateBox = the box can move..
27821     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27822         this.updateBox = true;
27823     }
27824
27825     this.activeHandle = null;
27826
27827     if(this.resizeChild){
27828         if(typeof this.resizeChild == "boolean"){
27829             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27830         }else{
27831             this.resizeChild = Roo.get(this.resizeChild, true);
27832         }
27833     }
27834     
27835     if(this.adjustments == "auto"){
27836         var rc = this.resizeChild;
27837         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27838         if(rc && (hw || hn)){
27839             rc.position("relative");
27840             rc.setLeft(hw ? hw.el.getWidth() : 0);
27841             rc.setTop(hn ? hn.el.getHeight() : 0);
27842         }
27843         this.adjustments = [
27844             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27845             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27846         ];
27847     }
27848
27849     if(this.draggable){
27850         this.dd = this.dynamic ?
27851             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27852         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27853     }
27854
27855     // public events
27856     this.addEvents({
27857         /**
27858          * @event beforeresize
27859          * Fired before resize is allowed. Set enabled to false to cancel resize.
27860          * @param {Roo.Resizable} this
27861          * @param {Roo.EventObject} e The mousedown event
27862          */
27863         "beforeresize" : true,
27864         /**
27865          * @event resize
27866          * Fired after a resize.
27867          * @param {Roo.Resizable} this
27868          * @param {Number} width The new width
27869          * @param {Number} height The new height
27870          * @param {Roo.EventObject} e The mouseup event
27871          */
27872         "resize" : true
27873     });
27874
27875     if(this.width !== null && this.height !== null){
27876         this.resizeTo(this.width, this.height);
27877     }else{
27878         this.updateChildSize();
27879     }
27880     if(Roo.isIE){
27881         this.el.dom.style.zoom = 1;
27882     }
27883     Roo.Resizable.superclass.constructor.call(this);
27884 };
27885
27886 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27887         resizeChild : false,
27888         adjustments : [0, 0],
27889         minWidth : 5,
27890         minHeight : 5,
27891         maxWidth : 10000,
27892         maxHeight : 10000,
27893         enabled : true,
27894         animate : false,
27895         duration : .35,
27896         dynamic : false,
27897         handles : false,
27898         multiDirectional : false,
27899         disableTrackOver : false,
27900         easing : 'easeOutStrong',
27901         widthIncrement : 0,
27902         heightIncrement : 0,
27903         pinned : false,
27904         width : null,
27905         height : null,
27906         preserveRatio : false,
27907         transparent: false,
27908         minX: 0,
27909         minY: 0,
27910         draggable: false,
27911
27912         /**
27913          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27914          */
27915         constrainTo: undefined,
27916         /**
27917          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27918          */
27919         resizeRegion: undefined,
27920
27921
27922     /**
27923      * Perform a manual resize
27924      * @param {Number} width
27925      * @param {Number} height
27926      */
27927     resizeTo : function(width, height){
27928         this.el.setSize(width, height);
27929         this.updateChildSize();
27930         this.fireEvent("resize", this, width, height, null);
27931     },
27932
27933     // private
27934     startSizing : function(e, handle){
27935         this.fireEvent("beforeresize", this, e);
27936         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27937
27938             if(!this.overlay){
27939                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27940                 this.overlay.unselectable();
27941                 this.overlay.enableDisplayMode("block");
27942                 this.overlay.on("mousemove", this.onMouseMove, this);
27943                 this.overlay.on("mouseup", this.onMouseUp, this);
27944             }
27945             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27946
27947             this.resizing = true;
27948             this.startBox = this.el.getBox();
27949             this.startPoint = e.getXY();
27950             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27951                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27952
27953             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27954             this.overlay.show();
27955
27956             if(this.constrainTo) {
27957                 var ct = Roo.get(this.constrainTo);
27958                 this.resizeRegion = ct.getRegion().adjust(
27959                     ct.getFrameWidth('t'),
27960                     ct.getFrameWidth('l'),
27961                     -ct.getFrameWidth('b'),
27962                     -ct.getFrameWidth('r')
27963                 );
27964             }
27965
27966             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27967             this.proxy.show();
27968             this.proxy.setBox(this.startBox);
27969             if(!this.dynamic){
27970                 this.proxy.setStyle('visibility', 'visible');
27971             }
27972         }
27973     },
27974
27975     // private
27976     onMouseDown : function(handle, e){
27977         if(this.enabled){
27978             e.stopEvent();
27979             this.activeHandle = handle;
27980             this.startSizing(e, handle);
27981         }
27982     },
27983
27984     // private
27985     onMouseUp : function(e){
27986         var size = this.resizeElement();
27987         this.resizing = false;
27988         this.handleOut();
27989         this.overlay.hide();
27990         this.proxy.hide();
27991         this.fireEvent("resize", this, size.width, size.height, e);
27992     },
27993
27994     // private
27995     updateChildSize : function(){
27996         if(this.resizeChild){
27997             var el = this.el;
27998             var child = this.resizeChild;
27999             var adj = this.adjustments;
28000             if(el.dom.offsetWidth){
28001                 var b = el.getSize(true);
28002                 child.setSize(b.width+adj[0], b.height+adj[1]);
28003             }
28004             // Second call here for IE
28005             // The first call enables instant resizing and
28006             // the second call corrects scroll bars if they
28007             // exist
28008             if(Roo.isIE){
28009                 setTimeout(function(){
28010                     if(el.dom.offsetWidth){
28011                         var b = el.getSize(true);
28012                         child.setSize(b.width+adj[0], b.height+adj[1]);
28013                     }
28014                 }, 10);
28015             }
28016         }
28017     },
28018
28019     // private
28020     snap : function(value, inc, min){
28021         if(!inc || !value) return value;
28022         var newValue = value;
28023         var m = value % inc;
28024         if(m > 0){
28025             if(m > (inc/2)){
28026                 newValue = value + (inc-m);
28027             }else{
28028                 newValue = value - m;
28029             }
28030         }
28031         return Math.max(min, newValue);
28032     },
28033
28034     // private
28035     resizeElement : function(){
28036         var box = this.proxy.getBox();
28037         if(this.updateBox){
28038             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28039         }else{
28040             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28041         }
28042         this.updateChildSize();
28043         if(!this.dynamic){
28044             this.proxy.hide();
28045         }
28046         return box;
28047     },
28048
28049     // private
28050     constrain : function(v, diff, m, mx){
28051         if(v - diff < m){
28052             diff = v - m;
28053         }else if(v - diff > mx){
28054             diff = mx - v;
28055         }
28056         return diff;
28057     },
28058
28059     // private
28060     onMouseMove : function(e){
28061         if(this.enabled){
28062             try{// try catch so if something goes wrong the user doesn't get hung
28063
28064             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28065                 return;
28066             }
28067
28068             //var curXY = this.startPoint;
28069             var curSize = this.curSize || this.startBox;
28070             var x = this.startBox.x, y = this.startBox.y;
28071             var ox = x, oy = y;
28072             var w = curSize.width, h = curSize.height;
28073             var ow = w, oh = h;
28074             var mw = this.minWidth, mh = this.minHeight;
28075             var mxw = this.maxWidth, mxh = this.maxHeight;
28076             var wi = this.widthIncrement;
28077             var hi = this.heightIncrement;
28078
28079             var eventXY = e.getXY();
28080             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28081             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28082
28083             var pos = this.activeHandle.position;
28084
28085             switch(pos){
28086                 case "east":
28087                     w += diffX;
28088                     w = Math.min(Math.max(mw, w), mxw);
28089                     break;
28090              
28091                 case "south":
28092                     h += diffY;
28093                     h = Math.min(Math.max(mh, h), mxh);
28094                     break;
28095                 case "southeast":
28096                     w += diffX;
28097                     h += diffY;
28098                     w = Math.min(Math.max(mw, w), mxw);
28099                     h = Math.min(Math.max(mh, h), mxh);
28100                     break;
28101                 case "north":
28102                     diffY = this.constrain(h, diffY, mh, mxh);
28103                     y += diffY;
28104                     h -= diffY;
28105                     break;
28106                 case "hdrag":
28107                     
28108                     if (wi) {
28109                         var adiffX = Math.abs(diffX);
28110                         var sub = (adiffX % wi); // how much 
28111                         if (sub > (wi/2)) { // far enough to snap
28112                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28113                         } else {
28114                             // remove difference.. 
28115                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28116                         }
28117                     }
28118                     x += diffX;
28119                     x = Math.max(this.minX, x);
28120                     break;
28121                 case "west":
28122                     diffX = this.constrain(w, diffX, mw, mxw);
28123                     x += diffX;
28124                     w -= diffX;
28125                     break;
28126                 case "northeast":
28127                     w += diffX;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     diffY = this.constrain(h, diffY, mh, mxh);
28130                     y += diffY;
28131                     h -= diffY;
28132                     break;
28133                 case "northwest":
28134                     diffX = this.constrain(w, diffX, mw, mxw);
28135                     diffY = this.constrain(h, diffY, mh, mxh);
28136                     y += diffY;
28137                     h -= diffY;
28138                     x += diffX;
28139                     w -= diffX;
28140                     break;
28141                case "southwest":
28142                     diffX = this.constrain(w, diffX, mw, mxw);
28143                     h += diffY;
28144                     h = Math.min(Math.max(mh, h), mxh);
28145                     x += diffX;
28146                     w -= diffX;
28147                     break;
28148             }
28149
28150             var sw = this.snap(w, wi, mw);
28151             var sh = this.snap(h, hi, mh);
28152             if(sw != w || sh != h){
28153                 switch(pos){
28154                     case "northeast":
28155                         y -= sh - h;
28156                     break;
28157                     case "north":
28158                         y -= sh - h;
28159                         break;
28160                     case "southwest":
28161                         x -= sw - w;
28162                     break;
28163                     case "west":
28164                         x -= sw - w;
28165                         break;
28166                     case "northwest":
28167                         x -= sw - w;
28168                         y -= sh - h;
28169                     break;
28170                 }
28171                 w = sw;
28172                 h = sh;
28173             }
28174
28175             if(this.preserveRatio){
28176                 switch(pos){
28177                     case "southeast":
28178                     case "east":
28179                         h = oh * (w/ow);
28180                         h = Math.min(Math.max(mh, h), mxh);
28181                         w = ow * (h/oh);
28182                        break;
28183                     case "south":
28184                         w = ow * (h/oh);
28185                         w = Math.min(Math.max(mw, w), mxw);
28186                         h = oh * (w/ow);
28187                         break;
28188                     case "northeast":
28189                         w = ow * (h/oh);
28190                         w = Math.min(Math.max(mw, w), mxw);
28191                         h = oh * (w/ow);
28192                     break;
28193                     case "north":
28194                         var tw = w;
28195                         w = ow * (h/oh);
28196                         w = Math.min(Math.max(mw, w), mxw);
28197                         h = oh * (w/ow);
28198                         x += (tw - w) / 2;
28199                         break;
28200                     case "southwest":
28201                         h = oh * (w/ow);
28202                         h = Math.min(Math.max(mh, h), mxh);
28203                         var tw = w;
28204                         w = ow * (h/oh);
28205                         x += tw - w;
28206                         break;
28207                     case "west":
28208                         var th = h;
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         y += (th - h) / 2;
28212                         var tw = w;
28213                         w = ow * (h/oh);
28214                         x += tw - w;
28215                        break;
28216                     case "northwest":
28217                         var tw = w;
28218                         var th = h;
28219                         h = oh * (w/ow);
28220                         h = Math.min(Math.max(mh, h), mxh);
28221                         w = ow * (h/oh);
28222                         y += th - h;
28223                         x += tw - w;
28224                        break;
28225
28226                 }
28227             }
28228             if (pos == 'hdrag') {
28229                 w = ow;
28230             }
28231             this.proxy.setBounds(x, y, w, h);
28232             if(this.dynamic){
28233                 this.resizeElement();
28234             }
28235             }catch(e){}
28236         }
28237     },
28238
28239     // private
28240     handleOver : function(){
28241         if(this.enabled){
28242             this.el.addClass("x-resizable-over");
28243         }
28244     },
28245
28246     // private
28247     handleOut : function(){
28248         if(!this.resizing){
28249             this.el.removeClass("x-resizable-over");
28250         }
28251     },
28252
28253     /**
28254      * Returns the element this component is bound to.
28255      * @return {Roo.Element}
28256      */
28257     getEl : function(){
28258         return this.el;
28259     },
28260
28261     /**
28262      * Returns the resizeChild element (or null).
28263      * @return {Roo.Element}
28264      */
28265     getResizeChild : function(){
28266         return this.resizeChild;
28267     },
28268
28269     /**
28270      * Destroys this resizable. If the element was wrapped and
28271      * removeEl is not true then the element remains.
28272      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28273      */
28274     destroy : function(removeEl){
28275         this.proxy.remove();
28276         if(this.overlay){
28277             this.overlay.removeAllListeners();
28278             this.overlay.remove();
28279         }
28280         var ps = Roo.Resizable.positions;
28281         for(var k in ps){
28282             if(typeof ps[k] != "function" && this[ps[k]]){
28283                 var h = this[ps[k]];
28284                 h.el.removeAllListeners();
28285                 h.el.remove();
28286             }
28287         }
28288         if(removeEl){
28289             this.el.update("");
28290             this.el.remove();
28291         }
28292     }
28293 });
28294
28295 // private
28296 // hash to map config positions to true positions
28297 Roo.Resizable.positions = {
28298     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28299     hd: "hdrag"
28300 };
28301
28302 // private
28303 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28304     if(!this.tpl){
28305         // only initialize the template if resizable is used
28306         var tpl = Roo.DomHelper.createTemplate(
28307             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28308         );
28309         tpl.compile();
28310         Roo.Resizable.Handle.prototype.tpl = tpl;
28311     }
28312     this.position = pos;
28313     this.rz = rz;
28314     // show north drag fro topdra
28315     var handlepos = pos == 'hdrag' ? 'north' : pos;
28316     
28317     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28318     if (pos == 'hdrag') {
28319         this.el.setStyle('cursor', 'pointer');
28320     }
28321     this.el.unselectable();
28322     if(transparent){
28323         this.el.setOpacity(0);
28324     }
28325     this.el.on("mousedown", this.onMouseDown, this);
28326     if(!disableTrackOver){
28327         this.el.on("mouseover", this.onMouseOver, this);
28328         this.el.on("mouseout", this.onMouseOut, this);
28329     }
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle.prototype = {
28334     afterResize : function(rz){
28335         // do nothing
28336     },
28337     // private
28338     onMouseDown : function(e){
28339         this.rz.onMouseDown(this, e);
28340     },
28341     // private
28342     onMouseOver : function(e){
28343         this.rz.handleOver(this, e);
28344     },
28345     // private
28346     onMouseOut : function(e){
28347         this.rz.handleOut(this, e);
28348     }
28349 };/*
28350  * Based on:
28351  * Ext JS Library 1.1.1
28352  * Copyright(c) 2006-2007, Ext JS, LLC.
28353  *
28354  * Originally Released Under LGPL - original licence link has changed is not relivant.
28355  *
28356  * Fork - LGPL
28357  * <script type="text/javascript">
28358  */
28359
28360 /**
28361  * @class Roo.Editor
28362  * @extends Roo.Component
28363  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28364  * @constructor
28365  * Create a new Editor
28366  * @param {Roo.form.Field} field The Field object (or descendant)
28367  * @param {Object} config The config object
28368  */
28369 Roo.Editor = function(field, config){
28370     Roo.Editor.superclass.constructor.call(this, config);
28371     this.field = field;
28372     this.addEvents({
28373         /**
28374              * @event beforestartedit
28375              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28376              * false from the handler of this event.
28377              * @param {Editor} this
28378              * @param {Roo.Element} boundEl The underlying element bound to this editor
28379              * @param {Mixed} value The field value being set
28380              */
28381         "beforestartedit" : true,
28382         /**
28383              * @event startedit
28384              * Fires when this editor is displayed
28385              * @param {Roo.Element} boundEl The underlying element bound to this editor
28386              * @param {Mixed} value The starting field value
28387              */
28388         "startedit" : true,
28389         /**
28390              * @event beforecomplete
28391              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28392              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28393              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28394              * event will not fire since no edit actually occurred.
28395              * @param {Editor} this
28396              * @param {Mixed} value The current field value
28397              * @param {Mixed} startValue The original field value
28398              */
28399         "beforecomplete" : true,
28400         /**
28401              * @event complete
28402              * Fires after editing is complete and any changed value has been written to the underlying field.
28403              * @param {Editor} this
28404              * @param {Mixed} value The current field value
28405              * @param {Mixed} startValue The original field value
28406              */
28407         "complete" : true,
28408         /**
28409          * @event specialkey
28410          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28411          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28412          * @param {Roo.form.Field} this
28413          * @param {Roo.EventObject} e The event object
28414          */
28415         "specialkey" : true
28416     });
28417 };
28418
28419 Roo.extend(Roo.Editor, Roo.Component, {
28420     /**
28421      * @cfg {Boolean/String} autosize
28422      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28423      * or "height" to adopt the height only (defaults to false)
28424      */
28425     /**
28426      * @cfg {Boolean} revertInvalid
28427      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28428      * validation fails (defaults to true)
28429      */
28430     /**
28431      * @cfg {Boolean} ignoreNoChange
28432      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28433      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28434      * will never be ignored.
28435      */
28436     /**
28437      * @cfg {Boolean} hideEl
28438      * False to keep the bound element visible while the editor is displayed (defaults to true)
28439      */
28440     /**
28441      * @cfg {Mixed} value
28442      * The data value of the underlying field (defaults to "")
28443      */
28444     value : "",
28445     /**
28446      * @cfg {String} alignment
28447      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28448      */
28449     alignment: "c-c?",
28450     /**
28451      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28452      * for bottom-right shadow (defaults to "frame")
28453      */
28454     shadow : "frame",
28455     /**
28456      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28457      */
28458     constrain : false,
28459     /**
28460      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28461      */
28462     completeOnEnter : false,
28463     /**
28464      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28465      */
28466     cancelOnEsc : false,
28467     /**
28468      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28469      */
28470     updateEl : false,
28471
28472     // private
28473     onRender : function(ct, position){
28474         this.el = new Roo.Layer({
28475             shadow: this.shadow,
28476             cls: "x-editor",
28477             parentEl : ct,
28478             shim : this.shim,
28479             shadowOffset:4,
28480             id: this.id,
28481             constrain: this.constrain
28482         });
28483         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28484         if(this.field.msgTarget != 'title'){
28485             this.field.msgTarget = 'qtip';
28486         }
28487         this.field.render(this.el);
28488         if(Roo.isGecko){
28489             this.field.el.dom.setAttribute('autocomplete', 'off');
28490         }
28491         this.field.on("specialkey", this.onSpecialKey, this);
28492         if(this.swallowKeys){
28493             this.field.el.swallowEvent(['keydown','keypress']);
28494         }
28495         this.field.show();
28496         this.field.on("blur", this.onBlur, this);
28497         if(this.field.grow){
28498             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28499         }
28500     },
28501
28502     onSpecialKey : function(field, e)
28503     {
28504         //Roo.log('editor onSpecialKey');
28505         if(this.completeOnEnter && e.getKey() == e.ENTER){
28506             e.stopEvent();
28507             this.completeEdit();
28508             return;
28509         }
28510         // do not fire special key otherwise it might hide close the editor...
28511         if(e.getKey() == e.ENTER){    
28512             return;
28513         }
28514         if(this.cancelOnEsc && e.getKey() == e.ESC){
28515             this.cancelEdit();
28516             return;
28517         } 
28518         this.fireEvent('specialkey', field, e);
28519     
28520     },
28521
28522     /**
28523      * Starts the editing process and shows the editor.
28524      * @param {String/HTMLElement/Element} el The element to edit
28525      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28526       * to the innerHTML of el.
28527      */
28528     startEdit : function(el, value){
28529         if(this.editing){
28530             this.completeEdit();
28531         }
28532         this.boundEl = Roo.get(el);
28533         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28534         if(!this.rendered){
28535             this.render(this.parentEl || document.body);
28536         }
28537         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28538             return;
28539         }
28540         this.startValue = v;
28541         this.field.setValue(v);
28542         if(this.autoSize){
28543             var sz = this.boundEl.getSize();
28544             switch(this.autoSize){
28545                 case "width":
28546                 this.setSize(sz.width,  "");
28547                 break;
28548                 case "height":
28549                 this.setSize("",  sz.height);
28550                 break;
28551                 default:
28552                 this.setSize(sz.width,  sz.height);
28553             }
28554         }
28555         this.el.alignTo(this.boundEl, this.alignment);
28556         this.editing = true;
28557         if(Roo.QuickTips){
28558             Roo.QuickTips.disable();
28559         }
28560         this.show();
28561     },
28562
28563     /**
28564      * Sets the height and width of this editor.
28565      * @param {Number} width The new width
28566      * @param {Number} height The new height
28567      */
28568     setSize : function(w, h){
28569         this.field.setSize(w, h);
28570         if(this.el){
28571             this.el.sync();
28572         }
28573     },
28574
28575     /**
28576      * Realigns the editor to the bound field based on the current alignment config value.
28577      */
28578     realign : function(){
28579         this.el.alignTo(this.boundEl, this.alignment);
28580     },
28581
28582     /**
28583      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28584      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28585      */
28586     completeEdit : function(remainVisible){
28587         if(!this.editing){
28588             return;
28589         }
28590         var v = this.getValue();
28591         if(this.revertInvalid !== false && !this.field.isValid()){
28592             v = this.startValue;
28593             this.cancelEdit(true);
28594         }
28595         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28596             this.editing = false;
28597             this.hide();
28598             return;
28599         }
28600         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28601             this.editing = false;
28602             if(this.updateEl && this.boundEl){
28603                 this.boundEl.update(v);
28604             }
28605             if(remainVisible !== true){
28606                 this.hide();
28607             }
28608             this.fireEvent("complete", this, v, this.startValue);
28609         }
28610     },
28611
28612     // private
28613     onShow : function(){
28614         this.el.show();
28615         if(this.hideEl !== false){
28616             this.boundEl.hide();
28617         }
28618         this.field.show();
28619         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28620             this.fixIEFocus = true;
28621             this.deferredFocus.defer(50, this);
28622         }else{
28623             this.field.focus();
28624         }
28625         this.fireEvent("startedit", this.boundEl, this.startValue);
28626     },
28627
28628     deferredFocus : function(){
28629         if(this.editing){
28630             this.field.focus();
28631         }
28632     },
28633
28634     /**
28635      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28636      * reverted to the original starting value.
28637      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28638      * cancel (defaults to false)
28639      */
28640     cancelEdit : function(remainVisible){
28641         if(this.editing){
28642             this.setValue(this.startValue);
28643             if(remainVisible !== true){
28644                 this.hide();
28645             }
28646         }
28647     },
28648
28649     // private
28650     onBlur : function(){
28651         if(this.allowBlur !== true && this.editing){
28652             this.completeEdit();
28653         }
28654     },
28655
28656     // private
28657     onHide : function(){
28658         if(this.editing){
28659             this.completeEdit();
28660             return;
28661         }
28662         this.field.blur();
28663         if(this.field.collapse){
28664             this.field.collapse();
28665         }
28666         this.el.hide();
28667         if(this.hideEl !== false){
28668             this.boundEl.show();
28669         }
28670         if(Roo.QuickTips){
28671             Roo.QuickTips.enable();
28672         }
28673     },
28674
28675     /**
28676      * Sets the data value of the editor
28677      * @param {Mixed} value Any valid value supported by the underlying field
28678      */
28679     setValue : function(v){
28680         this.field.setValue(v);
28681     },
28682
28683     /**
28684      * Gets the data value of the editor
28685      * @return {Mixed} The data value
28686      */
28687     getValue : function(){
28688         return this.field.getValue();
28689     }
28690 });/*
28691  * Based on:
28692  * Ext JS Library 1.1.1
28693  * Copyright(c) 2006-2007, Ext JS, LLC.
28694  *
28695  * Originally Released Under LGPL - original licence link has changed is not relivant.
28696  *
28697  * Fork - LGPL
28698  * <script type="text/javascript">
28699  */
28700  
28701 /**
28702  * @class Roo.BasicDialog
28703  * @extends Roo.util.Observable
28704  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28705  * <pre><code>
28706 var dlg = new Roo.BasicDialog("my-dlg", {
28707     height: 200,
28708     width: 300,
28709     minHeight: 100,
28710     minWidth: 150,
28711     modal: true,
28712     proxyDrag: true,
28713     shadow: true
28714 });
28715 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28716 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28717 dlg.addButton('Cancel', dlg.hide, dlg);
28718 dlg.show();
28719 </code></pre>
28720   <b>A Dialog should always be a direct child of the body element.</b>
28721  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28722  * @cfg {String} title Default text to display in the title bar (defaults to null)
28723  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28724  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28725  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28726  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28727  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28728  * (defaults to null with no animation)
28729  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28730  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28731  * property for valid values (defaults to 'all')
28732  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28733  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28734  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28735  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28736  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28737  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28738  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28739  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28740  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28741  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28742  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28743  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28744  * draggable = true (defaults to false)
28745  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28746  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28747  * shadow (defaults to false)
28748  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28749  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28750  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28751  * @cfg {Array} buttons Array of buttons
28752  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28753  * @constructor
28754  * Create a new BasicDialog.
28755  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28756  * @param {Object} config Configuration options
28757  */
28758 Roo.BasicDialog = function(el, config){
28759     this.el = Roo.get(el);
28760     var dh = Roo.DomHelper;
28761     if(!this.el && config && config.autoCreate){
28762         if(typeof config.autoCreate == "object"){
28763             if(!config.autoCreate.id){
28764                 config.autoCreate.id = el;
28765             }
28766             this.el = dh.append(document.body,
28767                         config.autoCreate, true);
28768         }else{
28769             this.el = dh.append(document.body,
28770                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28771         }
28772     }
28773     el = this.el;
28774     el.setDisplayed(true);
28775     el.hide = this.hideAction;
28776     this.id = el.id;
28777     el.addClass("x-dlg");
28778
28779     Roo.apply(this, config);
28780
28781     this.proxy = el.createProxy("x-dlg-proxy");
28782     this.proxy.hide = this.hideAction;
28783     this.proxy.setOpacity(.5);
28784     this.proxy.hide();
28785
28786     if(config.width){
28787         el.setWidth(config.width);
28788     }
28789     if(config.height){
28790         el.setHeight(config.height);
28791     }
28792     this.size = el.getSize();
28793     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28794         this.xy = [config.x,config.y];
28795     }else{
28796         this.xy = el.getCenterXY(true);
28797     }
28798     /** The header element @type Roo.Element */
28799     this.header = el.child("> .x-dlg-hd");
28800     /** The body element @type Roo.Element */
28801     this.body = el.child("> .x-dlg-bd");
28802     /** The footer element @type Roo.Element */
28803     this.footer = el.child("> .x-dlg-ft");
28804
28805     if(!this.header){
28806         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28807     }
28808     if(!this.body){
28809         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28810     }
28811
28812     this.header.unselectable();
28813     if(this.title){
28814         this.header.update(this.title);
28815     }
28816     // this element allows the dialog to be focused for keyboard event
28817     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28818     this.focusEl.swallowEvent("click", true);
28819
28820     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28821
28822     // wrap the body and footer for special rendering
28823     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28824     if(this.footer){
28825         this.bwrap.dom.appendChild(this.footer.dom);
28826     }
28827
28828     this.bg = this.el.createChild({
28829         tag: "div", cls:"x-dlg-bg",
28830         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28831     });
28832     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28833
28834
28835     if(this.autoScroll !== false && !this.autoTabs){
28836         this.body.setStyle("overflow", "auto");
28837     }
28838
28839     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28840
28841     if(this.closable !== false){
28842         this.el.addClass("x-dlg-closable");
28843         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28844         this.close.on("click", this.closeClick, this);
28845         this.close.addClassOnOver("x-dlg-close-over");
28846     }
28847     if(this.collapsible !== false){
28848         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28849         this.collapseBtn.on("click", this.collapseClick, this);
28850         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28851         this.header.on("dblclick", this.collapseClick, this);
28852     }
28853     if(this.resizable !== false){
28854         this.el.addClass("x-dlg-resizable");
28855         this.resizer = new Roo.Resizable(el, {
28856             minWidth: this.minWidth || 80,
28857             minHeight:this.minHeight || 80,
28858             handles: this.resizeHandles || "all",
28859             pinned: true
28860         });
28861         this.resizer.on("beforeresize", this.beforeResize, this);
28862         this.resizer.on("resize", this.onResize, this);
28863     }
28864     if(this.draggable !== false){
28865         el.addClass("x-dlg-draggable");
28866         if (!this.proxyDrag) {
28867             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28868         }
28869         else {
28870             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28871         }
28872         dd.setHandleElId(this.header.id);
28873         dd.endDrag = this.endMove.createDelegate(this);
28874         dd.startDrag = this.startMove.createDelegate(this);
28875         dd.onDrag = this.onDrag.createDelegate(this);
28876         dd.scroll = false;
28877         this.dd = dd;
28878     }
28879     if(this.modal){
28880         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28881         this.mask.enableDisplayMode("block");
28882         this.mask.hide();
28883         this.el.addClass("x-dlg-modal");
28884     }
28885     if(this.shadow){
28886         this.shadow = new Roo.Shadow({
28887             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28888             offset : this.shadowOffset
28889         });
28890     }else{
28891         this.shadowOffset = 0;
28892     }
28893     if(Roo.useShims && this.shim !== false){
28894         this.shim = this.el.createShim();
28895         this.shim.hide = this.hideAction;
28896         this.shim.hide();
28897     }else{
28898         this.shim = false;
28899     }
28900     if(this.autoTabs){
28901         this.initTabs();
28902     }
28903     if (this.buttons) { 
28904         var bts= this.buttons;
28905         this.buttons = [];
28906         Roo.each(bts, function(b) {
28907             this.addButton(b);
28908         }, this);
28909     }
28910     
28911     
28912     this.addEvents({
28913         /**
28914          * @event keydown
28915          * Fires when a key is pressed
28916          * @param {Roo.BasicDialog} this
28917          * @param {Roo.EventObject} e
28918          */
28919         "keydown" : true,
28920         /**
28921          * @event move
28922          * Fires when this dialog is moved by the user.
28923          * @param {Roo.BasicDialog} this
28924          * @param {Number} x The new page X
28925          * @param {Number} y The new page Y
28926          */
28927         "move" : true,
28928         /**
28929          * @event resize
28930          * Fires when this dialog is resized by the user.
28931          * @param {Roo.BasicDialog} this
28932          * @param {Number} width The new width
28933          * @param {Number} height The new height
28934          */
28935         "resize" : true,
28936         /**
28937          * @event beforehide
28938          * Fires before this dialog is hidden.
28939          * @param {Roo.BasicDialog} this
28940          */
28941         "beforehide" : true,
28942         /**
28943          * @event hide
28944          * Fires when this dialog is hidden.
28945          * @param {Roo.BasicDialog} this
28946          */
28947         "hide" : true,
28948         /**
28949          * @event beforeshow
28950          * Fires before this dialog is shown.
28951          * @param {Roo.BasicDialog} this
28952          */
28953         "beforeshow" : true,
28954         /**
28955          * @event show
28956          * Fires when this dialog is shown.
28957          * @param {Roo.BasicDialog} this
28958          */
28959         "show" : true
28960     });
28961     el.on("keydown", this.onKeyDown, this);
28962     el.on("mousedown", this.toFront, this);
28963     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28964     this.el.hide();
28965     Roo.DialogManager.register(this);
28966     Roo.BasicDialog.superclass.constructor.call(this);
28967 };
28968
28969 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28970     shadowOffset: Roo.isIE ? 6 : 5,
28971     minHeight: 80,
28972     minWidth: 200,
28973     minButtonWidth: 75,
28974     defaultButton: null,
28975     buttonAlign: "right",
28976     tabTag: 'div',
28977     firstShow: true,
28978
28979     /**
28980      * Sets the dialog title text
28981      * @param {String} text The title text to display
28982      * @return {Roo.BasicDialog} this
28983      */
28984     setTitle : function(text){
28985         this.header.update(text);
28986         return this;
28987     },
28988
28989     // private
28990     closeClick : function(){
28991         this.hide();
28992     },
28993
28994     // private
28995     collapseClick : function(){
28996         this[this.collapsed ? "expand" : "collapse"]();
28997     },
28998
28999     /**
29000      * Collapses the dialog to its minimized state (only the title bar is visible).
29001      * Equivalent to the user clicking the collapse dialog button.
29002      */
29003     collapse : function(){
29004         if(!this.collapsed){
29005             this.collapsed = true;
29006             this.el.addClass("x-dlg-collapsed");
29007             this.restoreHeight = this.el.getHeight();
29008             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29009         }
29010     },
29011
29012     /**
29013      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29014      * clicking the expand dialog button.
29015      */
29016     expand : function(){
29017         if(this.collapsed){
29018             this.collapsed = false;
29019             this.el.removeClass("x-dlg-collapsed");
29020             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29021         }
29022     },
29023
29024     /**
29025      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29026      * @return {Roo.TabPanel} The tabs component
29027      */
29028     initTabs : function(){
29029         var tabs = this.getTabs();
29030         while(tabs.getTab(0)){
29031             tabs.removeTab(0);
29032         }
29033         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29034             var dom = el.dom;
29035             tabs.addTab(Roo.id(dom), dom.title);
29036             dom.title = "";
29037         });
29038         tabs.activate(0);
29039         return tabs;
29040     },
29041
29042     // private
29043     beforeResize : function(){
29044         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29045     },
29046
29047     // private
29048     onResize : function(){
29049         this.refreshSize();
29050         this.syncBodyHeight();
29051         this.adjustAssets();
29052         this.focus();
29053         this.fireEvent("resize", this, this.size.width, this.size.height);
29054     },
29055
29056     // private
29057     onKeyDown : function(e){
29058         if(this.isVisible()){
29059             this.fireEvent("keydown", this, e);
29060         }
29061     },
29062
29063     /**
29064      * Resizes the dialog.
29065      * @param {Number} width
29066      * @param {Number} height
29067      * @return {Roo.BasicDialog} this
29068      */
29069     resizeTo : function(width, height){
29070         this.el.setSize(width, height);
29071         this.size = {width: width, height: height};
29072         this.syncBodyHeight();
29073         if(this.fixedcenter){
29074             this.center();
29075         }
29076         if(this.isVisible()){
29077             this.constrainXY();
29078             this.adjustAssets();
29079         }
29080         this.fireEvent("resize", this, width, height);
29081         return this;
29082     },
29083
29084
29085     /**
29086      * Resizes the dialog to fit the specified content size.
29087      * @param {Number} width
29088      * @param {Number} height
29089      * @return {Roo.BasicDialog} this
29090      */
29091     setContentSize : function(w, h){
29092         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29093         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29094         //if(!this.el.isBorderBox()){
29095             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29096             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29097         //}
29098         if(this.tabs){
29099             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29100             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29101         }
29102         this.resizeTo(w, h);
29103         return this;
29104     },
29105
29106     /**
29107      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29108      * executed in response to a particular key being pressed while the dialog is active.
29109      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29110      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29111      * @param {Function} fn The function to call
29112      * @param {Object} scope (optional) The scope of the function
29113      * @return {Roo.BasicDialog} this
29114      */
29115     addKeyListener : function(key, fn, scope){
29116         var keyCode, shift, ctrl, alt;
29117         if(typeof key == "object" && !(key instanceof Array)){
29118             keyCode = key["key"];
29119             shift = key["shift"];
29120             ctrl = key["ctrl"];
29121             alt = key["alt"];
29122         }else{
29123             keyCode = key;
29124         }
29125         var handler = function(dlg, e){
29126             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29127                 var k = e.getKey();
29128                 if(keyCode instanceof Array){
29129                     for(var i = 0, len = keyCode.length; i < len; i++){
29130                         if(keyCode[i] == k){
29131                           fn.call(scope || window, dlg, k, e);
29132                           return;
29133                         }
29134                     }
29135                 }else{
29136                     if(k == keyCode){
29137                         fn.call(scope || window, dlg, k, e);
29138                     }
29139                 }
29140             }
29141         };
29142         this.on("keydown", handler);
29143         return this;
29144     },
29145
29146     /**
29147      * Returns the TabPanel component (creates it if it doesn't exist).
29148      * Note: If you wish to simply check for the existence of tabs without creating them,
29149      * check for a null 'tabs' property.
29150      * @return {Roo.TabPanel} The tabs component
29151      */
29152     getTabs : function(){
29153         if(!this.tabs){
29154             this.el.addClass("x-dlg-auto-tabs");
29155             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29156             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29157         }
29158         return this.tabs;
29159     },
29160
29161     /**
29162      * Adds a button to the footer section of the dialog.
29163      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29164      * object or a valid Roo.DomHelper element config
29165      * @param {Function} handler The function called when the button is clicked
29166      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29167      * @return {Roo.Button} The new button
29168      */
29169     addButton : function(config, handler, scope){
29170         var dh = Roo.DomHelper;
29171         if(!this.footer){
29172             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29173         }
29174         if(!this.btnContainer){
29175             var tb = this.footer.createChild({
29176
29177                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29178                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29179             }, null, true);
29180             this.btnContainer = tb.firstChild.firstChild.firstChild;
29181         }
29182         var bconfig = {
29183             handler: handler,
29184             scope: scope,
29185             minWidth: this.minButtonWidth,
29186             hideParent:true
29187         };
29188         if(typeof config == "string"){
29189             bconfig.text = config;
29190         }else{
29191             if(config.tag){
29192                 bconfig.dhconfig = config;
29193             }else{
29194                 Roo.apply(bconfig, config);
29195             }
29196         }
29197         var fc = false;
29198         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29199             bconfig.position = Math.max(0, bconfig.position);
29200             fc = this.btnContainer.childNodes[bconfig.position];
29201         }
29202          
29203         var btn = new Roo.Button(
29204             fc ? 
29205                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29206                 : this.btnContainer.appendChild(document.createElement("td")),
29207             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29208             bconfig
29209         );
29210         this.syncBodyHeight();
29211         if(!this.buttons){
29212             /**
29213              * Array of all the buttons that have been added to this dialog via addButton
29214              * @type Array
29215              */
29216             this.buttons = [];
29217         }
29218         this.buttons.push(btn);
29219         return btn;
29220     },
29221
29222     /**
29223      * Sets the default button to be focused when the dialog is displayed.
29224      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29225      * @return {Roo.BasicDialog} this
29226      */
29227     setDefaultButton : function(btn){
29228         this.defaultButton = btn;
29229         return this;
29230     },
29231
29232     // private
29233     getHeaderFooterHeight : function(safe){
29234         var height = 0;
29235         if(this.header){
29236            height += this.header.getHeight();
29237         }
29238         if(this.footer){
29239            var fm = this.footer.getMargins();
29240             height += (this.footer.getHeight()+fm.top+fm.bottom);
29241         }
29242         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29243         height += this.centerBg.getPadding("tb");
29244         return height;
29245     },
29246
29247     // private
29248     syncBodyHeight : function(){
29249         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29250         var height = this.size.height - this.getHeaderFooterHeight(false);
29251         bd.setHeight(height-bd.getMargins("tb"));
29252         var hh = this.header.getHeight();
29253         var h = this.size.height-hh;
29254         cb.setHeight(h);
29255         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29256         bw.setHeight(h-cb.getPadding("tb"));
29257         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29258         bd.setWidth(bw.getWidth(true));
29259         if(this.tabs){
29260             this.tabs.syncHeight();
29261             if(Roo.isIE){
29262                 this.tabs.el.repaint();
29263             }
29264         }
29265     },
29266
29267     /**
29268      * Restores the previous state of the dialog if Roo.state is configured.
29269      * @return {Roo.BasicDialog} this
29270      */
29271     restoreState : function(){
29272         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29273         if(box && box.width){
29274             this.xy = [box.x, box.y];
29275             this.resizeTo(box.width, box.height);
29276         }
29277         return this;
29278     },
29279
29280     // private
29281     beforeShow : function(){
29282         this.expand();
29283         if(this.fixedcenter){
29284             this.xy = this.el.getCenterXY(true);
29285         }
29286         if(this.modal){
29287             Roo.get(document.body).addClass("x-body-masked");
29288             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29289             this.mask.show();
29290         }
29291         this.constrainXY();
29292     },
29293
29294     // private
29295     animShow : function(){
29296         var b = Roo.get(this.animateTarget).getBox();
29297         this.proxy.setSize(b.width, b.height);
29298         this.proxy.setLocation(b.x, b.y);
29299         this.proxy.show();
29300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29301                     true, .35, this.showEl.createDelegate(this));
29302     },
29303
29304     /**
29305      * Shows the dialog.
29306      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29307      * @return {Roo.BasicDialog} this
29308      */
29309     show : function(animateTarget){
29310         if (this.fireEvent("beforeshow", this) === false){
29311             return;
29312         }
29313         if(this.syncHeightBeforeShow){
29314             this.syncBodyHeight();
29315         }else if(this.firstShow){
29316             this.firstShow = false;
29317             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29318         }
29319         this.animateTarget = animateTarget || this.animateTarget;
29320         if(!this.el.isVisible()){
29321             this.beforeShow();
29322             if(this.animateTarget && Roo.get(this.animateTarget)){
29323                 this.animShow();
29324             }else{
29325                 this.showEl();
29326             }
29327         }
29328         return this;
29329     },
29330
29331     // private
29332     showEl : function(){
29333         this.proxy.hide();
29334         this.el.setXY(this.xy);
29335         this.el.show();
29336         this.adjustAssets(true);
29337         this.toFront();
29338         this.focus();
29339         // IE peekaboo bug - fix found by Dave Fenwick
29340         if(Roo.isIE){
29341             this.el.repaint();
29342         }
29343         this.fireEvent("show", this);
29344     },
29345
29346     /**
29347      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29348      * dialog itself will receive focus.
29349      */
29350     focus : function(){
29351         if(this.defaultButton){
29352             this.defaultButton.focus();
29353         }else{
29354             this.focusEl.focus();
29355         }
29356     },
29357
29358     // private
29359     constrainXY : function(){
29360         if(this.constraintoviewport !== false){
29361             if(!this.viewSize){
29362                 if(this.container){
29363                     var s = this.container.getSize();
29364                     this.viewSize = [s.width, s.height];
29365                 }else{
29366                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29367                 }
29368             }
29369             var s = Roo.get(this.container||document).getScroll();
29370
29371             var x = this.xy[0], y = this.xy[1];
29372             var w = this.size.width, h = this.size.height;
29373             var vw = this.viewSize[0], vh = this.viewSize[1];
29374             // only move it if it needs it
29375             var moved = false;
29376             // first validate right/bottom
29377             if(x + w > vw+s.left){
29378                 x = vw - w;
29379                 moved = true;
29380             }
29381             if(y + h > vh+s.top){
29382                 y = vh - h;
29383                 moved = true;
29384             }
29385             // then make sure top/left isn't negative
29386             if(x < s.left){
29387                 x = s.left;
29388                 moved = true;
29389             }
29390             if(y < s.top){
29391                 y = s.top;
29392                 moved = true;
29393             }
29394             if(moved){
29395                 // cache xy
29396                 this.xy = [x, y];
29397                 if(this.isVisible()){
29398                     this.el.setLocation(x, y);
29399                     this.adjustAssets();
29400                 }
29401             }
29402         }
29403     },
29404
29405     // private
29406     onDrag : function(){
29407         if(!this.proxyDrag){
29408             this.xy = this.el.getXY();
29409             this.adjustAssets();
29410         }
29411     },
29412
29413     // private
29414     adjustAssets : function(doShow){
29415         var x = this.xy[0], y = this.xy[1];
29416         var w = this.size.width, h = this.size.height;
29417         if(doShow === true){
29418             if(this.shadow){
29419                 this.shadow.show(this.el);
29420             }
29421             if(this.shim){
29422                 this.shim.show();
29423             }
29424         }
29425         if(this.shadow && this.shadow.isVisible()){
29426             this.shadow.show(this.el);
29427         }
29428         if(this.shim && this.shim.isVisible()){
29429             this.shim.setBounds(x, y, w, h);
29430         }
29431     },
29432
29433     // private
29434     adjustViewport : function(w, h){
29435         if(!w || !h){
29436             w = Roo.lib.Dom.getViewWidth();
29437             h = Roo.lib.Dom.getViewHeight();
29438         }
29439         // cache the size
29440         this.viewSize = [w, h];
29441         if(this.modal && this.mask.isVisible()){
29442             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29443             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29444         }
29445         if(this.isVisible()){
29446             this.constrainXY();
29447         }
29448     },
29449
29450     /**
29451      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29452      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29453      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29454      */
29455     destroy : function(removeEl){
29456         if(this.isVisible()){
29457             this.animateTarget = null;
29458             this.hide();
29459         }
29460         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29461         if(this.tabs){
29462             this.tabs.destroy(removeEl);
29463         }
29464         Roo.destroy(
29465              this.shim,
29466              this.proxy,
29467              this.resizer,
29468              this.close,
29469              this.mask
29470         );
29471         if(this.dd){
29472             this.dd.unreg();
29473         }
29474         if(this.buttons){
29475            for(var i = 0, len = this.buttons.length; i < len; i++){
29476                this.buttons[i].destroy();
29477            }
29478         }
29479         this.el.removeAllListeners();
29480         if(removeEl === true){
29481             this.el.update("");
29482             this.el.remove();
29483         }
29484         Roo.DialogManager.unregister(this);
29485     },
29486
29487     // private
29488     startMove : function(){
29489         if(this.proxyDrag){
29490             this.proxy.show();
29491         }
29492         if(this.constraintoviewport !== false){
29493             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29494         }
29495     },
29496
29497     // private
29498     endMove : function(){
29499         if(!this.proxyDrag){
29500             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29501         }else{
29502             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29503             this.proxy.hide();
29504         }
29505         this.refreshSize();
29506         this.adjustAssets();
29507         this.focus();
29508         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29509     },
29510
29511     /**
29512      * Brings this dialog to the front of any other visible dialogs
29513      * @return {Roo.BasicDialog} this
29514      */
29515     toFront : function(){
29516         Roo.DialogManager.bringToFront(this);
29517         return this;
29518     },
29519
29520     /**
29521      * Sends this dialog to the back (under) of any other visible dialogs
29522      * @return {Roo.BasicDialog} this
29523      */
29524     toBack : function(){
29525         Roo.DialogManager.sendToBack(this);
29526         return this;
29527     },
29528
29529     /**
29530      * Centers this dialog in the viewport
29531      * @return {Roo.BasicDialog} this
29532      */
29533     center : function(){
29534         var xy = this.el.getCenterXY(true);
29535         this.moveTo(xy[0], xy[1]);
29536         return this;
29537     },
29538
29539     /**
29540      * Moves the dialog's top-left corner to the specified point
29541      * @param {Number} x
29542      * @param {Number} y
29543      * @return {Roo.BasicDialog} this
29544      */
29545     moveTo : function(x, y){
29546         this.xy = [x,y];
29547         if(this.isVisible()){
29548             this.el.setXY(this.xy);
29549             this.adjustAssets();
29550         }
29551         return this;
29552     },
29553
29554     /**
29555      * Aligns the dialog to the specified element
29556      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29557      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29558      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29559      * @return {Roo.BasicDialog} this
29560      */
29561     alignTo : function(element, position, offsets){
29562         this.xy = this.el.getAlignToXY(element, position, offsets);
29563         if(this.isVisible()){
29564             this.el.setXY(this.xy);
29565             this.adjustAssets();
29566         }
29567         return this;
29568     },
29569
29570     /**
29571      * Anchors an element to another element and realigns it when the window is resized.
29572      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29573      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29574      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29575      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29576      * is a number, it is used as the buffer delay (defaults to 50ms).
29577      * @return {Roo.BasicDialog} this
29578      */
29579     anchorTo : function(el, alignment, offsets, monitorScroll){
29580         var action = function(){
29581             this.alignTo(el, alignment, offsets);
29582         };
29583         Roo.EventManager.onWindowResize(action, this);
29584         var tm = typeof monitorScroll;
29585         if(tm != 'undefined'){
29586             Roo.EventManager.on(window, 'scroll', action, this,
29587                 {buffer: tm == 'number' ? monitorScroll : 50});
29588         }
29589         action.call(this);
29590         return this;
29591     },
29592
29593     /**
29594      * Returns true if the dialog is visible
29595      * @return {Boolean}
29596      */
29597     isVisible : function(){
29598         return this.el.isVisible();
29599     },
29600
29601     // private
29602     animHide : function(callback){
29603         var b = Roo.get(this.animateTarget).getBox();
29604         this.proxy.show();
29605         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29606         this.el.hide();
29607         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29608                     this.hideEl.createDelegate(this, [callback]));
29609     },
29610
29611     /**
29612      * Hides the dialog.
29613      * @param {Function} callback (optional) Function to call when the dialog is hidden
29614      * @return {Roo.BasicDialog} this
29615      */
29616     hide : function(callback){
29617         if (this.fireEvent("beforehide", this) === false){
29618             return;
29619         }
29620         if(this.shadow){
29621             this.shadow.hide();
29622         }
29623         if(this.shim) {
29624           this.shim.hide();
29625         }
29626         // sometimes animateTarget seems to get set.. causing problems...
29627         // this just double checks..
29628         if(this.animateTarget && Roo.get(this.animateTarget)) {
29629            this.animHide(callback);
29630         }else{
29631             this.el.hide();
29632             this.hideEl(callback);
29633         }
29634         return this;
29635     },
29636
29637     // private
29638     hideEl : function(callback){
29639         this.proxy.hide();
29640         if(this.modal){
29641             this.mask.hide();
29642             Roo.get(document.body).removeClass("x-body-masked");
29643         }
29644         this.fireEvent("hide", this);
29645         if(typeof callback == "function"){
29646             callback();
29647         }
29648     },
29649
29650     // private
29651     hideAction : function(){
29652         this.setLeft("-10000px");
29653         this.setTop("-10000px");
29654         this.setStyle("visibility", "hidden");
29655     },
29656
29657     // private
29658     refreshSize : function(){
29659         this.size = this.el.getSize();
29660         this.xy = this.el.getXY();
29661         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29662     },
29663
29664     // private
29665     // z-index is managed by the DialogManager and may be overwritten at any time
29666     setZIndex : function(index){
29667         if(this.modal){
29668             this.mask.setStyle("z-index", index);
29669         }
29670         if(this.shim){
29671             this.shim.setStyle("z-index", ++index);
29672         }
29673         if(this.shadow){
29674             this.shadow.setZIndex(++index);
29675         }
29676         this.el.setStyle("z-index", ++index);
29677         if(this.proxy){
29678             this.proxy.setStyle("z-index", ++index);
29679         }
29680         if(this.resizer){
29681             this.resizer.proxy.setStyle("z-index", ++index);
29682         }
29683
29684         this.lastZIndex = index;
29685     },
29686
29687     /**
29688      * Returns the element for this dialog
29689      * @return {Roo.Element} The underlying dialog Element
29690      */
29691     getEl : function(){
29692         return this.el;
29693     }
29694 });
29695
29696 /**
29697  * @class Roo.DialogManager
29698  * Provides global access to BasicDialogs that have been created and
29699  * support for z-indexing (layering) multiple open dialogs.
29700  */
29701 Roo.DialogManager = function(){
29702     var list = {};
29703     var accessList = [];
29704     var front = null;
29705
29706     // private
29707     var sortDialogs = function(d1, d2){
29708         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29709     };
29710
29711     // private
29712     var orderDialogs = function(){
29713         accessList.sort(sortDialogs);
29714         var seed = Roo.DialogManager.zseed;
29715         for(var i = 0, len = accessList.length; i < len; i++){
29716             var dlg = accessList[i];
29717             if(dlg){
29718                 dlg.setZIndex(seed + (i*10));
29719             }
29720         }
29721     };
29722
29723     return {
29724         /**
29725          * The starting z-index for BasicDialogs (defaults to 9000)
29726          * @type Number The z-index value
29727          */
29728         zseed : 9000,
29729
29730         // private
29731         register : function(dlg){
29732             list[dlg.id] = dlg;
29733             accessList.push(dlg);
29734         },
29735
29736         // private
29737         unregister : function(dlg){
29738             delete list[dlg.id];
29739             var i=0;
29740             var len=0;
29741             if(!accessList.indexOf){
29742                 for(  i = 0, len = accessList.length; i < len; i++){
29743                     if(accessList[i] == dlg){
29744                         accessList.splice(i, 1);
29745                         return;
29746                     }
29747                 }
29748             }else{
29749                  i = accessList.indexOf(dlg);
29750                 if(i != -1){
29751                     accessList.splice(i, 1);
29752                 }
29753             }
29754         },
29755
29756         /**
29757          * Gets a registered dialog by id
29758          * @param {String/Object} id The id of the dialog or a dialog
29759          * @return {Roo.BasicDialog} this
29760          */
29761         get : function(id){
29762             return typeof id == "object" ? id : list[id];
29763         },
29764
29765         /**
29766          * Brings the specified dialog to the front
29767          * @param {String/Object} dlg The id of the dialog or a dialog
29768          * @return {Roo.BasicDialog} this
29769          */
29770         bringToFront : function(dlg){
29771             dlg = this.get(dlg);
29772             if(dlg != front){
29773                 front = dlg;
29774                 dlg._lastAccess = new Date().getTime();
29775                 orderDialogs();
29776             }
29777             return dlg;
29778         },
29779
29780         /**
29781          * Sends the specified dialog to the back
29782          * @param {String/Object} dlg The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         sendToBack : function(dlg){
29786             dlg = this.get(dlg);
29787             dlg._lastAccess = -(new Date().getTime());
29788             orderDialogs();
29789             return dlg;
29790         },
29791
29792         /**
29793          * Hides all dialogs
29794          */
29795         hideAll : function(){
29796             for(var id in list){
29797                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29798                     list[id].hide();
29799                 }
29800             }
29801         }
29802     };
29803 }();
29804
29805 /**
29806  * @class Roo.LayoutDialog
29807  * @extends Roo.BasicDialog
29808  * Dialog which provides adjustments for working with a layout in a Dialog.
29809  * Add your necessary layout config options to the dialog's config.<br>
29810  * Example usage (including a nested layout):
29811  * <pre><code>
29812 if(!dialog){
29813     dialog = new Roo.LayoutDialog("download-dlg", {
29814         modal: true,
29815         width:600,
29816         height:450,
29817         shadow:true,
29818         minWidth:500,
29819         minHeight:350,
29820         autoTabs:true,
29821         proxyDrag:true,
29822         // layout config merges with the dialog config
29823         center:{
29824             tabPosition: "top",
29825             alwaysShowTabs: true
29826         }
29827     });
29828     dialog.addKeyListener(27, dialog.hide, dialog);
29829     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29830     dialog.addButton("Build It!", this.getDownload, this);
29831
29832     // we can even add nested layouts
29833     var innerLayout = new Roo.BorderLayout("dl-inner", {
29834         east: {
29835             initialSize: 200,
29836             autoScroll:true,
29837             split:true
29838         },
29839         center: {
29840             autoScroll:true
29841         }
29842     });
29843     innerLayout.beginUpdate();
29844     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29845     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29846     innerLayout.endUpdate(true);
29847
29848     var layout = dialog.getLayout();
29849     layout.beginUpdate();
29850     layout.add("center", new Roo.ContentPanel("standard-panel",
29851                         {title: "Download the Source", fitToFrame:true}));
29852     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29853                {title: "Build your own roo.js"}));
29854     layout.getRegion("center").showPanel(sp);
29855     layout.endUpdate();
29856 }
29857 </code></pre>
29858     * @constructor
29859     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29860     * @param {Object} config configuration options
29861   */
29862 Roo.LayoutDialog = function(el, cfg){
29863     
29864     var config=  cfg;
29865     if (typeof(cfg) == 'undefined') {
29866         config = Roo.apply({}, el);
29867         // not sure why we use documentElement here.. - it should always be body.
29868         // IE7 borks horribly if we use documentElement.
29869         // webkit also does not like documentElement - it creates a body element...
29870         el = Roo.get( document.body || document.documentElement ).createChild();
29871         //config.autoCreate = true;
29872     }
29873     
29874     
29875     config.autoTabs = false;
29876     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29877     this.body.setStyle({overflow:"hidden", position:"relative"});
29878     this.layout = new Roo.BorderLayout(this.body.dom, config);
29879     this.layout.monitorWindowResize = false;
29880     this.el.addClass("x-dlg-auto-layout");
29881     // fix case when center region overwrites center function
29882     this.center = Roo.BasicDialog.prototype.center;
29883     this.on("show", this.layout.layout, this.layout, true);
29884     if (config.items) {
29885         var xitems = config.items;
29886         delete config.items;
29887         Roo.each(xitems, this.addxtype, this);
29888     }
29889     
29890     
29891 };
29892 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29893     /**
29894      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29895      * @deprecated
29896      */
29897     endUpdate : function(){
29898         this.layout.endUpdate();
29899     },
29900
29901     /**
29902      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29903      *  @deprecated
29904      */
29905     beginUpdate : function(){
29906         this.layout.beginUpdate();
29907     },
29908
29909     /**
29910      * Get the BorderLayout for this dialog
29911      * @return {Roo.BorderLayout}
29912      */
29913     getLayout : function(){
29914         return this.layout;
29915     },
29916
29917     showEl : function(){
29918         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29919         if(Roo.isIE7){
29920             this.layout.layout();
29921         }
29922     },
29923
29924     // private
29925     // Use the syncHeightBeforeShow config option to control this automatically
29926     syncBodyHeight : function(){
29927         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29928         if(this.layout){this.layout.layout();}
29929     },
29930     
29931       /**
29932      * Add an xtype element (actually adds to the layout.)
29933      * @return {Object} xdata xtype object data.
29934      */
29935     
29936     addxtype : function(c) {
29937         return this.layout.addxtype(c);
29938     }
29939 });/*
29940  * Based on:
29941  * Ext JS Library 1.1.1
29942  * Copyright(c) 2006-2007, Ext JS, LLC.
29943  *
29944  * Originally Released Under LGPL - original licence link has changed is not relivant.
29945  *
29946  * Fork - LGPL
29947  * <script type="text/javascript">
29948  */
29949  
29950 /**
29951  * @class Roo.MessageBox
29952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29953  * Example usage:
29954  *<pre><code>
29955 // Basic alert:
29956 Roo.Msg.alert('Status', 'Changes saved successfully.');
29957
29958 // Prompt for user data:
29959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29960     if (btn == 'ok'){
29961         // process text value...
29962     }
29963 });
29964
29965 // Show a dialog using config options:
29966 Roo.Msg.show({
29967    title:'Save Changes?',
29968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29969    buttons: Roo.Msg.YESNOCANCEL,
29970    fn: processResult,
29971    animEl: 'elId'
29972 });
29973 </code></pre>
29974  * @singleton
29975  */
29976 Roo.MessageBox = function(){
29977     var dlg, opt, mask, waitTimer;
29978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29979     var buttons, activeTextEl, bwidth;
29980
29981     // private
29982     var handleButton = function(button){
29983         dlg.hide();
29984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29985     };
29986
29987     // private
29988     var handleHide = function(){
29989         if(opt && opt.cls){
29990             dlg.el.removeClass(opt.cls);
29991         }
29992         if(waitTimer){
29993             Roo.TaskMgr.stop(waitTimer);
29994             waitTimer = null;
29995         }
29996     };
29997
29998     // private
29999     var updateButtons = function(b){
30000         var width = 0;
30001         if(!b){
30002             buttons["ok"].hide();
30003             buttons["cancel"].hide();
30004             buttons["yes"].hide();
30005             buttons["no"].hide();
30006             dlg.footer.dom.style.display = 'none';
30007             return width;
30008         }
30009         dlg.footer.dom.style.display = '';
30010         for(var k in buttons){
30011             if(typeof buttons[k] != "function"){
30012                 if(b[k]){
30013                     buttons[k].show();
30014                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30015                     width += buttons[k].el.getWidth()+15;
30016                 }else{
30017                     buttons[k].hide();
30018                 }
30019             }
30020         }
30021         return width;
30022     };
30023
30024     // private
30025     var handleEsc = function(d, k, e){
30026         if(opt && opt.closable !== false){
30027             dlg.hide();
30028         }
30029         if(e){
30030             e.stopEvent();
30031         }
30032     };
30033
30034     return {
30035         /**
30036          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30037          * @return {Roo.BasicDialog} The BasicDialog element
30038          */
30039         getDialog : function(){
30040            if(!dlg){
30041                 dlg = new Roo.BasicDialog("x-msg-box", {
30042                     autoCreate : true,
30043                     shadow: true,
30044                     draggable: true,
30045                     resizable:false,
30046                     constraintoviewport:false,
30047                     fixedcenter:true,
30048                     collapsible : false,
30049                     shim:true,
30050                     modal: true,
30051                     width:400, height:100,
30052                     buttonAlign:"center",
30053                     closeClick : function(){
30054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30055                             handleButton("no");
30056                         }else{
30057                             handleButton("cancel");
30058                         }
30059                     }
30060                 });
30061                 dlg.on("hide", handleHide);
30062                 mask = dlg.mask;
30063                 dlg.addKeyListener(27, handleEsc);
30064                 buttons = {};
30065                 var bt = this.buttonText;
30066                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30067                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30068                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30069                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30070                 bodyEl = dlg.body.createChild({
30071
30072                     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>'
30073                 });
30074                 msgEl = bodyEl.dom.firstChild;
30075                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30076                 textboxEl.enableDisplayMode();
30077                 textboxEl.addKeyListener([10,13], function(){
30078                     if(dlg.isVisible() && opt && opt.buttons){
30079                         if(opt.buttons.ok){
30080                             handleButton("ok");
30081                         }else if(opt.buttons.yes){
30082                             handleButton("yes");
30083                         }
30084                     }
30085                 });
30086                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30087                 textareaEl.enableDisplayMode();
30088                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30089                 progressEl.enableDisplayMode();
30090                 var pf = progressEl.dom.firstChild;
30091                 if (pf) {
30092                     pp = Roo.get(pf.firstChild);
30093                     pp.setHeight(pf.offsetHeight);
30094                 }
30095                 
30096             }
30097             return dlg;
30098         },
30099
30100         /**
30101          * Updates the message box body text
30102          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30103          * the XHTML-compliant non-breaking space character '&amp;#160;')
30104          * @return {Roo.MessageBox} This message box
30105          */
30106         updateText : function(text){
30107             if(!dlg.isVisible() && !opt.width){
30108                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30109             }
30110             msgEl.innerHTML = text || '&#160;';
30111       
30112             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30113             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30114             var w = Math.max(
30115                     Math.min(opt.width || cw , this.maxWidth), 
30116                     Math.max(opt.minWidth || this.minWidth, bwidth)
30117             );
30118             if(opt.prompt){
30119                 activeTextEl.setWidth(w);
30120             }
30121             if(dlg.isVisible()){
30122                 dlg.fixedcenter = false;
30123             }
30124             // to big, make it scroll. = But as usual stupid IE does not support
30125             // !important..
30126             
30127             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30128                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30129                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30130             } else {
30131                 bodyEl.dom.style.height = '';
30132                 bodyEl.dom.style.overflowY = '';
30133             }
30134             if (cw > w) {
30135                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30136             } else {
30137                 bodyEl.dom.style.overflowX = '';
30138             }
30139             
30140             dlg.setContentSize(w, bodyEl.getHeight());
30141             if(dlg.isVisible()){
30142                 dlg.fixedcenter = true;
30143             }
30144             return this;
30145         },
30146
30147         /**
30148          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30149          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30150          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30151          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30152          * @return {Roo.MessageBox} This message box
30153          */
30154         updateProgress : function(value, text){
30155             if(text){
30156                 this.updateText(text);
30157             }
30158             if (pp) { // weird bug on my firefox - for some reason this is not defined
30159                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30160             }
30161             return this;
30162         },        
30163
30164         /**
30165          * Returns true if the message box is currently displayed
30166          * @return {Boolean} True if the message box is visible, else false
30167          */
30168         isVisible : function(){
30169             return dlg && dlg.isVisible();  
30170         },
30171
30172         /**
30173          * Hides the message box if it is displayed
30174          */
30175         hide : function(){
30176             if(this.isVisible()){
30177                 dlg.hide();
30178             }  
30179         },
30180
30181         /**
30182          * Displays a new message box, or reinitializes an existing message box, based on the config options
30183          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30184          * The following config object properties are supported:
30185          * <pre>
30186 Property    Type             Description
30187 ----------  ---------------  ------------------------------------------------------------------------------------
30188 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30189                                    closes (defaults to undefined)
30190 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30191                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30192 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30193                                    progress and wait dialogs will ignore this property and always hide the
30194                                    close button as they can only be closed programmatically.
30195 cls               String           A custom CSS class to apply to the message box element
30196 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30197                                    displayed (defaults to 75)
30198 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30199                                    function will be btn (the name of the button that was clicked, if applicable,
30200                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30201                                    Progress and wait dialogs will ignore this option since they do not respond to
30202                                    user actions and can only be closed programmatically, so any required function
30203                                    should be called by the same code after it closes the dialog.
30204 icon              String           A CSS class that provides a background image to be used as an icon for
30205                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30206 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30207 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30208 modal             Boolean          False to allow user interaction with the page while the message box is
30209                                    displayed (defaults to true)
30210 msg               String           A string that will replace the existing message box body text (defaults
30211                                    to the XHTML-compliant non-breaking space character '&#160;')
30212 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30213 progress          Boolean          True to display a progress bar (defaults to false)
30214 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30215 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30216 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30217 title             String           The title text
30218 value             String           The string value to set into the active textbox element if displayed
30219 wait              Boolean          True to display a progress bar (defaults to false)
30220 width             Number           The width of the dialog in pixels
30221 </pre>
30222          *
30223          * Example usage:
30224          * <pre><code>
30225 Roo.Msg.show({
30226    title: 'Address',
30227    msg: 'Please enter your address:',
30228    width: 300,
30229    buttons: Roo.MessageBox.OKCANCEL,
30230    multiline: true,
30231    fn: saveAddress,
30232    animEl: 'addAddressBtn'
30233 });
30234 </code></pre>
30235          * @param {Object} config Configuration options
30236          * @return {Roo.MessageBox} This message box
30237          */
30238         show : function(options)
30239         {
30240             
30241             // this causes nightmares if you show one dialog after another
30242             // especially on callbacks..
30243              
30244             if(this.isVisible()){
30245                 
30246                 this.hide();
30247                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
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             Roo.log("ROOT not set in tree");
31347             return this;
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 | dom node.. 
39254      */
39255     insertAtCursor : function(text)
39256     {
39257         
39258         
39259         
39260         if(!this.activated){
39261             return;
39262         }
39263         /*
39264         if(Roo.isIE){
39265             this.win.focus();
39266             var r = this.doc.selection.createRange();
39267             if(r){
39268                 r.collapse(true);
39269                 r.pasteHTML(text);
39270                 this.syncValue();
39271                 this.deferFocus();
39272             
39273             }
39274             return;
39275         }
39276         */
39277         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39278             this.win.focus();
39279             
39280             
39281             // from jquery ui (MIT licenced)
39282             var range, node;
39283             var win = this.win;
39284             
39285             if (win.getSelection && win.getSelection().getRangeAt) {
39286                 range = win.getSelection().getRangeAt(0);
39287                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39288                 range.insertNode(node);
39289             } else if (win.document.selection && win.document.selection.createRange) {
39290                 // no firefox support
39291                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39292                 win.document.selection.createRange().pasteHTML(txt);
39293             } else {
39294                 // no firefox support
39295                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39296                 this.execCmd('InsertHTML', txt);
39297             } 
39298             
39299             this.syncValue();
39300             
39301             this.deferFocus();
39302         }
39303     },
39304  // private
39305     mozKeyPress : function(e){
39306         if(e.ctrlKey){
39307             var c = e.getCharCode(), cmd;
39308           
39309             if(c > 0){
39310                 c = String.fromCharCode(c).toLowerCase();
39311                 switch(c){
39312                     case 'b':
39313                         cmd = 'bold';
39314                     break;
39315                     case 'i':
39316                         cmd = 'italic';
39317                     break;
39318                     case 'u':
39319                         cmd = 'underline';
39320                         break;
39321                     case 'v':
39322                         this.cleanUpPaste.defer(100, this);
39323                         return;
39324                     break;
39325                 }
39326                 if(cmd){
39327                     this.win.focus();
39328                     this.execCmd(cmd);
39329                     this.deferFocus();
39330                     e.preventDefault();
39331                 }
39332                 
39333             }
39334         }
39335     },
39336
39337     // private
39338     fixKeys : function(){ // load time branching for fastest keydown performance
39339         if(Roo.isIE){
39340             return function(e){
39341                 var k = e.getKey(), r;
39342                 if(k == e.TAB){
39343                     e.stopEvent();
39344                     r = this.doc.selection.createRange();
39345                     if(r){
39346                         r.collapse(true);
39347                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39348                         this.deferFocus();
39349                     }
39350                     return;
39351                 }
39352                 
39353                 if(k == e.ENTER){
39354                     r = this.doc.selection.createRange();
39355                     if(r){
39356                         var target = r.parentElement();
39357                         if(!target || target.tagName.toLowerCase() != 'li'){
39358                             e.stopEvent();
39359                             r.pasteHTML('<br />');
39360                             r.collapse(false);
39361                             r.select();
39362                         }
39363                     }
39364                 }
39365                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39366                     this.cleanUpPaste.defer(100, this);
39367                     return;
39368                 }
39369                 
39370                 
39371             };
39372         }else if(Roo.isOpera){
39373             return function(e){
39374                 var k = e.getKey();
39375                 if(k == e.TAB){
39376                     e.stopEvent();
39377                     this.win.focus();
39378                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39379                     this.deferFocus();
39380                 }
39381                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39382                     this.cleanUpPaste.defer(100, this);
39383                     return;
39384                 }
39385                 
39386             };
39387         }else if(Roo.isSafari){
39388             return function(e){
39389                 var k = e.getKey();
39390                 
39391                 if(k == e.TAB){
39392                     e.stopEvent();
39393                     this.execCmd('InsertText','\t');
39394                     this.deferFocus();
39395                     return;
39396                 }
39397                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39398                     this.cleanUpPaste.defer(100, this);
39399                     return;
39400                 }
39401                 
39402              };
39403         }
39404     }(),
39405     
39406     getAllAncestors: function()
39407     {
39408         var p = this.getSelectedNode();
39409         var a = [];
39410         if (!p) {
39411             a.push(p); // push blank onto stack..
39412             p = this.getParentElement();
39413         }
39414         
39415         
39416         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39417             a.push(p);
39418             p = p.parentNode;
39419         }
39420         a.push(this.doc.body);
39421         return a;
39422     },
39423     lastSel : false,
39424     lastSelNode : false,
39425     
39426     
39427     getSelection : function() 
39428     {
39429         this.assignDocWin();
39430         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39431     },
39432     
39433     getSelectedNode: function() 
39434     {
39435         // this may only work on Gecko!!!
39436         
39437         // should we cache this!!!!
39438         
39439         
39440         
39441          
39442         var range = this.createRange(this.getSelection()).cloneRange();
39443         
39444         if (Roo.isIE) {
39445             var parent = range.parentElement();
39446             while (true) {
39447                 var testRange = range.duplicate();
39448                 testRange.moveToElementText(parent);
39449                 if (testRange.inRange(range)) {
39450                     break;
39451                 }
39452                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39453                     break;
39454                 }
39455                 parent = parent.parentElement;
39456             }
39457             return parent;
39458         }
39459         
39460         // is ancestor a text element.
39461         var ac =  range.commonAncestorContainer;
39462         if (ac.nodeType == 3) {
39463             ac = ac.parentNode;
39464         }
39465         
39466         var ar = ac.childNodes;
39467          
39468         var nodes = [];
39469         var other_nodes = [];
39470         var has_other_nodes = false;
39471         for (var i=0;i<ar.length;i++) {
39472             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39473                 continue;
39474             }
39475             // fullly contained node.
39476             
39477             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39478                 nodes.push(ar[i]);
39479                 continue;
39480             }
39481             
39482             // probably selected..
39483             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39484                 other_nodes.push(ar[i]);
39485                 continue;
39486             }
39487             // outer..
39488             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39489                 continue;
39490             }
39491             
39492             
39493             has_other_nodes = true;
39494         }
39495         if (!nodes.length && other_nodes.length) {
39496             nodes= other_nodes;
39497         }
39498         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39499             return false;
39500         }
39501         
39502         return nodes[0];
39503     },
39504     createRange: function(sel)
39505     {
39506         // this has strange effects when using with 
39507         // top toolbar - not sure if it's a great idea.
39508         //this.editor.contentWindow.focus();
39509         if (typeof sel != "undefined") {
39510             try {
39511                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39512             } catch(e) {
39513                 return this.doc.createRange();
39514             }
39515         } else {
39516             return this.doc.createRange();
39517         }
39518     },
39519     getParentElement: function()
39520     {
39521         
39522         this.assignDocWin();
39523         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39524         
39525         var range = this.createRange(sel);
39526          
39527         try {
39528             var p = range.commonAncestorContainer;
39529             while (p.nodeType == 3) { // text node
39530                 p = p.parentNode;
39531             }
39532             return p;
39533         } catch (e) {
39534             return null;
39535         }
39536     
39537     },
39538     /***
39539      *
39540      * Range intersection.. the hard stuff...
39541      *  '-1' = before
39542      *  '0' = hits..
39543      *  '1' = after.
39544      *         [ -- selected range --- ]
39545      *   [fail]                        [fail]
39546      *
39547      *    basically..
39548      *      if end is before start or  hits it. fail.
39549      *      if start is after end or hits it fail.
39550      *
39551      *   if either hits (but other is outside. - then it's not 
39552      *   
39553      *    
39554      **/
39555     
39556     
39557     // @see http://www.thismuchiknow.co.uk/?p=64.
39558     rangeIntersectsNode : function(range, node)
39559     {
39560         var nodeRange = node.ownerDocument.createRange();
39561         try {
39562             nodeRange.selectNode(node);
39563         } catch (e) {
39564             nodeRange.selectNodeContents(node);
39565         }
39566     
39567         var rangeStartRange = range.cloneRange();
39568         rangeStartRange.collapse(true);
39569     
39570         var rangeEndRange = range.cloneRange();
39571         rangeEndRange.collapse(false);
39572     
39573         var nodeStartRange = nodeRange.cloneRange();
39574         nodeStartRange.collapse(true);
39575     
39576         var nodeEndRange = nodeRange.cloneRange();
39577         nodeEndRange.collapse(false);
39578     
39579         return rangeStartRange.compareBoundaryPoints(
39580                  Range.START_TO_START, nodeEndRange) == -1 &&
39581                rangeEndRange.compareBoundaryPoints(
39582                  Range.START_TO_START, nodeStartRange) == 1;
39583         
39584          
39585     },
39586     rangeCompareNode : function(range, node)
39587     {
39588         var nodeRange = node.ownerDocument.createRange();
39589         try {
39590             nodeRange.selectNode(node);
39591         } catch (e) {
39592             nodeRange.selectNodeContents(node);
39593         }
39594         
39595         
39596         range.collapse(true);
39597     
39598         nodeRange.collapse(true);
39599      
39600         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39601         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39602          
39603         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39604         
39605         var nodeIsBefore   =  ss == 1;
39606         var nodeIsAfter    = ee == -1;
39607         
39608         if (nodeIsBefore && nodeIsAfter)
39609             return 0; // outer
39610         if (!nodeIsBefore && nodeIsAfter)
39611             return 1; //right trailed.
39612         
39613         if (nodeIsBefore && !nodeIsAfter)
39614             return 2;  // left trailed.
39615         // fully contined.
39616         return 3;
39617     },
39618
39619     // private? - in a new class?
39620     cleanUpPaste :  function()
39621     {
39622         // cleans up the whole document..
39623          Roo.log('cleanuppaste');
39624         this.cleanUpChildren(this.doc.body);
39625         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39626         if (clean != this.doc.body.innerHTML) {
39627             this.doc.body.innerHTML = clean;
39628         }
39629         
39630     },
39631     
39632     cleanWordChars : function(input) {
39633         var he = Roo.form.HtmlEditor;
39634     
39635         var output = input;
39636         Roo.each(he.swapCodes, function(sw) { 
39637         
39638             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39639             output = output.replace(swapper, sw[1]);
39640         });
39641         return output;
39642     },
39643     
39644     
39645     cleanUpChildren : function (n)
39646     {
39647         if (!n.childNodes.length) {
39648             return;
39649         }
39650         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39651            this.cleanUpChild(n.childNodes[i]);
39652         }
39653     },
39654     
39655     
39656         
39657     
39658     cleanUpChild : function (node)
39659     {
39660         //console.log(node);
39661         if (node.nodeName == "#text") {
39662             // clean up silly Windows -- stuff?
39663             return; 
39664         }
39665         if (node.nodeName == "#comment") {
39666             node.parentNode.removeChild(node);
39667             // clean up silly Windows -- stuff?
39668             return; 
39669         }
39670         
39671         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39672             // remove node.
39673             node.parentNode.removeChild(node);
39674             return;
39675             
39676         }
39677         
39678         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39679         
39680         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39681         
39682         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39683             remove_keep_children = true;
39684         }
39685         
39686         if (remove_keep_children) {
39687             this.cleanUpChildren(node);
39688             // inserts everything just before this node...
39689             while (node.childNodes.length) {
39690                 var cn = node.childNodes[0];
39691                 node.removeChild(cn);
39692                 node.parentNode.insertBefore(cn, node);
39693             }
39694             node.parentNode.removeChild(node);
39695             return;
39696         }
39697         
39698         if (!node.attributes || !node.attributes.length) {
39699             this.cleanUpChildren(node);
39700             return;
39701         }
39702         
39703         function cleanAttr(n,v)
39704         {
39705             
39706             if (v.match(/^\./) || v.match(/^\//)) {
39707                 return;
39708             }
39709             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39710                 return;
39711             }
39712             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39713             node.removeAttribute(n);
39714             
39715         }
39716         
39717         function cleanStyle(n,v)
39718         {
39719             if (v.match(/expression/)) { //XSS?? should we even bother..
39720                 node.removeAttribute(n);
39721                 return;
39722             }
39723             
39724             
39725             var parts = v.split(/;/);
39726             Roo.each(parts, function(p) {
39727                 p = p.replace(/\s+/g,'');
39728                 if (!p.length) {
39729                     return true;
39730                 }
39731                 var l = p.split(':').shift().replace(/\s+/g,'');
39732                 
39733                 // only allow 'c whitelisted system attributes'
39734                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39735                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39736                     node.removeAttribute(n);
39737                     return false;
39738                 }
39739                 return true;
39740             });
39741             
39742             
39743         }
39744         
39745         
39746         for (var i = node.attributes.length-1; i > -1 ; i--) {
39747             var a = node.attributes[i];
39748             //console.log(a);
39749             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39750                 node.removeAttribute(a.name);
39751                 return;
39752             }
39753             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39754                 cleanAttr(a.name,a.value); // fixme..
39755                 return;
39756             }
39757             if (a.name == 'style') {
39758                 cleanStyle(a.name,a.value);
39759             }
39760             /// clean up MS crap..
39761             // tecnically this should be a list of valid class'es..
39762             
39763             
39764             if (a.name == 'class') {
39765                 if (a.value.match(/^Mso/)) {
39766                     node.className = '';
39767                 }
39768                 
39769                 if (a.value.match(/body/)) {
39770                     node.className = '';
39771                 }
39772             }
39773             
39774             // style cleanup!?
39775             // class cleanup?
39776             
39777         }
39778         
39779         
39780         this.cleanUpChildren(node);
39781         
39782         
39783     }
39784     
39785     
39786     // hide stuff that is not compatible
39787     /**
39788      * @event blur
39789      * @hide
39790      */
39791     /**
39792      * @event change
39793      * @hide
39794      */
39795     /**
39796      * @event focus
39797      * @hide
39798      */
39799     /**
39800      * @event specialkey
39801      * @hide
39802      */
39803     /**
39804      * @cfg {String} fieldClass @hide
39805      */
39806     /**
39807      * @cfg {String} focusClass @hide
39808      */
39809     /**
39810      * @cfg {String} autoCreate @hide
39811      */
39812     /**
39813      * @cfg {String} inputType @hide
39814      */
39815     /**
39816      * @cfg {String} invalidClass @hide
39817      */
39818     /**
39819      * @cfg {String} invalidText @hide
39820      */
39821     /**
39822      * @cfg {String} msgFx @hide
39823      */
39824     /**
39825      * @cfg {String} validateOnBlur @hide
39826      */
39827 });
39828
39829 Roo.form.HtmlEditor.white = [
39830         'area', 'br', 'img', 'input', 'hr', 'wbr',
39831         
39832        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39833        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39834        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39835        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39836        'table',   'ul',         'xmp', 
39837        
39838        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39839       'thead',   'tr', 
39840      
39841       'dir', 'menu', 'ol', 'ul', 'dl',
39842        
39843       'embed',  'object'
39844 ];
39845
39846
39847 Roo.form.HtmlEditor.black = [
39848     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39849         'applet', // 
39850         'base',   'basefont', 'bgsound', 'blink',  'body', 
39851         'frame',  'frameset', 'head',    'html',   'ilayer', 
39852         'iframe', 'layer',  'link',     'meta',    'object',   
39853         'script', 'style' ,'title',  'xml' // clean later..
39854 ];
39855 Roo.form.HtmlEditor.clean = [
39856     'script', 'style', 'title', 'xml'
39857 ];
39858 Roo.form.HtmlEditor.remove = [
39859     'font'
39860 ];
39861 // attributes..
39862
39863 Roo.form.HtmlEditor.ablack = [
39864     'on'
39865 ];
39866     
39867 Roo.form.HtmlEditor.aclean = [ 
39868     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39869 ];
39870
39871 // protocols..
39872 Roo.form.HtmlEditor.pwhite= [
39873         'http',  'https',  'mailto'
39874 ];
39875
39876 // white listed style attributes.
39877 Roo.form.HtmlEditor.cwhite= [
39878         'text-align',
39879         'font-size'
39880 ];
39881
39882
39883 Roo.form.HtmlEditor.swapCodes   =[ 
39884     [    8211, "--" ], 
39885     [    8212, "--" ], 
39886     [    8216,  "'" ],  
39887     [    8217, "'" ],  
39888     [    8220, '"' ],  
39889     [    8221, '"' ],  
39890     [    8226, "*" ],  
39891     [    8230, "..." ]
39892 ]; 
39893
39894     // <script type="text/javascript">
39895 /*
39896  * Based on
39897  * Ext JS Library 1.1.1
39898  * Copyright(c) 2006-2007, Ext JS, LLC.
39899  *  
39900  
39901  */
39902
39903 /**
39904  * @class Roo.form.HtmlEditorToolbar1
39905  * Basic Toolbar
39906  * 
39907  * Usage:
39908  *
39909  new Roo.form.HtmlEditor({
39910     ....
39911     toolbars : [
39912         new Roo.form.HtmlEditorToolbar1({
39913             disable : { fonts: 1 , format: 1, ..., ... , ...],
39914             btns : [ .... ]
39915         })
39916     }
39917      
39918  * 
39919  * @cfg {Object} disable List of elements to disable..
39920  * @cfg {Array} btns List of additional buttons.
39921  * 
39922  * 
39923  * NEEDS Extra CSS? 
39924  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39925  */
39926  
39927 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39928 {
39929     
39930     Roo.apply(this, config);
39931     
39932     // default disabled, based on 'good practice'..
39933     this.disable = this.disable || {};
39934     Roo.applyIf(this.disable, {
39935         fontSize : true,
39936         colors : true,
39937         specialElements : true
39938     });
39939     
39940     
39941     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39942     // dont call parent... till later.
39943 }
39944
39945 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39946     
39947     tb: false,
39948     
39949     rendered: false,
39950     
39951     editor : false,
39952     /**
39953      * @cfg {Object} disable  List of toolbar elements to disable
39954          
39955      */
39956     disable : false,
39957       /**
39958      * @cfg {Array} fontFamilies An array of available font families
39959      */
39960     fontFamilies : [
39961         'Arial',
39962         'Courier New',
39963         'Tahoma',
39964         'Times New Roman',
39965         'Verdana'
39966     ],
39967     
39968     specialChars : [
39969            "&#169;",
39970           "&#174;",     
39971           "&#8482;",    
39972           "&#163;" ,    
39973          // "&#8212;",    
39974           "&#8230;",    
39975           "&#247;" ,    
39976         //  "&#225;" ,     ?? a acute?
39977            "&#8364;"    , //Euro
39978        //   "&#8220;"    ,
39979         //  "&#8221;"    ,
39980         //  "&#8226;"    ,
39981           "&#176;"  //   , // degrees
39982
39983          // "&#233;"     , // e ecute
39984          // "&#250;"     , // u ecute?
39985     ],
39986     
39987     specialElements : [
39988         {
39989             text: "Insert Table",
39990             xtype: 'MenuItem',
39991             xns : Roo.Menu,
39992             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39993                 
39994         },
39995         {    
39996             text: "Insert Image",
39997             xtype: 'MenuItem',
39998             xns : Roo.Menu,
39999             ihtml : '<img src="about:blank"/>'
40000             
40001         }
40002         
40003          
40004     ],
40005     
40006     
40007     inputElements : [ 
40008             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40009             "input:submit", "input:button", "select", "textarea", "label" ],
40010     formats : [
40011         ["p"] ,  
40012         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40013         ["pre"],[ "code"], 
40014         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40015     ],
40016      /**
40017      * @cfg {String} defaultFont default font to use.
40018      */
40019     defaultFont: 'tahoma',
40020    
40021     fontSelect : false,
40022     
40023     
40024     formatCombo : false,
40025     
40026     init : function(editor)
40027     {
40028         this.editor = editor;
40029         
40030         
40031         var fid = editor.frameId;
40032         var etb = this;
40033         function btn(id, toggle, handler){
40034             var xid = fid + '-'+ id ;
40035             return {
40036                 id : xid,
40037                 cmd : id,
40038                 cls : 'x-btn-icon x-edit-'+id,
40039                 enableToggle:toggle !== false,
40040                 scope: editor, // was editor...
40041                 handler:handler||editor.relayBtnCmd,
40042                 clickEvent:'mousedown',
40043                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40044                 tabIndex:-1
40045             };
40046         }
40047         
40048         
40049         
40050         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40051         this.tb = tb;
40052          // stop form submits
40053         tb.el.on('click', function(e){
40054             e.preventDefault(); // what does this do?
40055         });
40056
40057         if(!this.disable.font && !Roo.isSafari){
40058             /* why no safari for fonts
40059             editor.fontSelect = tb.el.createChild({
40060                 tag:'select',
40061                 tabIndex: -1,
40062                 cls:'x-font-select',
40063                 html: editor.createFontOptions()
40064             });
40065             editor.fontSelect.on('change', function(){
40066                 var font = editor.fontSelect.dom.value;
40067                 editor.relayCmd('fontname', font);
40068                 editor.deferFocus();
40069             }, editor);
40070             tb.add(
40071                 editor.fontSelect.dom,
40072                 '-'
40073             );
40074             */
40075         };
40076         if(!this.disable.formats){
40077             this.formatCombo = new Roo.form.ComboBox({
40078                 store: new Roo.data.SimpleStore({
40079                     id : 'tag',
40080                     fields: ['tag'],
40081                     data : this.formats // from states.js
40082                 }),
40083                 blockFocus : true,
40084                 //autoCreate : {tag: "div",  size: "20"},
40085                 displayField:'tag',
40086                 typeAhead: false,
40087                 mode: 'local',
40088                 editable : false,
40089                 triggerAction: 'all',
40090                 emptyText:'Add tag',
40091                 selectOnFocus:true,
40092                 width:135,
40093                 listeners : {
40094                     'select': function(c, r, i) {
40095                         editor.insertTag(r.get('tag'));
40096                         editor.focus();
40097                     }
40098                 }
40099
40100             });
40101             tb.addField(this.formatCombo);
40102             
40103         }
40104         
40105         if(!this.disable.format){
40106             tb.add(
40107                 btn('bold'),
40108                 btn('italic'),
40109                 btn('underline')
40110             );
40111         };
40112         if(!this.disable.fontSize){
40113             tb.add(
40114                 '-',
40115                 
40116                 
40117                 btn('increasefontsize', false, editor.adjustFont),
40118                 btn('decreasefontsize', false, editor.adjustFont)
40119             );
40120         };
40121         
40122         
40123         if(!this.disable.colors){
40124             tb.add(
40125                 '-', {
40126                     id:editor.frameId +'-forecolor',
40127                     cls:'x-btn-icon x-edit-forecolor',
40128                     clickEvent:'mousedown',
40129                     tooltip: this.buttonTips['forecolor'] || undefined,
40130                     tabIndex:-1,
40131                     menu : new Roo.menu.ColorMenu({
40132                         allowReselect: true,
40133                         focus: Roo.emptyFn,
40134                         value:'000000',
40135                         plain:true,
40136                         selectHandler: function(cp, color){
40137                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40138                             editor.deferFocus();
40139                         },
40140                         scope: editor,
40141                         clickEvent:'mousedown'
40142                     })
40143                 }, {
40144                     id:editor.frameId +'backcolor',
40145                     cls:'x-btn-icon x-edit-backcolor',
40146                     clickEvent:'mousedown',
40147                     tooltip: this.buttonTips['backcolor'] || undefined,
40148                     tabIndex:-1,
40149                     menu : new Roo.menu.ColorMenu({
40150                         focus: Roo.emptyFn,
40151                         value:'FFFFFF',
40152                         plain:true,
40153                         allowReselect: true,
40154                         selectHandler: function(cp, color){
40155                             if(Roo.isGecko){
40156                                 editor.execCmd('useCSS', false);
40157                                 editor.execCmd('hilitecolor', color);
40158                                 editor.execCmd('useCSS', true);
40159                                 editor.deferFocus();
40160                             }else{
40161                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40162                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40163                                 editor.deferFocus();
40164                             }
40165                         },
40166                         scope:editor,
40167                         clickEvent:'mousedown'
40168                     })
40169                 }
40170             );
40171         };
40172         // now add all the items...
40173         
40174
40175         if(!this.disable.alignments){
40176             tb.add(
40177                 '-',
40178                 btn('justifyleft'),
40179                 btn('justifycenter'),
40180                 btn('justifyright')
40181             );
40182         };
40183
40184         //if(!Roo.isSafari){
40185             if(!this.disable.links){
40186                 tb.add(
40187                     '-',
40188                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40189                 );
40190             };
40191
40192             if(!this.disable.lists){
40193                 tb.add(
40194                     '-',
40195                     btn('insertorderedlist'),
40196                     btn('insertunorderedlist')
40197                 );
40198             }
40199             if(!this.disable.sourceEdit){
40200                 tb.add(
40201                     '-',
40202                     btn('sourceedit', true, function(btn){
40203                         this.toggleSourceEdit(btn.pressed);
40204                     })
40205                 );
40206             }
40207         //}
40208         
40209         var smenu = { };
40210         // special menu.. - needs to be tidied up..
40211         if (!this.disable.special) {
40212             smenu = {
40213                 text: "&#169;",
40214                 cls: 'x-edit-none',
40215                 
40216                 menu : {
40217                     items : []
40218                 }
40219             };
40220             for (var i =0; i < this.specialChars.length; i++) {
40221                 smenu.menu.items.push({
40222                     
40223                     html: this.specialChars[i],
40224                     handler: function(a,b) {
40225                         //editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40226                         editor.insertAtCursor(a.html);
40227                         
40228                     },
40229                     tabIndex:-1
40230                 });
40231             }
40232             
40233             
40234             tb.add(smenu);
40235             
40236             
40237         }
40238          
40239         if (!this.disable.specialElements) {
40240             var semenu = {
40241                 text: "Other;",
40242                 cls: 'x-edit-none',
40243                 menu : {
40244                     items : []
40245                 }
40246             };
40247             for (var i =0; i < this.specialElements.length; i++) {
40248                 semenu.menu.items.push(
40249                     Roo.apply({ 
40250                         handler: function(a,b) {
40251                             editor.insertAtCursor(this.ihtml);
40252                         }
40253                     }, this.specialElements[i])
40254                 );
40255                     
40256             }
40257             
40258             tb.add(semenu);
40259             
40260             
40261         }
40262          
40263         
40264         if (this.btns) {
40265             for(var i =0; i< this.btns.length;i++) {
40266                 var b = this.btns[i];
40267                 b.cls =  'x-edit-none';
40268                 b.scope = editor;
40269                 tb.add(b);
40270             }
40271         
40272         }
40273         
40274         
40275         
40276         // disable everything...
40277         
40278         this.tb.items.each(function(item){
40279            if(item.id != editor.frameId+ '-sourceedit'){
40280                 item.disable();
40281             }
40282         });
40283         this.rendered = true;
40284         
40285         // the all the btns;
40286         editor.on('editorevent', this.updateToolbar, this);
40287         // other toolbars need to implement this..
40288         //editor.on('editmodechange', this.updateToolbar, this);
40289     },
40290     
40291     
40292     
40293     /**
40294      * Protected method that will not generally be called directly. It triggers
40295      * a toolbar update by reading the markup state of the current selection in the editor.
40296      */
40297     updateToolbar: function(){
40298
40299         if(!this.editor.activated){
40300             this.editor.onFirstFocus();
40301             return;
40302         }
40303
40304         var btns = this.tb.items.map, 
40305             doc = this.editor.doc,
40306             frameId = this.editor.frameId;
40307
40308         if(!this.disable.font && !Roo.isSafari){
40309             /*
40310             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40311             if(name != this.fontSelect.dom.value){
40312                 this.fontSelect.dom.value = name;
40313             }
40314             */
40315         }
40316         if(!this.disable.format){
40317             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40318             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40319             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40320         }
40321         if(!this.disable.alignments){
40322             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40323             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40324             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40325         }
40326         if(!Roo.isSafari && !this.disable.lists){
40327             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40328             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40329         }
40330         
40331         var ans = this.editor.getAllAncestors();
40332         if (this.formatCombo) {
40333             
40334             
40335             var store = this.formatCombo.store;
40336             this.formatCombo.setValue("");
40337             for (var i =0; i < ans.length;i++) {
40338                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40339                     // select it..
40340                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40341                     break;
40342                 }
40343             }
40344         }
40345         
40346         
40347         
40348         // hides menus... - so this cant be on a menu...
40349         Roo.menu.MenuMgr.hideAll();
40350
40351         //this.editorsyncValue();
40352     },
40353    
40354     
40355     createFontOptions : function(){
40356         var buf = [], fs = this.fontFamilies, ff, lc;
40357         for(var i = 0, len = fs.length; i< len; i++){
40358             ff = fs[i];
40359             lc = ff.toLowerCase();
40360             buf.push(
40361                 '<option value="',lc,'" style="font-family:',ff,';"',
40362                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40363                     ff,
40364                 '</option>'
40365             );
40366         }
40367         return buf.join('');
40368     },
40369     
40370     toggleSourceEdit : function(sourceEditMode){
40371         if(sourceEditMode === undefined){
40372             sourceEditMode = !this.sourceEditMode;
40373         }
40374         this.sourceEditMode = sourceEditMode === true;
40375         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40376         // just toggle the button?
40377         if(btn.pressed !== this.editor.sourceEditMode){
40378             btn.toggle(this.editor.sourceEditMode);
40379             return;
40380         }
40381         
40382         if(this.sourceEditMode){
40383             this.tb.items.each(function(item){
40384                 if(item.cmd != 'sourceedit'){
40385                     item.disable();
40386                 }
40387             });
40388           
40389         }else{
40390             if(this.initialized){
40391                 this.tb.items.each(function(item){
40392                     item.enable();
40393                 });
40394             }
40395             
40396         }
40397         // tell the editor that it's been pressed..
40398         this.editor.toggleSourceEdit(sourceEditMode);
40399        
40400     },
40401      /**
40402      * Object collection of toolbar tooltips for the buttons in the editor. The key
40403      * is the command id associated with that button and the value is a valid QuickTips object.
40404      * For example:
40405 <pre><code>
40406 {
40407     bold : {
40408         title: 'Bold (Ctrl+B)',
40409         text: 'Make the selected text bold.',
40410         cls: 'x-html-editor-tip'
40411     },
40412     italic : {
40413         title: 'Italic (Ctrl+I)',
40414         text: 'Make the selected text italic.',
40415         cls: 'x-html-editor-tip'
40416     },
40417     ...
40418 </code></pre>
40419     * @type Object
40420      */
40421     buttonTips : {
40422         bold : {
40423             title: 'Bold (Ctrl+B)',
40424             text: 'Make the selected text bold.',
40425             cls: 'x-html-editor-tip'
40426         },
40427         italic : {
40428             title: 'Italic (Ctrl+I)',
40429             text: 'Make the selected text italic.',
40430             cls: 'x-html-editor-tip'
40431         },
40432         underline : {
40433             title: 'Underline (Ctrl+U)',
40434             text: 'Underline the selected text.',
40435             cls: 'x-html-editor-tip'
40436         },
40437         increasefontsize : {
40438             title: 'Grow Text',
40439             text: 'Increase the font size.',
40440             cls: 'x-html-editor-tip'
40441         },
40442         decreasefontsize : {
40443             title: 'Shrink Text',
40444             text: 'Decrease the font size.',
40445             cls: 'x-html-editor-tip'
40446         },
40447         backcolor : {
40448             title: 'Text Highlight Color',
40449             text: 'Change the background color of the selected text.',
40450             cls: 'x-html-editor-tip'
40451         },
40452         forecolor : {
40453             title: 'Font Color',
40454             text: 'Change the color of the selected text.',
40455             cls: 'x-html-editor-tip'
40456         },
40457         justifyleft : {
40458             title: 'Align Text Left',
40459             text: 'Align text to the left.',
40460             cls: 'x-html-editor-tip'
40461         },
40462         justifycenter : {
40463             title: 'Center Text',
40464             text: 'Center text in the editor.',
40465             cls: 'x-html-editor-tip'
40466         },
40467         justifyright : {
40468             title: 'Align Text Right',
40469             text: 'Align text to the right.',
40470             cls: 'x-html-editor-tip'
40471         },
40472         insertunorderedlist : {
40473             title: 'Bullet List',
40474             text: 'Start a bulleted list.',
40475             cls: 'x-html-editor-tip'
40476         },
40477         insertorderedlist : {
40478             title: 'Numbered List',
40479             text: 'Start a numbered list.',
40480             cls: 'x-html-editor-tip'
40481         },
40482         createlink : {
40483             title: 'Hyperlink',
40484             text: 'Make the selected text a hyperlink.',
40485             cls: 'x-html-editor-tip'
40486         },
40487         sourceedit : {
40488             title: 'Source Edit',
40489             text: 'Switch to source editing mode.',
40490             cls: 'x-html-editor-tip'
40491         }
40492     },
40493     // private
40494     onDestroy : function(){
40495         if(this.rendered){
40496             
40497             this.tb.items.each(function(item){
40498                 if(item.menu){
40499                     item.menu.removeAll();
40500                     if(item.menu.el){
40501                         item.menu.el.destroy();
40502                     }
40503                 }
40504                 item.destroy();
40505             });
40506              
40507         }
40508     },
40509     onFirstFocus: function() {
40510         this.tb.items.each(function(item){
40511            item.enable();
40512         });
40513     }
40514 });
40515
40516
40517
40518
40519 // <script type="text/javascript">
40520 /*
40521  * Based on
40522  * Ext JS Library 1.1.1
40523  * Copyright(c) 2006-2007, Ext JS, LLC.
40524  *  
40525  
40526  */
40527
40528  
40529 /**
40530  * @class Roo.form.HtmlEditor.ToolbarContext
40531  * Context Toolbar
40532  * 
40533  * Usage:
40534  *
40535  new Roo.form.HtmlEditor({
40536     ....
40537     toolbars : [
40538         { xtype: 'ToolbarStandard', styles : {} }
40539         { xtype: 'ToolbarContext', disable : {} }
40540     ]
40541 })
40542
40543      
40544  * 
40545  * @config : {Object} disable List of elements to disable.. (not done yet.)
40546  * @config : {Object} styles  Map of styles available.
40547  * 
40548  */
40549
40550 Roo.form.HtmlEditor.ToolbarContext = function(config)
40551 {
40552     
40553     Roo.apply(this, config);
40554     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40555     // dont call parent... till later.
40556     this.styles = this.styles || {};
40557 }
40558 Roo.form.HtmlEditor.ToolbarContext.types = {
40559     'IMG' : {
40560         width : {
40561             title: "Width",
40562             width: 40
40563         },
40564         height:  {
40565             title: "Height",
40566             width: 40
40567         },
40568         align: {
40569             title: "Align",
40570             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40571             width : 80
40572             
40573         },
40574         border: {
40575             title: "Border",
40576             width: 40
40577         },
40578         alt: {
40579             title: "Alt",
40580             width: 120
40581         },
40582         src : {
40583             title: "Src",
40584             width: 220
40585         }
40586         
40587     },
40588     'A' : {
40589         name : {
40590             title: "Name",
40591             width: 50
40592         },
40593         href:  {
40594             title: "Href",
40595             width: 220
40596         } // border?
40597         
40598     },
40599     'TABLE' : {
40600         rows : {
40601             title: "Rows",
40602             width: 20
40603         },
40604         cols : {
40605             title: "Cols",
40606             width: 20
40607         },
40608         width : {
40609             title: "Width",
40610             width: 40
40611         },
40612         height : {
40613             title: "Height",
40614             width: 40
40615         },
40616         border : {
40617             title: "Border",
40618             width: 20
40619         }
40620     },
40621     'TD' : {
40622         width : {
40623             title: "Width",
40624             width: 40
40625         },
40626         height : {
40627             title: "Height",
40628             width: 40
40629         },   
40630         align: {
40631             title: "Align",
40632             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40633             width: 80
40634         },
40635         valign: {
40636             title: "Valign",
40637             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40638             width: 80
40639         },
40640         colspan: {
40641             title: "Colspan",
40642             width: 20
40643             
40644         }
40645     },
40646     'INPUT' : {
40647         name : {
40648             title: "name",
40649             width: 120
40650         },
40651         value : {
40652             title: "Value",
40653             width: 120
40654         },
40655         width : {
40656             title: "Width",
40657             width: 40
40658         }
40659     },
40660     'LABEL' : {
40661         'for' : {
40662             title: "For",
40663             width: 120
40664         }
40665     },
40666     'TEXTAREA' : {
40667           name : {
40668             title: "name",
40669             width: 120
40670         },
40671         rows : {
40672             title: "Rows",
40673             width: 20
40674         },
40675         cols : {
40676             title: "Cols",
40677             width: 20
40678         }
40679     },
40680     'SELECT' : {
40681         name : {
40682             title: "name",
40683             width: 120
40684         },
40685         selectoptions : {
40686             title: "Options",
40687             width: 200
40688         }
40689     },
40690     
40691     // should we really allow this??
40692     // should this just be 
40693     'BODY' : {
40694         title : {
40695             title: "title",
40696             width: 200,
40697             disabled : true
40698         }
40699     },
40700     '*' : {
40701         // empty..
40702     }
40703 };
40704
40705
40706
40707 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40708     
40709     tb: false,
40710     
40711     rendered: false,
40712     
40713     editor : false,
40714     /**
40715      * @cfg {Object} disable  List of toolbar elements to disable
40716          
40717      */
40718     disable : false,
40719     /**
40720      * @cfg {Object} styles List of styles 
40721      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40722      *
40723      * These must be defined in the page, so they get rendered correctly..
40724      * .headline { }
40725      * TD.underline { }
40726      * 
40727      */
40728     styles : false,
40729     
40730     
40731     
40732     toolbars : false,
40733     
40734     init : function(editor)
40735     {
40736         this.editor = editor;
40737         
40738         
40739         var fid = editor.frameId;
40740         var etb = this;
40741         function btn(id, toggle, handler){
40742             var xid = fid + '-'+ id ;
40743             return {
40744                 id : xid,
40745                 cmd : id,
40746                 cls : 'x-btn-icon x-edit-'+id,
40747                 enableToggle:toggle !== false,
40748                 scope: editor, // was editor...
40749                 handler:handler||editor.relayBtnCmd,
40750                 clickEvent:'mousedown',
40751                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40752                 tabIndex:-1
40753             };
40754         }
40755         // create a new element.
40756         var wdiv = editor.wrap.createChild({
40757                 tag: 'div'
40758             }, editor.wrap.dom.firstChild.nextSibling, true);
40759         
40760         // can we do this more than once??
40761         
40762          // stop form submits
40763       
40764  
40765         // disable everything...
40766         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40767         this.toolbars = {};
40768            
40769         for (var i in  ty) {
40770           
40771             this.toolbars[i] = this.buildToolbar(ty[i],i);
40772         }
40773         this.tb = this.toolbars.BODY;
40774         this.tb.el.show();
40775         this.buildFooter();
40776         this.footer.show();
40777          
40778         this.rendered = true;
40779         
40780         // the all the btns;
40781         editor.on('editorevent', this.updateToolbar, this);
40782         // other toolbars need to implement this..
40783         //editor.on('editmodechange', this.updateToolbar, this);
40784     },
40785     
40786     
40787     
40788     /**
40789      * Protected method that will not generally be called directly. It triggers
40790      * a toolbar update by reading the markup state of the current selection in the editor.
40791      */
40792     updateToolbar: function(ignore_a,ignore_b,sel){
40793
40794         
40795         if(!this.editor.activated){
40796              this.editor.onFirstFocus();
40797             return;
40798         }
40799         var updateFooter = sel ? false : true;
40800         
40801         
40802         var ans = this.editor.getAllAncestors();
40803         
40804         // pick
40805         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40806         
40807         if (!sel) { 
40808             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40809             sel = sel ? sel : this.editor.doc.body;
40810             sel = sel.tagName.length ? sel : this.editor.doc.body;
40811             
40812         }
40813         // pick a menu that exists..
40814         var tn = sel.tagName.toUpperCase();
40815         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40816         
40817         tn = sel.tagName.toUpperCase();
40818         
40819         var lastSel = this.tb.selectedNode
40820         
40821         this.tb.selectedNode = sel;
40822         
40823         // if current menu does not match..
40824         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40825                 
40826             this.tb.el.hide();
40827             ///console.log("show: " + tn);
40828             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40829             this.tb.el.show();
40830             // update name
40831             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40832             
40833             
40834             // update attributes
40835             if (this.tb.fields) {
40836                 this.tb.fields.each(function(e) {
40837                    e.setValue(sel.getAttribute(e.name));
40838                 });
40839             }
40840             
40841             // update styles
40842             var st = this.tb.fields.item(0);
40843             st.store.removeAll();
40844             var cn = sel.className.split(/\s+/);
40845             
40846             var avs = [];
40847             if (this.styles['*']) {
40848                 
40849                 Roo.each(this.styles['*'], function(v) {
40850                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40851                 });
40852             }
40853             if (this.styles[tn]) { 
40854                 Roo.each(this.styles[tn], function(v) {
40855                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40856                 });
40857             }
40858             
40859             st.store.loadData(avs);
40860             st.collapse();
40861             st.setValue(cn);
40862             
40863             // flag our selected Node.
40864             this.tb.selectedNode = sel;
40865            
40866            
40867             Roo.menu.MenuMgr.hideAll();
40868
40869         }
40870         
40871         if (!updateFooter) {
40872             return;
40873         }
40874         // update the footer
40875         //
40876         var html = '';
40877         
40878         this.footerEls = ans.reverse();
40879         Roo.each(this.footerEls, function(a,i) {
40880             if (!a) { return; }
40881             html += html.length ? ' &gt; '  :  '';
40882             
40883             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40884             
40885         });
40886        
40887         // 
40888         var sz = this.footDisp.up('td').getSize();
40889         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40890         this.footDisp.dom.style.marginLeft = '5px';
40891         
40892         this.footDisp.dom.style.overflow = 'hidden';
40893         
40894         this.footDisp.dom.innerHTML = html;
40895             
40896         //this.editorsyncValue();
40897     },
40898    
40899        
40900     // private
40901     onDestroy : function(){
40902         if(this.rendered){
40903             
40904             this.tb.items.each(function(item){
40905                 if(item.menu){
40906                     item.menu.removeAll();
40907                     if(item.menu.el){
40908                         item.menu.el.destroy();
40909                     }
40910                 }
40911                 item.destroy();
40912             });
40913              
40914         }
40915     },
40916     onFirstFocus: function() {
40917         // need to do this for all the toolbars..
40918         this.tb.items.each(function(item){
40919            item.enable();
40920         });
40921     },
40922     buildToolbar: function(tlist, nm)
40923     {
40924         var editor = this.editor;
40925          // create a new element.
40926         var wdiv = editor.wrap.createChild({
40927                 tag: 'div'
40928             }, editor.wrap.dom.firstChild.nextSibling, true);
40929         
40930        
40931         var tb = new Roo.Toolbar(wdiv);
40932         // add the name..
40933         
40934         tb.add(nm+ ":&nbsp;");
40935         
40936         // styles...
40937         if (this.styles) {
40938             
40939             // this needs a multi-select checkbox...
40940             tb.addField( new Roo.form.ComboBox({
40941                 store: new Roo.data.SimpleStore({
40942                     id : 'val',
40943                     fields: ['val', 'selected'],
40944                     data : [] 
40945                 }),
40946                 name : 'className',
40947                 displayField:'val',
40948                 typeAhead: false,
40949                 mode: 'local',
40950                 editable : false,
40951                 triggerAction: 'all',
40952                 emptyText:'Select Style',
40953                 selectOnFocus:true,
40954                 width: 130,
40955                 listeners : {
40956                     'select': function(c, r, i) {
40957                         // initial support only for on class per el..
40958                         tb.selectedNode.className =  r ? r.get('val') : '';
40959                     }
40960                 }
40961     
40962             }));
40963         }
40964             
40965         
40966         
40967         for (var i in tlist) {
40968             
40969             var item = tlist[i];
40970             tb.add(item.title + ":&nbsp;");
40971             
40972             
40973             
40974             
40975             if (item.opts) {
40976                 // opts == pulldown..
40977                 tb.addField( new Roo.form.ComboBox({
40978                     store: new Roo.data.SimpleStore({
40979                         id : 'val',
40980                         fields: ['val'],
40981                         data : item.opts  
40982                     }),
40983                     name : i,
40984                     displayField:'val',
40985                     typeAhead: false,
40986                     mode: 'local',
40987                     editable : false,
40988                     triggerAction: 'all',
40989                     emptyText:'Select',
40990                     selectOnFocus:true,
40991                     width: item.width ? item.width  : 130,
40992                     listeners : {
40993                         'select': function(c, r, i) {
40994                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40995                         }
40996                     }
40997
40998                 }));
40999                 continue;
41000                     
41001                  
41002                 
41003                 tb.addField( new Roo.form.TextField({
41004                     name: i,
41005                     width: 100,
41006                     //allowBlank:false,
41007                     value: ''
41008                 }));
41009                 continue;
41010             }
41011             tb.addField( new Roo.form.TextField({
41012                 name: i,
41013                 width: item.width,
41014                 //allowBlank:true,
41015                 value: '',
41016                 listeners: {
41017                     'change' : function(f, nv, ov) {
41018                         tb.selectedNode.setAttribute(f.name, nv);
41019                     }
41020                 }
41021             }));
41022              
41023         }
41024         tb.el.on('click', function(e){
41025             e.preventDefault(); // what does this do?
41026         });
41027         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41028         tb.el.hide();
41029         tb.name = nm;
41030         // dont need to disable them... as they will get hidden
41031         return tb;
41032          
41033         
41034     },
41035     buildFooter : function()
41036     {
41037         
41038         var fel = this.editor.wrap.createChild();
41039         this.footer = new Roo.Toolbar(fel);
41040         // toolbar has scrolly on left / right?
41041         var footDisp= new Roo.Toolbar.Fill();
41042         var _t = this;
41043         this.footer.add(
41044             {
41045                 text : '&lt;',
41046                 xtype: 'Button',
41047                 handler : function() {
41048                     _t.footDisp.scrollTo('left',0,true)
41049                 }
41050             }
41051         );
41052         this.footer.add( footDisp );
41053         this.footer.add( 
41054             {
41055                 text : '&gt;',
41056                 xtype: 'Button',
41057                 handler : function() {
41058                     // no animation..
41059                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41060                 }
41061             }
41062         );
41063         var fel = Roo.get(footDisp.el);
41064         fel.addClass('x-editor-context');
41065         this.footDispWrap = fel; 
41066         this.footDispWrap.overflow  = 'hidden';
41067         
41068         this.footDisp = fel.createChild();
41069         this.footDispWrap.on('click', this.onContextClick, this)
41070         
41071         
41072     },
41073     onContextClick : function (ev,dom)
41074     {
41075         ev.preventDefault();
41076         var  cn = dom.className;
41077         Roo.log(cn);
41078         if (!cn.match(/x-ed-loc-/)) {
41079             return;
41080         }
41081         var n = cn.split('-').pop();
41082         var ans = this.footerEls;
41083         var sel = ans[n];
41084         
41085          // pick
41086         var range = this.editor.createRange();
41087         
41088         range.selectNodeContents(sel);
41089         //range.selectNode(sel);
41090         
41091         
41092         var selection = this.editor.getSelection();
41093         selection.removeAllRanges();
41094         selection.addRange(range);
41095         
41096         
41097         
41098         this.updateToolbar(null, null, sel);
41099         
41100         
41101     }
41102     
41103     
41104     
41105     
41106     
41107 });
41108
41109
41110
41111
41112
41113 /*
41114  * Based on:
41115  * Ext JS Library 1.1.1
41116  * Copyright(c) 2006-2007, Ext JS, LLC.
41117  *
41118  * Originally Released Under LGPL - original licence link has changed is not relivant.
41119  *
41120  * Fork - LGPL
41121  * <script type="text/javascript">
41122  */
41123  
41124 /**
41125  * @class Roo.form.BasicForm
41126  * @extends Roo.util.Observable
41127  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41128  * @constructor
41129  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41130  * @param {Object} config Configuration options
41131  */
41132 Roo.form.BasicForm = function(el, config){
41133     this.allItems = [];
41134     this.childForms = [];
41135     Roo.apply(this, config);
41136     /*
41137      * The Roo.form.Field items in this form.
41138      * @type MixedCollection
41139      */
41140      
41141      
41142     this.items = new Roo.util.MixedCollection(false, function(o){
41143         return o.id || (o.id = Roo.id());
41144     });
41145     this.addEvents({
41146         /**
41147          * @event beforeaction
41148          * Fires before any action is performed. Return false to cancel the action.
41149          * @param {Form} this
41150          * @param {Action} action The action to be performed
41151          */
41152         beforeaction: true,
41153         /**
41154          * @event actionfailed
41155          * Fires when an action fails.
41156          * @param {Form} this
41157          * @param {Action} action The action that failed
41158          */
41159         actionfailed : true,
41160         /**
41161          * @event actioncomplete
41162          * Fires when an action is completed.
41163          * @param {Form} this
41164          * @param {Action} action The action that completed
41165          */
41166         actioncomplete : true
41167     });
41168     if(el){
41169         this.initEl(el);
41170     }
41171     Roo.form.BasicForm.superclass.constructor.call(this);
41172 };
41173
41174 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41175     /**
41176      * @cfg {String} method
41177      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41178      */
41179     /**
41180      * @cfg {DataReader} reader
41181      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41182      * This is optional as there is built-in support for processing JSON.
41183      */
41184     /**
41185      * @cfg {DataReader} errorReader
41186      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41187      * This is completely optional as there is built-in support for processing JSON.
41188      */
41189     /**
41190      * @cfg {String} url
41191      * The URL to use for form actions if one isn't supplied in the action options.
41192      */
41193     /**
41194      * @cfg {Boolean} fileUpload
41195      * Set to true if this form is a file upload.
41196      */
41197      
41198     /**
41199      * @cfg {Object} baseParams
41200      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41201      */
41202      /**
41203      
41204     /**
41205      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41206      */
41207     timeout: 30,
41208
41209     // private
41210     activeAction : null,
41211
41212     /**
41213      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41214      * or setValues() data instead of when the form was first created.
41215      */
41216     trackResetOnLoad : false,
41217     
41218     
41219     /**
41220      * childForms - used for multi-tab forms
41221      * @type {Array}
41222      */
41223     childForms : false,
41224     
41225     /**
41226      * allItems - full list of fields.
41227      * @type {Array}
41228      */
41229     allItems : false,
41230     
41231     /**
41232      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41233      * element by passing it or its id or mask the form itself by passing in true.
41234      * @type Mixed
41235      */
41236     waitMsgTarget : false,
41237
41238     // private
41239     initEl : function(el){
41240         this.el = Roo.get(el);
41241         this.id = this.el.id || Roo.id();
41242         this.el.on('submit', this.onSubmit, this);
41243         this.el.addClass('x-form');
41244     },
41245
41246     // private
41247     onSubmit : function(e){
41248         e.stopEvent();
41249     },
41250
41251     /**
41252      * Returns true if client-side validation on the form is successful.
41253      * @return Boolean
41254      */
41255     isValid : function(){
41256         var valid = true;
41257         this.items.each(function(f){
41258            if(!f.validate()){
41259                valid = false;
41260            }
41261         });
41262         return valid;
41263     },
41264
41265     /**
41266      * Returns true if any fields in this form have changed since their original load.
41267      * @return Boolean
41268      */
41269     isDirty : function(){
41270         var dirty = false;
41271         this.items.each(function(f){
41272            if(f.isDirty()){
41273                dirty = true;
41274                return false;
41275            }
41276         });
41277         return dirty;
41278     },
41279
41280     /**
41281      * Performs a predefined action (submit or load) or custom actions you define on this form.
41282      * @param {String} actionName The name of the action type
41283      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41284      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41285      * accept other config options):
41286      * <pre>
41287 Property          Type             Description
41288 ----------------  ---------------  ----------------------------------------------------------------------------------
41289 url               String           The url for the action (defaults to the form's url)
41290 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41291 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41292 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41293                                    validate the form on the client (defaults to false)
41294      * </pre>
41295      * @return {BasicForm} this
41296      */
41297     doAction : function(action, options){
41298         if(typeof action == 'string'){
41299             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41300         }
41301         if(this.fireEvent('beforeaction', this, action) !== false){
41302             this.beforeAction(action);
41303             action.run.defer(100, action);
41304         }
41305         return this;
41306     },
41307
41308     /**
41309      * Shortcut to do a submit action.
41310      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41311      * @return {BasicForm} this
41312      */
41313     submit : function(options){
41314         this.doAction('submit', options);
41315         return this;
41316     },
41317
41318     /**
41319      * Shortcut to do a load action.
41320      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41321      * @return {BasicForm} this
41322      */
41323     load : function(options){
41324         this.doAction('load', options);
41325         return this;
41326     },
41327
41328     /**
41329      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41330      * @param {Record} record The record to edit
41331      * @return {BasicForm} this
41332      */
41333     updateRecord : function(record){
41334         record.beginEdit();
41335         var fs = record.fields;
41336         fs.each(function(f){
41337             var field = this.findField(f.name);
41338             if(field){
41339                 record.set(f.name, field.getValue());
41340             }
41341         }, this);
41342         record.endEdit();
41343         return this;
41344     },
41345
41346     /**
41347      * Loads an Roo.data.Record into this form.
41348      * @param {Record} record The record to load
41349      * @return {BasicForm} this
41350      */
41351     loadRecord : function(record){
41352         this.setValues(record.data);
41353         return this;
41354     },
41355
41356     // private
41357     beforeAction : function(action){
41358         var o = action.options;
41359         
41360        
41361         if(this.waitMsgTarget === true){
41362             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41363         }else if(this.waitMsgTarget){
41364             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41365             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41366         }else {
41367             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41368         }
41369          
41370     },
41371
41372     // private
41373     afterAction : function(action, success){
41374         this.activeAction = null;
41375         var o = action.options;
41376         
41377         if(this.waitMsgTarget === true){
41378             this.el.unmask();
41379         }else if(this.waitMsgTarget){
41380             this.waitMsgTarget.unmask();
41381         }else{
41382             Roo.MessageBox.updateProgress(1);
41383             Roo.MessageBox.hide();
41384         }
41385          
41386         if(success){
41387             if(o.reset){
41388                 this.reset();
41389             }
41390             Roo.callback(o.success, o.scope, [this, action]);
41391             this.fireEvent('actioncomplete', this, action);
41392             
41393         }else{
41394             Roo.callback(o.failure, o.scope, [this, action]);
41395             // show an error message if no failed handler is set..
41396             if (!this.hasListener('actionfailed')) {
41397                 Roo.MessageBox.alert("Error",
41398                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41399                         action.result.errorMsg :
41400                         "Saving Failed, please check your entries"
41401                 );
41402             }
41403             
41404             this.fireEvent('actionfailed', this, action);
41405         }
41406         
41407     },
41408
41409     /**
41410      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41411      * @param {String} id The value to search for
41412      * @return Field
41413      */
41414     findField : function(id){
41415         var field = this.items.get(id);
41416         if(!field){
41417             this.items.each(function(f){
41418                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41419                     field = f;
41420                     return false;
41421                 }
41422             });
41423         }
41424         return field || null;
41425     },
41426
41427     /**
41428      * Add a secondary form to this one, 
41429      * Used to provide tabbed forms. One form is primary, with hidden values 
41430      * which mirror the elements from the other forms.
41431      * 
41432      * @param {Roo.form.Form} form to add.
41433      * 
41434      */
41435     addForm : function(form)
41436     {
41437        
41438         if (this.childForms.indexOf(form) > -1) {
41439             // already added..
41440             return;
41441         }
41442         this.childForms.push(form);
41443         var n = '';
41444         Roo.each(form.allItems, function (fe) {
41445             
41446             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41447             if (this.findField(n)) { // already added..
41448                 return;
41449             }
41450             var add = new Roo.form.Hidden({
41451                 name : n
41452             });
41453             add.render(this.el);
41454             
41455             this.add( add );
41456         }, this);
41457         
41458     },
41459     /**
41460      * Mark fields in this form invalid in bulk.
41461      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41462      * @return {BasicForm} this
41463      */
41464     markInvalid : function(errors){
41465         if(errors instanceof Array){
41466             for(var i = 0, len = errors.length; i < len; i++){
41467                 var fieldError = errors[i];
41468                 var f = this.findField(fieldError.id);
41469                 if(f){
41470                     f.markInvalid(fieldError.msg);
41471                 }
41472             }
41473         }else{
41474             var field, id;
41475             for(id in errors){
41476                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41477                     field.markInvalid(errors[id]);
41478                 }
41479             }
41480         }
41481         Roo.each(this.childForms || [], function (f) {
41482             f.markInvalid(errors);
41483         });
41484         
41485         return this;
41486     },
41487
41488     /**
41489      * Set values for fields in this form in bulk.
41490      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41491      * @return {BasicForm} this
41492      */
41493     setValues : function(values){
41494         if(values instanceof Array){ // array of objects
41495             for(var i = 0, len = values.length; i < len; i++){
41496                 var v = values[i];
41497                 var f = this.findField(v.id);
41498                 if(f){
41499                     f.setValue(v.value);
41500                     if(this.trackResetOnLoad){
41501                         f.originalValue = f.getValue();
41502                     }
41503                 }
41504             }
41505         }else{ // object hash
41506             var field, id;
41507             for(id in values){
41508                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41509                     
41510                     if (field.setFromData && 
41511                         field.valueField && 
41512                         field.displayField &&
41513                         // combos' with local stores can 
41514                         // be queried via setValue()
41515                         // to set their value..
41516                         (field.store && !field.store.isLocal)
41517                         ) {
41518                         // it's a combo
41519                         var sd = { };
41520                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41521                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41522                         field.setFromData(sd);
41523                         
41524                     } else {
41525                         field.setValue(values[id]);
41526                     }
41527                     
41528                     
41529                     if(this.trackResetOnLoad){
41530                         field.originalValue = field.getValue();
41531                     }
41532                 }
41533             }
41534         }
41535          
41536         Roo.each(this.childForms || [], function (f) {
41537             f.setValues(values);
41538         });
41539                 
41540         return this;
41541     },
41542
41543     /**
41544      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41545      * they are returned as an array.
41546      * @param {Boolean} asString
41547      * @return {Object}
41548      */
41549     getValues : function(asString){
41550         if (this.childForms) {
41551             // copy values from the child forms
41552             Roo.each(this.childForms, function (f) {
41553                 this.setValues(f.getValues());
41554             }, this);
41555         }
41556         
41557         
41558         
41559         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41560         if(asString === true){
41561             return fs;
41562         }
41563         return Roo.urlDecode(fs);
41564     },
41565     
41566     /**
41567      * Returns the fields in this form as an object with key/value pairs. 
41568      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41569      * @return {Object}
41570      */
41571     getFieldValues : function(with_hidden)
41572     {
41573         if (this.childForms) {
41574             // copy values from the child forms
41575             // should this call getFieldValues - probably not as we do not currently copy
41576             // hidden fields when we generate..
41577             Roo.each(this.childForms, function (f) {
41578                 this.setValues(f.getValues());
41579             }, this);
41580         }
41581         
41582         var ret = {};
41583         this.items.each(function(f){
41584             if (!f.getName()) {
41585                 return;
41586             }
41587             var v = f.getValue();
41588             // not sure if this supported any more..
41589             if ((typeof(v) == 'object') && f.getRawValue) {
41590                 v = f.getRawValue() ; // dates..
41591             }
41592             // combo boxes where name != hiddenName...
41593             if (f.name != f.getName()) {
41594                 ret[f.name] = f.getRawValue();
41595             }
41596             ret[f.getName()] = v;
41597         });
41598         
41599         return ret;
41600     },
41601
41602     /**
41603      * Clears all invalid messages in this form.
41604      * @return {BasicForm} this
41605      */
41606     clearInvalid : function(){
41607         this.items.each(function(f){
41608            f.clearInvalid();
41609         });
41610         
41611         Roo.each(this.childForms || [], function (f) {
41612             f.clearInvalid();
41613         });
41614         
41615         
41616         return this;
41617     },
41618
41619     /**
41620      * Resets this form.
41621      * @return {BasicForm} this
41622      */
41623     reset : function(){
41624         this.items.each(function(f){
41625             f.reset();
41626         });
41627         
41628         Roo.each(this.childForms || [], function (f) {
41629             f.reset();
41630         });
41631        
41632         
41633         return this;
41634     },
41635
41636     /**
41637      * Add Roo.form components to this form.
41638      * @param {Field} field1
41639      * @param {Field} field2 (optional)
41640      * @param {Field} etc (optional)
41641      * @return {BasicForm} this
41642      */
41643     add : function(){
41644         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41645         return this;
41646     },
41647
41648
41649     /**
41650      * Removes a field from the items collection (does NOT remove its markup).
41651      * @param {Field} field
41652      * @return {BasicForm} this
41653      */
41654     remove : function(field){
41655         this.items.remove(field);
41656         return this;
41657     },
41658
41659     /**
41660      * Looks at the fields in this form, checks them for an id attribute,
41661      * and calls applyTo on the existing dom element with that id.
41662      * @return {BasicForm} this
41663      */
41664     render : function(){
41665         this.items.each(function(f){
41666             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41667                 f.applyTo(f.id);
41668             }
41669         });
41670         return this;
41671     },
41672
41673     /**
41674      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41675      * @param {Object} values
41676      * @return {BasicForm} this
41677      */
41678     applyToFields : function(o){
41679         this.items.each(function(f){
41680            Roo.apply(f, o);
41681         });
41682         return this;
41683     },
41684
41685     /**
41686      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41687      * @param {Object} values
41688      * @return {BasicForm} this
41689      */
41690     applyIfToFields : function(o){
41691         this.items.each(function(f){
41692            Roo.applyIf(f, o);
41693         });
41694         return this;
41695     }
41696 });
41697
41698 // back compat
41699 Roo.BasicForm = Roo.form.BasicForm;/*
41700  * Based on:
41701  * Ext JS Library 1.1.1
41702  * Copyright(c) 2006-2007, Ext JS, LLC.
41703  *
41704  * Originally Released Under LGPL - original licence link has changed is not relivant.
41705  *
41706  * Fork - LGPL
41707  * <script type="text/javascript">
41708  */
41709
41710 /**
41711  * @class Roo.form.Form
41712  * @extends Roo.form.BasicForm
41713  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41714  * @constructor
41715  * @param {Object} config Configuration options
41716  */
41717 Roo.form.Form = function(config){
41718     var xitems =  [];
41719     if (config.items) {
41720         xitems = config.items;
41721         delete config.items;
41722     }
41723    
41724     
41725     Roo.form.Form.superclass.constructor.call(this, null, config);
41726     this.url = this.url || this.action;
41727     if(!this.root){
41728         this.root = new Roo.form.Layout(Roo.applyIf({
41729             id: Roo.id()
41730         }, config));
41731     }
41732     this.active = this.root;
41733     /**
41734      * Array of all the buttons that have been added to this form via {@link addButton}
41735      * @type Array
41736      */
41737     this.buttons = [];
41738     this.allItems = [];
41739     this.addEvents({
41740         /**
41741          * @event clientvalidation
41742          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41743          * @param {Form} this
41744          * @param {Boolean} valid true if the form has passed client-side validation
41745          */
41746         clientvalidation: true,
41747         /**
41748          * @event rendered
41749          * Fires when the form is rendered
41750          * @param {Roo.form.Form} form
41751          */
41752         rendered : true
41753     });
41754     
41755     if (this.progressUrl) {
41756             // push a hidden field onto the list of fields..
41757             this.addxtype( {
41758                     xns: Roo.form, 
41759                     xtype : 'Hidden', 
41760                     name : 'UPLOAD_IDENTIFIER' 
41761             });
41762         }
41763         
41764     
41765     Roo.each(xitems, this.addxtype, this);
41766     
41767     
41768     
41769 };
41770
41771 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41772     /**
41773      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41774      */
41775     /**
41776      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41777      */
41778     /**
41779      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41780      */
41781     buttonAlign:'center',
41782
41783     /**
41784      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41785      */
41786     minButtonWidth:75,
41787
41788     /**
41789      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41790      * This property cascades to child containers if not set.
41791      */
41792     labelAlign:'left',
41793
41794     /**
41795      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41796      * fires a looping event with that state. This is required to bind buttons to the valid
41797      * state using the config value formBind:true on the button.
41798      */
41799     monitorValid : false,
41800
41801     /**
41802      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41803      */
41804     monitorPoll : 200,
41805     
41806     /**
41807      * @cfg {String} progressUrl - Url to return progress data 
41808      */
41809     
41810     progressUrl : false,
41811   
41812     /**
41813      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41814      * fields are added and the column is closed. If no fields are passed the column remains open
41815      * until end() is called.
41816      * @param {Object} config The config to pass to the column
41817      * @param {Field} field1 (optional)
41818      * @param {Field} field2 (optional)
41819      * @param {Field} etc (optional)
41820      * @return Column The column container object
41821      */
41822     column : function(c){
41823         var col = new Roo.form.Column(c);
41824         this.start(col);
41825         if(arguments.length > 1){ // duplicate code required because of Opera
41826             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41827             this.end();
41828         }
41829         return col;
41830     },
41831
41832     /**
41833      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41834      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41835      * until end() is called.
41836      * @param {Object} config The config to pass to the fieldset
41837      * @param {Field} field1 (optional)
41838      * @param {Field} field2 (optional)
41839      * @param {Field} etc (optional)
41840      * @return FieldSet The fieldset container object
41841      */
41842     fieldset : function(c){
41843         var fs = new Roo.form.FieldSet(c);
41844         this.start(fs);
41845         if(arguments.length > 1){ // duplicate code required because of Opera
41846             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41847             this.end();
41848         }
41849         return fs;
41850     },
41851
41852     /**
41853      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41854      * fields are added and the container is closed. If no fields are passed the container remains open
41855      * until end() is called.
41856      * @param {Object} config The config to pass to the Layout
41857      * @param {Field} field1 (optional)
41858      * @param {Field} field2 (optional)
41859      * @param {Field} etc (optional)
41860      * @return Layout The container object
41861      */
41862     container : function(c){
41863         var l = new Roo.form.Layout(c);
41864         this.start(l);
41865         if(arguments.length > 1){ // duplicate code required because of Opera
41866             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41867             this.end();
41868         }
41869         return l;
41870     },
41871
41872     /**
41873      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41874      * @param {Object} container A Roo.form.Layout or subclass of Layout
41875      * @return {Form} this
41876      */
41877     start : function(c){
41878         // cascade label info
41879         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41880         this.active.stack.push(c);
41881         c.ownerCt = this.active;
41882         this.active = c;
41883         return this;
41884     },
41885
41886     /**
41887      * Closes the current open container
41888      * @return {Form} this
41889      */
41890     end : function(){
41891         if(this.active == this.root){
41892             return this;
41893         }
41894         this.active = this.active.ownerCt;
41895         return this;
41896     },
41897
41898     /**
41899      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41900      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41901      * as the label of the field.
41902      * @param {Field} field1
41903      * @param {Field} field2 (optional)
41904      * @param {Field} etc. (optional)
41905      * @return {Form} this
41906      */
41907     add : function(){
41908         this.active.stack.push.apply(this.active.stack, arguments);
41909         this.allItems.push.apply(this.allItems,arguments);
41910         var r = [];
41911         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41912             if(a[i].isFormField){
41913                 r.push(a[i]);
41914             }
41915         }
41916         if(r.length > 0){
41917             Roo.form.Form.superclass.add.apply(this, r);
41918         }
41919         return this;
41920     },
41921     
41922
41923     
41924     
41925     
41926      /**
41927      * Find any element that has been added to a form, using it's ID or name
41928      * This can include framesets, columns etc. along with regular fields..
41929      * @param {String} id - id or name to find.
41930      
41931      * @return {Element} e - or false if nothing found.
41932      */
41933     findbyId : function(id)
41934     {
41935         var ret = false;
41936         if (!id) {
41937             return ret;
41938         }
41939         Roo.each(this.allItems, function(f){
41940             if (f.id == id || f.name == id ){
41941                 ret = f;
41942                 return false;
41943             }
41944         });
41945         return ret;
41946     },
41947
41948     
41949     
41950     /**
41951      * Render this form into the passed container. This should only be called once!
41952      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41953      * @return {Form} this
41954      */
41955     render : function(ct)
41956     {
41957         
41958         
41959         
41960         ct = Roo.get(ct);
41961         var o = this.autoCreate || {
41962             tag: 'form',
41963             method : this.method || 'POST',
41964             id : this.id || Roo.id()
41965         };
41966         this.initEl(ct.createChild(o));
41967
41968         this.root.render(this.el);
41969         
41970        
41971              
41972         this.items.each(function(f){
41973             f.render('x-form-el-'+f.id);
41974         });
41975
41976         if(this.buttons.length > 0){
41977             // tables are required to maintain order and for correct IE layout
41978             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41979                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41980                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41981             }}, null, true);
41982             var tr = tb.getElementsByTagName('tr')[0];
41983             for(var i = 0, len = this.buttons.length; i < len; i++) {
41984                 var b = this.buttons[i];
41985                 var td = document.createElement('td');
41986                 td.className = 'x-form-btn-td';
41987                 b.render(tr.appendChild(td));
41988             }
41989         }
41990         if(this.monitorValid){ // initialize after render
41991             this.startMonitoring();
41992         }
41993         this.fireEvent('rendered', this);
41994         return this;
41995     },
41996
41997     /**
41998      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41999      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42000      * object or a valid Roo.DomHelper element config
42001      * @param {Function} handler The function called when the button is clicked
42002      * @param {Object} scope (optional) The scope of the handler function
42003      * @return {Roo.Button}
42004      */
42005     addButton : function(config, handler, scope){
42006         var bc = {
42007             handler: handler,
42008             scope: scope,
42009             minWidth: this.minButtonWidth,
42010             hideParent:true
42011         };
42012         if(typeof config == "string"){
42013             bc.text = config;
42014         }else{
42015             Roo.apply(bc, config);
42016         }
42017         var btn = new Roo.Button(null, bc);
42018         this.buttons.push(btn);
42019         return btn;
42020     },
42021
42022      /**
42023      * Adds a series of form elements (using the xtype property as the factory method.
42024      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42025      * @param {Object} config 
42026      */
42027     
42028     addxtype : function()
42029     {
42030         var ar = Array.prototype.slice.call(arguments, 0);
42031         var ret = false;
42032         for(var i = 0; i < ar.length; i++) {
42033             if (!ar[i]) {
42034                 continue; // skip -- if this happends something invalid got sent, we 
42035                 // should ignore it, as basically that interface element will not show up
42036                 // and that should be pretty obvious!!
42037             }
42038             
42039             if (Roo.form[ar[i].xtype]) {
42040                 ar[i].form = this;
42041                 var fe = Roo.factory(ar[i], Roo.form);
42042                 if (!ret) {
42043                     ret = fe;
42044                 }
42045                 fe.form = this;
42046                 if (fe.store) {
42047                     fe.store.form = this;
42048                 }
42049                 if (fe.isLayout) {  
42050                          
42051                     this.start(fe);
42052                     this.allItems.push(fe);
42053                     if (fe.items && fe.addxtype) {
42054                         fe.addxtype.apply(fe, fe.items);
42055                         delete fe.items;
42056                     }
42057                      this.end();
42058                     continue;
42059                 }
42060                 
42061                 
42062                  
42063                 this.add(fe);
42064               //  console.log('adding ' + ar[i].xtype);
42065             }
42066             if (ar[i].xtype == 'Button') {  
42067                 //console.log('adding button');
42068                 //console.log(ar[i]);
42069                 this.addButton(ar[i]);
42070                 this.allItems.push(fe);
42071                 continue;
42072             }
42073             
42074             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42075                 alert('end is not supported on xtype any more, use items');
42076             //    this.end();
42077             //    //console.log('adding end');
42078             }
42079             
42080         }
42081         return ret;
42082     },
42083     
42084     /**
42085      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42086      * option "monitorValid"
42087      */
42088     startMonitoring : function(){
42089         if(!this.bound){
42090             this.bound = true;
42091             Roo.TaskMgr.start({
42092                 run : this.bindHandler,
42093                 interval : this.monitorPoll || 200,
42094                 scope: this
42095             });
42096         }
42097     },
42098
42099     /**
42100      * Stops monitoring of the valid state of this form
42101      */
42102     stopMonitoring : function(){
42103         this.bound = false;
42104     },
42105
42106     // private
42107     bindHandler : function(){
42108         if(!this.bound){
42109             return false; // stops binding
42110         }
42111         var valid = true;
42112         this.items.each(function(f){
42113             if(!f.isValid(true)){
42114                 valid = false;
42115                 return false;
42116             }
42117         });
42118         for(var i = 0, len = this.buttons.length; i < len; i++){
42119             var btn = this.buttons[i];
42120             if(btn.formBind === true && btn.disabled === valid){
42121                 btn.setDisabled(!valid);
42122             }
42123         }
42124         this.fireEvent('clientvalidation', this, valid);
42125     }
42126     
42127     
42128     
42129     
42130     
42131     
42132     
42133     
42134 });
42135
42136
42137 // back compat
42138 Roo.Form = Roo.form.Form;
42139 /*
42140  * Based on:
42141  * Ext JS Library 1.1.1
42142  * Copyright(c) 2006-2007, Ext JS, LLC.
42143  *
42144  * Originally Released Under LGPL - original licence link has changed is not relivant.
42145  *
42146  * Fork - LGPL
42147  * <script type="text/javascript">
42148  */
42149  
42150  /**
42151  * @class Roo.form.Action
42152  * Internal Class used to handle form actions
42153  * @constructor
42154  * @param {Roo.form.BasicForm} el The form element or its id
42155  * @param {Object} config Configuration options
42156  */
42157  
42158  
42159 // define the action interface
42160 Roo.form.Action = function(form, options){
42161     this.form = form;
42162     this.options = options || {};
42163 };
42164 /**
42165  * Client Validation Failed
42166  * @const 
42167  */
42168 Roo.form.Action.CLIENT_INVALID = 'client';
42169 /**
42170  * Server Validation Failed
42171  * @const 
42172  */
42173  Roo.form.Action.SERVER_INVALID = 'server';
42174  /**
42175  * Connect to Server Failed
42176  * @const 
42177  */
42178 Roo.form.Action.CONNECT_FAILURE = 'connect';
42179 /**
42180  * Reading Data from Server Failed
42181  * @const 
42182  */
42183 Roo.form.Action.LOAD_FAILURE = 'load';
42184
42185 Roo.form.Action.prototype = {
42186     type : 'default',
42187     failureType : undefined,
42188     response : undefined,
42189     result : undefined,
42190
42191     // interface method
42192     run : function(options){
42193
42194     },
42195
42196     // interface method
42197     success : function(response){
42198
42199     },
42200
42201     // interface method
42202     handleResponse : function(response){
42203
42204     },
42205
42206     // default connection failure
42207     failure : function(response){
42208         
42209         this.response = response;
42210         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42211         this.form.afterAction(this, false);
42212     },
42213
42214     processResponse : function(response){
42215         this.response = response;
42216         if(!response.responseText){
42217             return true;
42218         }
42219         this.result = this.handleResponse(response);
42220         return this.result;
42221     },
42222
42223     // utility functions used internally
42224     getUrl : function(appendParams){
42225         var url = this.options.url || this.form.url || this.form.el.dom.action;
42226         if(appendParams){
42227             var p = this.getParams();
42228             if(p){
42229                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42230             }
42231         }
42232         return url;
42233     },
42234
42235     getMethod : function(){
42236         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42237     },
42238
42239     getParams : function(){
42240         var bp = this.form.baseParams;
42241         var p = this.options.params;
42242         if(p){
42243             if(typeof p == "object"){
42244                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42245             }else if(typeof p == 'string' && bp){
42246                 p += '&' + Roo.urlEncode(bp);
42247             }
42248         }else if(bp){
42249             p = Roo.urlEncode(bp);
42250         }
42251         return p;
42252     },
42253
42254     createCallback : function(){
42255         return {
42256             success: this.success,
42257             failure: this.failure,
42258             scope: this,
42259             timeout: (this.form.timeout*1000),
42260             upload: this.form.fileUpload ? this.success : undefined
42261         };
42262     }
42263 };
42264
42265 Roo.form.Action.Submit = function(form, options){
42266     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42267 };
42268
42269 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42270     type : 'submit',
42271
42272     haveProgress : false,
42273     uploadComplete : false,
42274     
42275     // uploadProgress indicator.
42276     uploadProgress : function()
42277     {
42278         if (!this.form.progressUrl) {
42279             return;
42280         }
42281         
42282         if (!this.haveProgress) {
42283             Roo.MessageBox.progress("Uploading", "Uploading");
42284         }
42285         if (this.uploadComplete) {
42286            Roo.MessageBox.hide();
42287            return;
42288         }
42289         
42290         this.haveProgress = true;
42291    
42292         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42293         
42294         var c = new Roo.data.Connection();
42295         c.request({
42296             url : this.form.progressUrl,
42297             params: {
42298                 id : uid
42299             },
42300             method: 'GET',
42301             success : function(req){
42302                //console.log(data);
42303                 var rdata = false;
42304                 var edata;
42305                 try  {
42306                    rdata = Roo.decode(req.responseText)
42307                 } catch (e) {
42308                     Roo.log("Invalid data from server..");
42309                     Roo.log(edata);
42310                     return;
42311                 }
42312                 if (!rdata || !rdata.success) {
42313                     Roo.log(rdata);
42314                     return;
42315                 }
42316                 var data = rdata.data;
42317                 
42318                 if (this.uploadComplete) {
42319                    Roo.MessageBox.hide();
42320                    return;
42321                 }
42322                    
42323                 if (data){
42324                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42325                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42326                     );
42327                 }
42328                 this.uploadProgress.defer(2000,this);
42329             },
42330        
42331             failure: function(data) {
42332                 Roo.log('progress url failed ');
42333                 Roo.log(data);
42334             },
42335             scope : this
42336         });
42337            
42338     },
42339     
42340     
42341     run : function()
42342     {
42343         // run get Values on the form, so it syncs any secondary forms.
42344         this.form.getValues();
42345         
42346         var o = this.options;
42347         var method = this.getMethod();
42348         var isPost = method == 'POST';
42349         if(o.clientValidation === false || this.form.isValid()){
42350             
42351             if (this.form.progressUrl) {
42352                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42353                     (new Date() * 1) + '' + Math.random());
42354                     
42355             } 
42356             
42357             
42358             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42359                 form:this.form.el.dom,
42360                 url:this.getUrl(!isPost),
42361                 method: method,
42362                 params:isPost ? this.getParams() : null,
42363                 isUpload: this.form.fileUpload
42364             }));
42365             
42366             this.uploadProgress();
42367
42368         }else if (o.clientValidation !== false){ // client validation failed
42369             this.failureType = Roo.form.Action.CLIENT_INVALID;
42370             this.form.afterAction(this, false);
42371         }
42372     },
42373
42374     success : function(response)
42375     {
42376         this.uploadComplete= true;
42377         if (this.haveProgress) {
42378             Roo.MessageBox.hide();
42379         }
42380         
42381         
42382         var result = this.processResponse(response);
42383         if(result === true || result.success){
42384             this.form.afterAction(this, true);
42385             return;
42386         }
42387         if(result.errors){
42388             this.form.markInvalid(result.errors);
42389             this.failureType = Roo.form.Action.SERVER_INVALID;
42390         }
42391         this.form.afterAction(this, false);
42392     },
42393     failure : function(response)
42394     {
42395         this.uploadComplete= true;
42396         if (this.haveProgress) {
42397             Roo.MessageBox.hide();
42398         }
42399         
42400         this.response = response;
42401         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42402         this.form.afterAction(this, false);
42403     },
42404     
42405     handleResponse : function(response){
42406         if(this.form.errorReader){
42407             var rs = this.form.errorReader.read(response);
42408             var errors = [];
42409             if(rs.records){
42410                 for(var i = 0, len = rs.records.length; i < len; i++) {
42411                     var r = rs.records[i];
42412                     errors[i] = r.data;
42413                 }
42414             }
42415             if(errors.length < 1){
42416                 errors = null;
42417             }
42418             return {
42419                 success : rs.success,
42420                 errors : errors
42421             };
42422         }
42423         var ret = false;
42424         try {
42425             ret = Roo.decode(response.responseText);
42426         } catch (e) {
42427             ret = {
42428                 success: false,
42429                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42430                 errors : []
42431             };
42432         }
42433         return ret;
42434         
42435     }
42436 });
42437
42438
42439 Roo.form.Action.Load = function(form, options){
42440     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42441     this.reader = this.form.reader;
42442 };
42443
42444 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42445     type : 'load',
42446
42447     run : function(){
42448         
42449         Roo.Ajax.request(Roo.apply(
42450                 this.createCallback(), {
42451                     method:this.getMethod(),
42452                     url:this.getUrl(false),
42453                     params:this.getParams()
42454         }));
42455     },
42456
42457     success : function(response){
42458         
42459         var result = this.processResponse(response);
42460         if(result === true || !result.success || !result.data){
42461             this.failureType = Roo.form.Action.LOAD_FAILURE;
42462             this.form.afterAction(this, false);
42463             return;
42464         }
42465         this.form.clearInvalid();
42466         this.form.setValues(result.data);
42467         this.form.afterAction(this, true);
42468     },
42469
42470     handleResponse : function(response){
42471         if(this.form.reader){
42472             var rs = this.form.reader.read(response);
42473             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42474             return {
42475                 success : rs.success,
42476                 data : data
42477             };
42478         }
42479         return Roo.decode(response.responseText);
42480     }
42481 });
42482
42483 Roo.form.Action.ACTION_TYPES = {
42484     'load' : Roo.form.Action.Load,
42485     'submit' : Roo.form.Action.Submit
42486 };/*
42487  * Based on:
42488  * Ext JS Library 1.1.1
42489  * Copyright(c) 2006-2007, Ext JS, LLC.
42490  *
42491  * Originally Released Under LGPL - original licence link has changed is not relivant.
42492  *
42493  * Fork - LGPL
42494  * <script type="text/javascript">
42495  */
42496  
42497 /**
42498  * @class Roo.form.Layout
42499  * @extends Roo.Component
42500  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42501  * @constructor
42502  * @param {Object} config Configuration options
42503  */
42504 Roo.form.Layout = function(config){
42505     var xitems = [];
42506     if (config.items) {
42507         xitems = config.items;
42508         delete config.items;
42509     }
42510     Roo.form.Layout.superclass.constructor.call(this, config);
42511     this.stack = [];
42512     Roo.each(xitems, this.addxtype, this);
42513      
42514 };
42515
42516 Roo.extend(Roo.form.Layout, Roo.Component, {
42517     /**
42518      * @cfg {String/Object} autoCreate
42519      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42520      */
42521     /**
42522      * @cfg {String/Object/Function} style
42523      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42524      * a function which returns such a specification.
42525      */
42526     /**
42527      * @cfg {String} labelAlign
42528      * Valid values are "left," "top" and "right" (defaults to "left")
42529      */
42530     /**
42531      * @cfg {Number} labelWidth
42532      * Fixed width in pixels of all field labels (defaults to undefined)
42533      */
42534     /**
42535      * @cfg {Boolean} clear
42536      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42537      */
42538     clear : true,
42539     /**
42540      * @cfg {String} labelSeparator
42541      * The separator to use after field labels (defaults to ':')
42542      */
42543     labelSeparator : ':',
42544     /**
42545      * @cfg {Boolean} hideLabels
42546      * True to suppress the display of field labels in this layout (defaults to false)
42547      */
42548     hideLabels : false,
42549
42550     // private
42551     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42552     
42553     isLayout : true,
42554     
42555     // private
42556     onRender : function(ct, position){
42557         if(this.el){ // from markup
42558             this.el = Roo.get(this.el);
42559         }else {  // generate
42560             var cfg = this.getAutoCreate();
42561             this.el = ct.createChild(cfg, position);
42562         }
42563         if(this.style){
42564             this.el.applyStyles(this.style);
42565         }
42566         if(this.labelAlign){
42567             this.el.addClass('x-form-label-'+this.labelAlign);
42568         }
42569         if(this.hideLabels){
42570             this.labelStyle = "display:none";
42571             this.elementStyle = "padding-left:0;";
42572         }else{
42573             if(typeof this.labelWidth == 'number'){
42574                 this.labelStyle = "width:"+this.labelWidth+"px;";
42575                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42576             }
42577             if(this.labelAlign == 'top'){
42578                 this.labelStyle = "width:auto;";
42579                 this.elementStyle = "padding-left:0;";
42580             }
42581         }
42582         var stack = this.stack;
42583         var slen = stack.length;
42584         if(slen > 0){
42585             if(!this.fieldTpl){
42586                 var t = new Roo.Template(
42587                     '<div class="x-form-item {5}">',
42588                         '<label for="{0}" style="{2}">{1}{4}</label>',
42589                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42590                         '</div>',
42591                     '</div><div class="x-form-clear-left"></div>'
42592                 );
42593                 t.disableFormats = true;
42594                 t.compile();
42595                 Roo.form.Layout.prototype.fieldTpl = t;
42596             }
42597             for(var i = 0; i < slen; i++) {
42598                 if(stack[i].isFormField){
42599                     this.renderField(stack[i]);
42600                 }else{
42601                     this.renderComponent(stack[i]);
42602                 }
42603             }
42604         }
42605         if(this.clear){
42606             this.el.createChild({cls:'x-form-clear'});
42607         }
42608     },
42609
42610     // private
42611     renderField : function(f){
42612         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42613                f.id, //0
42614                f.fieldLabel, //1
42615                f.labelStyle||this.labelStyle||'', //2
42616                this.elementStyle||'', //3
42617                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42618                f.itemCls||this.itemCls||''  //5
42619        ], true).getPrevSibling());
42620     },
42621
42622     // private
42623     renderComponent : function(c){
42624         c.render(c.isLayout ? this.el : this.el.createChild());    
42625     },
42626     /**
42627      * Adds a object form elements (using the xtype property as the factory method.)
42628      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42629      * @param {Object} config 
42630      */
42631     addxtype : function(o)
42632     {
42633         // create the lement.
42634         o.form = this.form;
42635         var fe = Roo.factory(o, Roo.form);
42636         this.form.allItems.push(fe);
42637         this.stack.push(fe);
42638         
42639         if (fe.isFormField) {
42640             this.form.items.add(fe);
42641         }
42642          
42643         return fe;
42644     }
42645 });
42646
42647 /**
42648  * @class Roo.form.Column
42649  * @extends Roo.form.Layout
42650  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42651  * @constructor
42652  * @param {Object} config Configuration options
42653  */
42654 Roo.form.Column = function(config){
42655     Roo.form.Column.superclass.constructor.call(this, config);
42656 };
42657
42658 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42659     /**
42660      * @cfg {Number/String} width
42661      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42662      */
42663     /**
42664      * @cfg {String/Object} autoCreate
42665      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42666      */
42667
42668     // private
42669     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42670
42671     // private
42672     onRender : function(ct, position){
42673         Roo.form.Column.superclass.onRender.call(this, ct, position);
42674         if(this.width){
42675             this.el.setWidth(this.width);
42676         }
42677     }
42678 });
42679
42680
42681 /**
42682  * @class Roo.form.Row
42683  * @extends Roo.form.Layout
42684  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42685  * @constructor
42686  * @param {Object} config Configuration options
42687  */
42688
42689  
42690 Roo.form.Row = function(config){
42691     Roo.form.Row.superclass.constructor.call(this, config);
42692 };
42693  
42694 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42695       /**
42696      * @cfg {Number/String} width
42697      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42698      */
42699     /**
42700      * @cfg {Number/String} height
42701      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42702      */
42703     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42704     
42705     padWidth : 20,
42706     // private
42707     onRender : function(ct, position){
42708         //console.log('row render');
42709         if(!this.rowTpl){
42710             var t = new Roo.Template(
42711                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42712                     '<label for="{0}" style="{2}">{1}{4}</label>',
42713                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42714                     '</div>',
42715                 '</div>'
42716             );
42717             t.disableFormats = true;
42718             t.compile();
42719             Roo.form.Layout.prototype.rowTpl = t;
42720         }
42721         this.fieldTpl = this.rowTpl;
42722         
42723         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42724         var labelWidth = 100;
42725         
42726         if ((this.labelAlign != 'top')) {
42727             if (typeof this.labelWidth == 'number') {
42728                 labelWidth = this.labelWidth
42729             }
42730             this.padWidth =  20 + labelWidth;
42731             
42732         }
42733         
42734         Roo.form.Column.superclass.onRender.call(this, ct, position);
42735         if(this.width){
42736             this.el.setWidth(this.width);
42737         }
42738         if(this.height){
42739             this.el.setHeight(this.height);
42740         }
42741     },
42742     
42743     // private
42744     renderField : function(f){
42745         f.fieldEl = this.fieldTpl.append(this.el, [
42746                f.id, f.fieldLabel,
42747                f.labelStyle||this.labelStyle||'',
42748                this.elementStyle||'',
42749                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42750                f.itemCls||this.itemCls||'',
42751                f.width ? f.width + this.padWidth : 160 + this.padWidth
42752        ],true);
42753     }
42754 });
42755  
42756
42757 /**
42758  * @class Roo.form.FieldSet
42759  * @extends Roo.form.Layout
42760  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42761  * @constructor
42762  * @param {Object} config Configuration options
42763  */
42764 Roo.form.FieldSet = function(config){
42765     Roo.form.FieldSet.superclass.constructor.call(this, config);
42766 };
42767
42768 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42769     /**
42770      * @cfg {String} legend
42771      * The text to display as the legend for the FieldSet (defaults to '')
42772      */
42773     /**
42774      * @cfg {String/Object} autoCreate
42775      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42776      */
42777
42778     // private
42779     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42780
42781     // private
42782     onRender : function(ct, position){
42783         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42784         if(this.legend){
42785             this.setLegend(this.legend);
42786         }
42787     },
42788
42789     // private
42790     setLegend : function(text){
42791         if(this.rendered){
42792             this.el.child('legend').update(text);
42793         }
42794     }
42795 });/*
42796  * Based on:
42797  * Ext JS Library 1.1.1
42798  * Copyright(c) 2006-2007, Ext JS, LLC.
42799  *
42800  * Originally Released Under LGPL - original licence link has changed is not relivant.
42801  *
42802  * Fork - LGPL
42803  * <script type="text/javascript">
42804  */
42805 /**
42806  * @class Roo.form.VTypes
42807  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42808  * @singleton
42809  */
42810 Roo.form.VTypes = function(){
42811     // closure these in so they are only created once.
42812     var alpha = /^[a-zA-Z_]+$/;
42813     var alphanum = /^[a-zA-Z0-9_]+$/;
42814     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42815     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42816
42817     // All these messages and functions are configurable
42818     return {
42819         /**
42820          * The function used to validate email addresses
42821          * @param {String} value The email address
42822          */
42823         'email' : function(v){
42824             return email.test(v);
42825         },
42826         /**
42827          * The error text to display when the email validation function returns false
42828          * @type String
42829          */
42830         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42831         /**
42832          * The keystroke filter mask to be applied on email input
42833          * @type RegExp
42834          */
42835         'emailMask' : /[a-z0-9_\.\-@]/i,
42836
42837         /**
42838          * The function used to validate URLs
42839          * @param {String} value The URL
42840          */
42841         'url' : function(v){
42842             return url.test(v);
42843         },
42844         /**
42845          * The error text to display when the url validation function returns false
42846          * @type String
42847          */
42848         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42849         
42850         /**
42851          * The function used to validate alpha values
42852          * @param {String} value The value
42853          */
42854         'alpha' : function(v){
42855             return alpha.test(v);
42856         },
42857         /**
42858          * The error text to display when the alpha validation function returns false
42859          * @type String
42860          */
42861         'alphaText' : 'This field should only contain letters and _',
42862         /**
42863          * The keystroke filter mask to be applied on alpha input
42864          * @type RegExp
42865          */
42866         'alphaMask' : /[a-z_]/i,
42867
42868         /**
42869          * The function used to validate alphanumeric values
42870          * @param {String} value The value
42871          */
42872         'alphanum' : function(v){
42873             return alphanum.test(v);
42874         },
42875         /**
42876          * The error text to display when the alphanumeric validation function returns false
42877          * @type String
42878          */
42879         'alphanumText' : 'This field should only contain letters, numbers and _',
42880         /**
42881          * The keystroke filter mask to be applied on alphanumeric input
42882          * @type RegExp
42883          */
42884         'alphanumMask' : /[a-z0-9_]/i
42885     };
42886 }();//<script type="text/javascript">
42887
42888 /**
42889  * @class Roo.form.FCKeditor
42890  * @extends Roo.form.TextArea
42891  * Wrapper around the FCKEditor http://www.fckeditor.net
42892  * @constructor
42893  * Creates a new FCKeditor
42894  * @param {Object} config Configuration options
42895  */
42896 Roo.form.FCKeditor = function(config){
42897     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42898     this.addEvents({
42899          /**
42900          * @event editorinit
42901          * Fired when the editor is initialized - you can add extra handlers here..
42902          * @param {FCKeditor} this
42903          * @param {Object} the FCK object.
42904          */
42905         editorinit : true
42906     });
42907     
42908     
42909 };
42910 Roo.form.FCKeditor.editors = { };
42911 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42912 {
42913     //defaultAutoCreate : {
42914     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42915     //},
42916     // private
42917     /**
42918      * @cfg {Object} fck options - see fck manual for details.
42919      */
42920     fckconfig : false,
42921     
42922     /**
42923      * @cfg {Object} fck toolbar set (Basic or Default)
42924      */
42925     toolbarSet : 'Basic',
42926     /**
42927      * @cfg {Object} fck BasePath
42928      */ 
42929     basePath : '/fckeditor/',
42930     
42931     
42932     frame : false,
42933     
42934     value : '',
42935     
42936    
42937     onRender : function(ct, position)
42938     {
42939         if(!this.el){
42940             this.defaultAutoCreate = {
42941                 tag: "textarea",
42942                 style:"width:300px;height:60px;",
42943                 autocomplete: "off"
42944             };
42945         }
42946         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42947         /*
42948         if(this.grow){
42949             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42950             if(this.preventScrollbars){
42951                 this.el.setStyle("overflow", "hidden");
42952             }
42953             this.el.setHeight(this.growMin);
42954         }
42955         */
42956         //console.log('onrender' + this.getId() );
42957         Roo.form.FCKeditor.editors[this.getId()] = this;
42958          
42959
42960         this.replaceTextarea() ;
42961         
42962     },
42963     
42964     getEditor : function() {
42965         return this.fckEditor;
42966     },
42967     /**
42968      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42969      * @param {Mixed} value The value to set
42970      */
42971     
42972     
42973     setValue : function(value)
42974     {
42975         //console.log('setValue: ' + value);
42976         
42977         if(typeof(value) == 'undefined') { // not sure why this is happending...
42978             return;
42979         }
42980         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42981         
42982         //if(!this.el || !this.getEditor()) {
42983         //    this.value = value;
42984             //this.setValue.defer(100,this,[value]);    
42985         //    return;
42986         //} 
42987         
42988         if(!this.getEditor()) {
42989             return;
42990         }
42991         
42992         this.getEditor().SetData(value);
42993         
42994         //
42995
42996     },
42997
42998     /**
42999      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43000      * @return {Mixed} value The field value
43001      */
43002     getValue : function()
43003     {
43004         
43005         if (this.frame && this.frame.dom.style.display == 'none') {
43006             return Roo.form.FCKeditor.superclass.getValue.call(this);
43007         }
43008         
43009         if(!this.el || !this.getEditor()) {
43010            
43011            // this.getValue.defer(100,this); 
43012             return this.value;
43013         }
43014        
43015         
43016         var value=this.getEditor().GetData();
43017         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43018         return Roo.form.FCKeditor.superclass.getValue.call(this);
43019         
43020
43021     },
43022
43023     /**
43024      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43025      * @return {Mixed} value The field value
43026      */
43027     getRawValue : function()
43028     {
43029         if (this.frame && this.frame.dom.style.display == 'none') {
43030             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43031         }
43032         
43033         if(!this.el || !this.getEditor()) {
43034             //this.getRawValue.defer(100,this); 
43035             return this.value;
43036             return;
43037         }
43038         
43039         
43040         
43041         var value=this.getEditor().GetData();
43042         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43043         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43044          
43045     },
43046     
43047     setSize : function(w,h) {
43048         
43049         
43050         
43051         //if (this.frame && this.frame.dom.style.display == 'none') {
43052         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43053         //    return;
43054         //}
43055         //if(!this.el || !this.getEditor()) {
43056         //    this.setSize.defer(100,this, [w,h]); 
43057         //    return;
43058         //}
43059         
43060         
43061         
43062         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43063         
43064         this.frame.dom.setAttribute('width', w);
43065         this.frame.dom.setAttribute('height', h);
43066         this.frame.setSize(w,h);
43067         
43068     },
43069     
43070     toggleSourceEdit : function(value) {
43071         
43072       
43073          
43074         this.el.dom.style.display = value ? '' : 'none';
43075         this.frame.dom.style.display = value ?  'none' : '';
43076         
43077     },
43078     
43079     
43080     focus: function(tag)
43081     {
43082         if (this.frame.dom.style.display == 'none') {
43083             return Roo.form.FCKeditor.superclass.focus.call(this);
43084         }
43085         if(!this.el || !this.getEditor()) {
43086             this.focus.defer(100,this, [tag]); 
43087             return;
43088         }
43089         
43090         
43091         
43092         
43093         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43094         this.getEditor().Focus();
43095         if (tgs.length) {
43096             if (!this.getEditor().Selection.GetSelection()) {
43097                 this.focus.defer(100,this, [tag]); 
43098                 return;
43099             }
43100             
43101             
43102             var r = this.getEditor().EditorDocument.createRange();
43103             r.setStart(tgs[0],0);
43104             r.setEnd(tgs[0],0);
43105             this.getEditor().Selection.GetSelection().removeAllRanges();
43106             this.getEditor().Selection.GetSelection().addRange(r);
43107             this.getEditor().Focus();
43108         }
43109         
43110     },
43111     
43112     
43113     
43114     replaceTextarea : function()
43115     {
43116         if ( document.getElementById( this.getId() + '___Frame' ) )
43117             return ;
43118         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43119         //{
43120             // We must check the elements firstly using the Id and then the name.
43121         var oTextarea = document.getElementById( this.getId() );
43122         
43123         var colElementsByName = document.getElementsByName( this.getId() ) ;
43124          
43125         oTextarea.style.display = 'none' ;
43126
43127         if ( oTextarea.tabIndex ) {            
43128             this.TabIndex = oTextarea.tabIndex ;
43129         }
43130         
43131         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43132         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43133         this.frame = Roo.get(this.getId() + '___Frame')
43134     },
43135     
43136     _getConfigHtml : function()
43137     {
43138         var sConfig = '' ;
43139
43140         for ( var o in this.fckconfig ) {
43141             sConfig += sConfig.length > 0  ? '&amp;' : '';
43142             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43143         }
43144
43145         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43146     },
43147     
43148     
43149     _getIFrameHtml : function()
43150     {
43151         var sFile = 'fckeditor.html' ;
43152         /* no idea what this is about..
43153         try
43154         {
43155             if ( (/fcksource=true/i).test( window.top.location.search ) )
43156                 sFile = 'fckeditor.original.html' ;
43157         }
43158         catch (e) { 
43159         */
43160
43161         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43162         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43163         
43164         
43165         var html = '<iframe id="' + this.getId() +
43166             '___Frame" src="' + sLink +
43167             '" width="' + this.width +
43168             '" height="' + this.height + '"' +
43169             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43170             ' frameborder="0" scrolling="no"></iframe>' ;
43171
43172         return html ;
43173     },
43174     
43175     _insertHtmlBefore : function( html, element )
43176     {
43177         if ( element.insertAdjacentHTML )       {
43178             // IE
43179             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43180         } else { // Gecko
43181             var oRange = document.createRange() ;
43182             oRange.setStartBefore( element ) ;
43183             var oFragment = oRange.createContextualFragment( html );
43184             element.parentNode.insertBefore( oFragment, element ) ;
43185         }
43186     }
43187     
43188     
43189   
43190     
43191     
43192     
43193     
43194
43195 });
43196
43197 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43198
43199 function FCKeditor_OnComplete(editorInstance){
43200     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43201     f.fckEditor = editorInstance;
43202     //console.log("loaded");
43203     f.fireEvent('editorinit', f, editorInstance);
43204
43205   
43206
43207  
43208
43209
43210
43211
43212
43213
43214
43215
43216
43217
43218
43219
43220
43221
43222
43223 //<script type="text/javascript">
43224 /**
43225  * @class Roo.form.GridField
43226  * @extends Roo.form.Field
43227  * Embed a grid (or editable grid into a form)
43228  * STATUS ALPHA
43229  * 
43230  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43231  * it needs 
43232  * xgrid.store = Roo.data.Store
43233  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43234  * xgrid.store.reader = Roo.data.JsonReader 
43235  * 
43236  * 
43237  * @constructor
43238  * Creates a new GridField
43239  * @param {Object} config Configuration options
43240  */
43241 Roo.form.GridField = function(config){
43242     Roo.form.GridField.superclass.constructor.call(this, config);
43243      
43244 };
43245
43246 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43247     /**
43248      * @cfg {Number} width  - used to restrict width of grid..
43249      */
43250     width : 100,
43251     /**
43252      * @cfg {Number} height - used to restrict height of grid..
43253      */
43254     height : 50,
43255      /**
43256      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43257          * 
43258          *}
43259      */
43260     xgrid : false, 
43261     /**
43262      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43263      * {tag: "input", type: "checkbox", autocomplete: "off"})
43264      */
43265    // defaultAutoCreate : { tag: 'div' },
43266     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43267     /**
43268      * @cfg {String} addTitle Text to include for adding a title.
43269      */
43270     addTitle : false,
43271     //
43272     onResize : function(){
43273         Roo.form.Field.superclass.onResize.apply(this, arguments);
43274     },
43275
43276     initEvents : function(){
43277         // Roo.form.Checkbox.superclass.initEvents.call(this);
43278         // has no events...
43279        
43280     },
43281
43282
43283     getResizeEl : function(){
43284         return this.wrap;
43285     },
43286
43287     getPositionEl : function(){
43288         return this.wrap;
43289     },
43290
43291     // private
43292     onRender : function(ct, position){
43293         
43294         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43295         var style = this.style;
43296         delete this.style;
43297         
43298         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43299         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43300         this.viewEl = this.wrap.createChild({ tag: 'div' });
43301         if (style) {
43302             this.viewEl.applyStyles(style);
43303         }
43304         if (this.width) {
43305             this.viewEl.setWidth(this.width);
43306         }
43307         if (this.height) {
43308             this.viewEl.setHeight(this.height);
43309         }
43310         //if(this.inputValue !== undefined){
43311         //this.setValue(this.value);
43312         
43313         
43314         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43315         
43316         
43317         this.grid.render();
43318         this.grid.getDataSource().on('remove', this.refreshValue, this);
43319         this.grid.getDataSource().on('update', this.refreshValue, this);
43320         this.grid.on('afteredit', this.refreshValue, this);
43321  
43322     },
43323      
43324     
43325     /**
43326      * Sets the value of the item. 
43327      * @param {String} either an object  or a string..
43328      */
43329     setValue : function(v){
43330         //this.value = v;
43331         v = v || []; // empty set..
43332         // this does not seem smart - it really only affects memoryproxy grids..
43333         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43334             var ds = this.grid.getDataSource();
43335             // assumes a json reader..
43336             var data = {}
43337             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43338             ds.loadData( data);
43339         }
43340         // clear selection so it does not get stale.
43341         if (this.grid.sm) { 
43342             this.grid.sm.clearSelections();
43343         }
43344         
43345         Roo.form.GridField.superclass.setValue.call(this, v);
43346         this.refreshValue();
43347         // should load data in the grid really....
43348     },
43349     
43350     // private
43351     refreshValue: function() {
43352          var val = [];
43353         this.grid.getDataSource().each(function(r) {
43354             val.push(r.data);
43355         });
43356         this.el.dom.value = Roo.encode(val);
43357     }
43358     
43359      
43360     
43361     
43362 });/*
43363  * Based on:
43364  * Ext JS Library 1.1.1
43365  * Copyright(c) 2006-2007, Ext JS, LLC.
43366  *
43367  * Originally Released Under LGPL - original licence link has changed is not relivant.
43368  *
43369  * Fork - LGPL
43370  * <script type="text/javascript">
43371  */
43372 /**
43373  * @class Roo.form.DisplayField
43374  * @extends Roo.form.Field
43375  * A generic Field to display non-editable data.
43376  * @constructor
43377  * Creates a new Display Field item.
43378  * @param {Object} config Configuration options
43379  */
43380 Roo.form.DisplayField = function(config){
43381     Roo.form.DisplayField.superclass.constructor.call(this, config);
43382     
43383 };
43384
43385 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43386     inputType:      'hidden',
43387     allowBlank:     true,
43388     readOnly:         true,
43389     
43390  
43391     /**
43392      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43393      */
43394     focusClass : undefined,
43395     /**
43396      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43397      */
43398     fieldClass: 'x-form-field',
43399     
43400      /**
43401      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43402      */
43403     valueRenderer: undefined,
43404     
43405     width: 100,
43406     /**
43407      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43408      * {tag: "input", type: "checkbox", autocomplete: "off"})
43409      */
43410      
43411  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43412
43413     onResize : function(){
43414         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43415         
43416     },
43417
43418     initEvents : function(){
43419         // Roo.form.Checkbox.superclass.initEvents.call(this);
43420         // has no events...
43421        
43422     },
43423
43424
43425     getResizeEl : function(){
43426         return this.wrap;
43427     },
43428
43429     getPositionEl : function(){
43430         return this.wrap;
43431     },
43432
43433     // private
43434     onRender : function(ct, position){
43435         
43436         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43437         //if(this.inputValue !== undefined){
43438         this.wrap = this.el.wrap();
43439         
43440         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43441         
43442         if (this.bodyStyle) {
43443             this.viewEl.applyStyles(this.bodyStyle);
43444         }
43445         //this.viewEl.setStyle('padding', '2px');
43446         
43447         this.setValue(this.value);
43448         
43449     },
43450 /*
43451     // private
43452     initValue : Roo.emptyFn,
43453
43454   */
43455
43456         // private
43457     onClick : function(){
43458         
43459     },
43460
43461     /**
43462      * Sets the checked state of the checkbox.
43463      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43464      */
43465     setValue : function(v){
43466         this.value = v;
43467         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43468         // this might be called before we have a dom element..
43469         if (!this.viewEl) {
43470             return;
43471         }
43472         this.viewEl.dom.innerHTML = html;
43473         Roo.form.DisplayField.superclass.setValue.call(this, v);
43474
43475     }
43476 });/*
43477  * 
43478  * Licence- LGPL
43479  * 
43480  */
43481
43482 /**
43483  * @class Roo.form.DayPicker
43484  * @extends Roo.form.Field
43485  * A Day picker show [M] [T] [W] ....
43486  * @constructor
43487  * Creates a new Day Picker
43488  * @param {Object} config Configuration options
43489  */
43490 Roo.form.DayPicker= function(config){
43491     Roo.form.DayPicker.superclass.constructor.call(this, config);
43492      
43493 };
43494
43495 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43496     /**
43497      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43498      */
43499     focusClass : undefined,
43500     /**
43501      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43502      */
43503     fieldClass: "x-form-field",
43504    
43505     /**
43506      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43507      * {tag: "input", type: "checkbox", autocomplete: "off"})
43508      */
43509     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43510     
43511    
43512     actionMode : 'viewEl', 
43513     //
43514     // private
43515  
43516     inputType : 'hidden',
43517     
43518      
43519     inputElement: false, // real input element?
43520     basedOn: false, // ????
43521     
43522     isFormField: true, // not sure where this is needed!!!!
43523
43524     onResize : function(){
43525         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43526         if(!this.boxLabel){
43527             this.el.alignTo(this.wrap, 'c-c');
43528         }
43529     },
43530
43531     initEvents : function(){
43532         Roo.form.Checkbox.superclass.initEvents.call(this);
43533         this.el.on("click", this.onClick,  this);
43534         this.el.on("change", this.onClick,  this);
43535     },
43536
43537
43538     getResizeEl : function(){
43539         return this.wrap;
43540     },
43541
43542     getPositionEl : function(){
43543         return this.wrap;
43544     },
43545
43546     
43547     // private
43548     onRender : function(ct, position){
43549         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43550        
43551         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43552         
43553         var r1 = '<table><tr>';
43554         var r2 = '<tr class="x-form-daypick-icons">';
43555         for (var i=0; i < 7; i++) {
43556             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43557             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43558         }
43559         
43560         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43561         viewEl.select('img').on('click', this.onClick, this);
43562         this.viewEl = viewEl;   
43563         
43564         
43565         // this will not work on Chrome!!!
43566         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43567         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43568         
43569         
43570           
43571
43572     },
43573
43574     // private
43575     initValue : Roo.emptyFn,
43576
43577     /**
43578      * Returns the checked state of the checkbox.
43579      * @return {Boolean} True if checked, else false
43580      */
43581     getValue : function(){
43582         return this.el.dom.value;
43583         
43584     },
43585
43586         // private
43587     onClick : function(e){ 
43588         //this.setChecked(!this.checked);
43589         Roo.get(e.target).toggleClass('x-menu-item-checked');
43590         this.refreshValue();
43591         //if(this.el.dom.checked != this.checked){
43592         //    this.setValue(this.el.dom.checked);
43593        // }
43594     },
43595     
43596     // private
43597     refreshValue : function()
43598     {
43599         var val = '';
43600         this.viewEl.select('img',true).each(function(e,i,n)  {
43601             val += e.is(".x-menu-item-checked") ? String(n) : '';
43602         });
43603         this.setValue(val, true);
43604     },
43605
43606     /**
43607      * Sets the checked state of the checkbox.
43608      * On is always based on a string comparison between inputValue and the param.
43609      * @param {Boolean/String} value - the value to set 
43610      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43611      */
43612     setValue : function(v,suppressEvent){
43613         if (!this.el.dom) {
43614             return;
43615         }
43616         var old = this.el.dom.value ;
43617         this.el.dom.value = v;
43618         if (suppressEvent) {
43619             return ;
43620         }
43621          
43622         // update display..
43623         this.viewEl.select('img',true).each(function(e,i,n)  {
43624             
43625             var on = e.is(".x-menu-item-checked");
43626             var newv = v.indexOf(String(n)) > -1;
43627             if (on != newv) {
43628                 e.toggleClass('x-menu-item-checked');
43629             }
43630             
43631         });
43632         
43633         
43634         this.fireEvent('change', this, v, old);
43635         
43636         
43637     },
43638    
43639     // handle setting of hidden value by some other method!!?!?
43640     setFromHidden: function()
43641     {
43642         if(!this.el){
43643             return;
43644         }
43645         //console.log("SET FROM HIDDEN");
43646         //alert('setFrom hidden');
43647         this.setValue(this.el.dom.value);
43648     },
43649     
43650     onDestroy : function()
43651     {
43652         if(this.viewEl){
43653             Roo.get(this.viewEl).remove();
43654         }
43655          
43656         Roo.form.DayPicker.superclass.onDestroy.call(this);
43657     }
43658
43659 });/*
43660  * RooJS Library 1.1.1
43661  * Copyright(c) 2008-2011  Alan Knowles
43662  *
43663  * License - LGPL
43664  */
43665  
43666
43667 /**
43668  * @class Roo.form.ComboCheck
43669  * @extends Roo.form.ComboBox
43670  * A combobox for multiple select items.
43671  *
43672  * FIXME - could do with a reset button..
43673  * 
43674  * @constructor
43675  * Create a new ComboCheck
43676  * @param {Object} config Configuration options
43677  */
43678 Roo.form.ComboCheck = function(config){
43679     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43680     // should verify some data...
43681     // like
43682     // hiddenName = required..
43683     // displayField = required
43684     // valudField == required
43685     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43686     var _t = this;
43687     Roo.each(req, function(e) {
43688         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43689             throw "Roo.form.ComboCheck : missing value for: " + e;
43690         }
43691     });
43692     
43693     
43694 };
43695
43696 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43697      
43698      
43699     editable : false,
43700      
43701     selectedClass: 'x-menu-item-checked', 
43702     
43703     // private
43704     onRender : function(ct, position){
43705         var _t = this;
43706         
43707         
43708         
43709         if(!this.tpl){
43710             var cls = 'x-combo-list';
43711
43712             
43713             this.tpl =  new Roo.Template({
43714                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43715                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43716                    '<span>{' + this.displayField + '}</span>' +
43717                     '</div>' 
43718                 
43719             });
43720         }
43721  
43722         
43723         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43724         this.view.singleSelect = false;
43725         this.view.multiSelect = true;
43726         this.view.toggleSelect = true;
43727         this.pageTb.add(new Roo.Toolbar.Fill(), {
43728             
43729             text: 'Done',
43730             handler: function()
43731             {
43732                 _t.collapse();
43733             }
43734         });
43735     },
43736     
43737     onViewOver : function(e, t){
43738         // do nothing...
43739         return;
43740         
43741     },
43742     
43743     onViewClick : function(doFocus,index){
43744         return;
43745         
43746     },
43747     select: function () {
43748         //Roo.log("SELECT CALLED");
43749     },
43750      
43751     selectByValue : function(xv, scrollIntoView){
43752         var ar = this.getValueArray();
43753         var sels = [];
43754         
43755         Roo.each(ar, function(v) {
43756             if(v === undefined || v === null){
43757                 return;
43758             }
43759             var r = this.findRecord(this.valueField, v);
43760             if(r){
43761                 sels.push(this.store.indexOf(r))
43762                 
43763             }
43764         },this);
43765         this.view.select(sels);
43766         return false;
43767     },
43768     
43769     
43770     
43771     onSelect : function(record, index){
43772        // Roo.log("onselect Called");
43773        // this is only called by the clear button now..
43774         this.view.clearSelections();
43775         this.setValue('[]');
43776         if (this.value != this.valueBefore) {
43777             this.fireEvent('change', this, this.value, this.valueBefore);
43778         }
43779     },
43780     getValueArray : function()
43781     {
43782         var ar = [] ;
43783         
43784         try {
43785             //Roo.log(this.value);
43786             if (typeof(this.value) == 'undefined') {
43787                 return [];
43788             }
43789             var ar = Roo.decode(this.value);
43790             return  ar instanceof Array ? ar : []; //?? valid?
43791             
43792         } catch(e) {
43793             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43794             return [];
43795         }
43796          
43797     },
43798     expand : function ()
43799     {
43800         Roo.form.ComboCheck.superclass.expand.call(this);
43801         this.valueBefore = this.value;
43802         
43803
43804     },
43805     
43806     collapse : function(){
43807         Roo.form.ComboCheck.superclass.collapse.call(this);
43808         var sl = this.view.getSelectedIndexes();
43809         var st = this.store;
43810         var nv = [];
43811         var tv = [];
43812         var r;
43813         Roo.each(sl, function(i) {
43814             r = st.getAt(i);
43815             nv.push(r.get(this.valueField));
43816         },this);
43817         this.setValue(Roo.encode(nv));
43818         if (this.value != this.valueBefore) {
43819
43820             this.fireEvent('change', this, this.value, this.valueBefore);
43821         }
43822         
43823     },
43824     
43825     setValue : function(v){
43826         // Roo.log(v);
43827         this.value = v;
43828         
43829         var vals = this.getValueArray();
43830         var tv = [];
43831         Roo.each(vals, function(k) {
43832             var r = this.findRecord(this.valueField, k);
43833             if(r){
43834                 tv.push(r.data[this.displayField]);
43835             }else if(this.valueNotFoundText !== undefined){
43836                 tv.push( this.valueNotFoundText );
43837             }
43838         },this);
43839        // Roo.log(tv);
43840         
43841         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43842         this.hiddenField.value = v;
43843         this.value = v;
43844     }
43845     
43846 });//<script type="text/javasscript">
43847  
43848
43849 /**
43850  * @class Roo.DDView
43851  * A DnD enabled version of Roo.View.
43852  * @param {Element/String} container The Element in which to create the View.
43853  * @param {String} tpl The template string used to create the markup for each element of the View
43854  * @param {Object} config The configuration properties. These include all the config options of
43855  * {@link Roo.View} plus some specific to this class.<br>
43856  * <p>
43857  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43858  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43859  * <p>
43860  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43861 .x-view-drag-insert-above {
43862         border-top:1px dotted #3366cc;
43863 }
43864 .x-view-drag-insert-below {
43865         border-bottom:1px dotted #3366cc;
43866 }
43867 </code></pre>
43868  * 
43869  */
43870  
43871 Roo.DDView = function(container, tpl, config) {
43872     Roo.DDView.superclass.constructor.apply(this, arguments);
43873     this.getEl().setStyle("outline", "0px none");
43874     this.getEl().unselectable();
43875     if (this.dragGroup) {
43876                 this.setDraggable(this.dragGroup.split(","));
43877     }
43878     if (this.dropGroup) {
43879                 this.setDroppable(this.dropGroup.split(","));
43880     }
43881     if (this.deletable) {
43882         this.setDeletable();
43883     }
43884     this.isDirtyFlag = false;
43885         this.addEvents({
43886                 "drop" : true
43887         });
43888 };
43889
43890 Roo.extend(Roo.DDView, Roo.View, {
43891 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43892 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43893 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43894 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43895
43896         isFormField: true,
43897
43898         reset: Roo.emptyFn,
43899         
43900         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43901
43902         validate: function() {
43903                 return true;
43904         },
43905         
43906         destroy: function() {
43907                 this.purgeListeners();
43908                 this.getEl.removeAllListeners();
43909                 this.getEl().remove();
43910                 if (this.dragZone) {
43911                         if (this.dragZone.destroy) {
43912                                 this.dragZone.destroy();
43913                         }
43914                 }
43915                 if (this.dropZone) {
43916                         if (this.dropZone.destroy) {
43917                                 this.dropZone.destroy();
43918                         }
43919                 }
43920         },
43921
43922 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43923         getName: function() {
43924                 return this.name;
43925         },
43926
43927 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43928         setValue: function(v) {
43929                 if (!this.store) {
43930                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43931                 }
43932                 var data = {};
43933                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43934                 this.store.proxy = new Roo.data.MemoryProxy(data);
43935                 this.store.load();
43936         },
43937
43938 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43939         getValue: function() {
43940                 var result = '(';
43941                 this.store.each(function(rec) {
43942                         result += rec.id + ',';
43943                 });
43944                 return result.substr(0, result.length - 1) + ')';
43945         },
43946         
43947         getIds: function() {
43948                 var i = 0, result = new Array(this.store.getCount());
43949                 this.store.each(function(rec) {
43950                         result[i++] = rec.id;
43951                 });
43952                 return result;
43953         },
43954         
43955         isDirty: function() {
43956                 return this.isDirtyFlag;
43957         },
43958
43959 /**
43960  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43961  *      whole Element becomes the target, and this causes the drop gesture to append.
43962  */
43963     getTargetFromEvent : function(e) {
43964                 var target = e.getTarget();
43965                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43966                 target = target.parentNode;
43967                 }
43968                 if (!target) {
43969                         target = this.el.dom.lastChild || this.el.dom;
43970                 }
43971                 return target;
43972     },
43973
43974 /**
43975  *      Create the drag data which consists of an object which has the property "ddel" as
43976  *      the drag proxy element. 
43977  */
43978     getDragData : function(e) {
43979         var target = this.findItemFromChild(e.getTarget());
43980                 if(target) {
43981                         this.handleSelection(e);
43982                         var selNodes = this.getSelectedNodes();
43983             var dragData = {
43984                 source: this,
43985                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43986                 nodes: selNodes,
43987                 records: []
43988                         };
43989                         var selectedIndices = this.getSelectedIndexes();
43990                         for (var i = 0; i < selectedIndices.length; i++) {
43991                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43992                         }
43993                         if (selNodes.length == 1) {
43994                                 dragData.ddel = target.cloneNode(true); // the div element
43995                         } else {
43996                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43997                                 div.className = 'multi-proxy';
43998                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43999                                         div.appendChild(selNodes[i].cloneNode(true));
44000                                 }
44001                                 dragData.ddel = div;
44002                         }
44003             //console.log(dragData)
44004             //console.log(dragData.ddel.innerHTML)
44005                         return dragData;
44006                 }
44007         //console.log('nodragData')
44008                 return false;
44009     },
44010     
44011 /**     Specify to which ddGroup items in this DDView may be dragged. */
44012     setDraggable: function(ddGroup) {
44013         if (ddGroup instanceof Array) {
44014                 Roo.each(ddGroup, this.setDraggable, this);
44015                 return;
44016         }
44017         if (this.dragZone) {
44018                 this.dragZone.addToGroup(ddGroup);
44019         } else {
44020                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44021                                 containerScroll: true,
44022                                 ddGroup: ddGroup 
44023
44024                         });
44025 //                      Draggability implies selection. DragZone's mousedown selects the element.
44026                         if (!this.multiSelect) { this.singleSelect = true; }
44027
44028 //                      Wire the DragZone's handlers up to methods in *this*
44029                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44030                 }
44031     },
44032
44033 /**     Specify from which ddGroup this DDView accepts drops. */
44034     setDroppable: function(ddGroup) {
44035         if (ddGroup instanceof Array) {
44036                 Roo.each(ddGroup, this.setDroppable, this);
44037                 return;
44038         }
44039         if (this.dropZone) {
44040                 this.dropZone.addToGroup(ddGroup);
44041         } else {
44042                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44043                                 containerScroll: true,
44044                                 ddGroup: ddGroup
44045                         });
44046
44047 //                      Wire the DropZone's handlers up to methods in *this*
44048                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44049                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44050                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44051                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44052                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44053                 }
44054     },
44055
44056 /**     Decide whether to drop above or below a View node. */
44057     getDropPoint : function(e, n, dd){
44058         if (n == this.el.dom) { return "above"; }
44059                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44060                 var c = t + (b - t) / 2;
44061                 var y = Roo.lib.Event.getPageY(e);
44062                 if(y <= c) {
44063                         return "above";
44064                 }else{
44065                         return "below";
44066                 }
44067     },
44068
44069     onNodeEnter : function(n, dd, e, data){
44070                 return false;
44071     },
44072     
44073     onNodeOver : function(n, dd, e, data){
44074                 var pt = this.getDropPoint(e, n, dd);
44075                 // set the insert point style on the target node
44076                 var dragElClass = this.dropNotAllowed;
44077                 if (pt) {
44078                         var targetElClass;
44079                         if (pt == "above"){
44080                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44081                                 targetElClass = "x-view-drag-insert-above";
44082                         } else {
44083                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44084                                 targetElClass = "x-view-drag-insert-below";
44085                         }
44086                         if (this.lastInsertClass != targetElClass){
44087                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44088                                 this.lastInsertClass = targetElClass;
44089                         }
44090                 }
44091                 return dragElClass;
44092         },
44093
44094     onNodeOut : function(n, dd, e, data){
44095                 this.removeDropIndicators(n);
44096     },
44097
44098     onNodeDrop : function(n, dd, e, data){
44099         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44100                 return false;
44101         }
44102         var pt = this.getDropPoint(e, n, dd);
44103                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44104                 if (pt == "below") { insertAt++; }
44105                 for (var i = 0; i < data.records.length; i++) {
44106                         var r = data.records[i];
44107                         var dup = this.store.getById(r.id);
44108                         if (dup && (dd != this.dragZone)) {
44109                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44110                         } else {
44111                                 if (data.copy) {
44112                                         this.store.insert(insertAt++, r.copy());
44113                                 } else {
44114                                         data.source.isDirtyFlag = true;
44115                                         r.store.remove(r);
44116                                         this.store.insert(insertAt++, r);
44117                                 }
44118                                 this.isDirtyFlag = true;
44119                         }
44120                 }
44121                 this.dragZone.cachedTarget = null;
44122                 return true;
44123     },
44124
44125     removeDropIndicators : function(n){
44126                 if(n){
44127                         Roo.fly(n).removeClass([
44128                                 "x-view-drag-insert-above",
44129                                 "x-view-drag-insert-below"]);
44130                         this.lastInsertClass = "_noclass";
44131                 }
44132     },
44133
44134 /**
44135  *      Utility method. Add a delete option to the DDView's context menu.
44136  *      @param {String} imageUrl The URL of the "delete" icon image.
44137  */
44138         setDeletable: function(imageUrl) {
44139                 if (!this.singleSelect && !this.multiSelect) {
44140                         this.singleSelect = true;
44141                 }
44142                 var c = this.getContextMenu();
44143                 this.contextMenu.on("itemclick", function(item) {
44144                         switch (item.id) {
44145                                 case "delete":
44146                                         this.remove(this.getSelectedIndexes());
44147                                         break;
44148                         }
44149                 }, this);
44150                 this.contextMenu.add({
44151                         icon: imageUrl,
44152                         id: "delete",
44153                         text: 'Delete'
44154                 });
44155         },
44156         
44157 /**     Return the context menu for this DDView. */
44158         getContextMenu: function() {
44159                 if (!this.contextMenu) {
44160 //                      Create the View's context menu
44161                         this.contextMenu = new Roo.menu.Menu({
44162                                 id: this.id + "-contextmenu"
44163                         });
44164                         this.el.on("contextmenu", this.showContextMenu, this);
44165                 }
44166                 return this.contextMenu;
44167         },
44168         
44169         disableContextMenu: function() {
44170                 if (this.contextMenu) {
44171                         this.el.un("contextmenu", this.showContextMenu, this);
44172                 }
44173         },
44174
44175         showContextMenu: function(e, item) {
44176         item = this.findItemFromChild(e.getTarget());
44177                 if (item) {
44178                         e.stopEvent();
44179                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44180                         this.contextMenu.showAt(e.getXY());
44181             }
44182     },
44183
44184 /**
44185  *      Remove {@link Roo.data.Record}s at the specified indices.
44186  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44187  */
44188     remove: function(selectedIndices) {
44189                 selectedIndices = [].concat(selectedIndices);
44190                 for (var i = 0; i < selectedIndices.length; i++) {
44191                         var rec = this.store.getAt(selectedIndices[i]);
44192                         this.store.remove(rec);
44193                 }
44194     },
44195
44196 /**
44197  *      Double click fires the event, but also, if this is draggable, and there is only one other
44198  *      related DropZone, it transfers the selected node.
44199  */
44200     onDblClick : function(e){
44201         var item = this.findItemFromChild(e.getTarget());
44202         if(item){
44203             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44204                 return false;
44205             }
44206             if (this.dragGroup) {
44207                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44208                     while (targets.indexOf(this.dropZone) > -1) {
44209                             targets.remove(this.dropZone);
44210                                 }
44211                     if (targets.length == 1) {
44212                                         this.dragZone.cachedTarget = null;
44213                         var el = Roo.get(targets[0].getEl());
44214                         var box = el.getBox(true);
44215                         targets[0].onNodeDrop(el.dom, {
44216                                 target: el.dom,
44217                                 xy: [box.x, box.y + box.height - 1]
44218                         }, null, this.getDragData(e));
44219                     }
44220                 }
44221         }
44222     },
44223     
44224     handleSelection: function(e) {
44225                 this.dragZone.cachedTarget = null;
44226         var item = this.findItemFromChild(e.getTarget());
44227         if (!item) {
44228                 this.clearSelections(true);
44229                 return;
44230         }
44231                 if (item && (this.multiSelect || this.singleSelect)){
44232                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44233                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44234                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44235                                 this.unselect(item);
44236                         } else {
44237                                 this.select(item, this.multiSelect && e.ctrlKey);
44238                                 this.lastSelection = item;
44239                         }
44240                 }
44241     },
44242
44243     onItemClick : function(item, index, e){
44244                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44245                         return false;
44246                 }
44247                 return true;
44248     },
44249
44250     unselect : function(nodeInfo, suppressEvent){
44251                 var node = this.getNode(nodeInfo);
44252                 if(node && this.isSelected(node)){
44253                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44254                                 Roo.fly(node).removeClass(this.selectedClass);
44255                                 this.selections.remove(node);
44256                                 if(!suppressEvent){
44257                                         this.fireEvent("selectionchange", this, this.selections);
44258                                 }
44259                         }
44260                 }
44261     }
44262 });
44263 /*
44264  * Based on:
44265  * Ext JS Library 1.1.1
44266  * Copyright(c) 2006-2007, Ext JS, LLC.
44267  *
44268  * Originally Released Under LGPL - original licence link has changed is not relivant.
44269  *
44270  * Fork - LGPL
44271  * <script type="text/javascript">
44272  */
44273  
44274 /**
44275  * @class Roo.LayoutManager
44276  * @extends Roo.util.Observable
44277  * Base class for layout managers.
44278  */
44279 Roo.LayoutManager = function(container, config){
44280     Roo.LayoutManager.superclass.constructor.call(this);
44281     this.el = Roo.get(container);
44282     // ie scrollbar fix
44283     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44284         document.body.scroll = "no";
44285     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44286         this.el.position('relative');
44287     }
44288     this.id = this.el.id;
44289     this.el.addClass("x-layout-container");
44290     /** false to disable window resize monitoring @type Boolean */
44291     this.monitorWindowResize = true;
44292     this.regions = {};
44293     this.addEvents({
44294         /**
44295          * @event layout
44296          * Fires when a layout is performed. 
44297          * @param {Roo.LayoutManager} this
44298          */
44299         "layout" : true,
44300         /**
44301          * @event regionresized
44302          * Fires when the user resizes a region. 
44303          * @param {Roo.LayoutRegion} region The resized region
44304          * @param {Number} newSize The new size (width for east/west, height for north/south)
44305          */
44306         "regionresized" : true,
44307         /**
44308          * @event regioncollapsed
44309          * Fires when a region is collapsed. 
44310          * @param {Roo.LayoutRegion} region The collapsed region
44311          */
44312         "regioncollapsed" : true,
44313         /**
44314          * @event regionexpanded
44315          * Fires when a region is expanded.  
44316          * @param {Roo.LayoutRegion} region The expanded region
44317          */
44318         "regionexpanded" : true
44319     });
44320     this.updating = false;
44321     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44322 };
44323
44324 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44325     /**
44326      * Returns true if this layout is currently being updated
44327      * @return {Boolean}
44328      */
44329     isUpdating : function(){
44330         return this.updating; 
44331     },
44332     
44333     /**
44334      * Suspend the LayoutManager from doing auto-layouts while
44335      * making multiple add or remove calls
44336      */
44337     beginUpdate : function(){
44338         this.updating = true;    
44339     },
44340     
44341     /**
44342      * Restore auto-layouts and optionally disable the manager from performing a layout
44343      * @param {Boolean} noLayout true to disable a layout update 
44344      */
44345     endUpdate : function(noLayout){
44346         this.updating = false;
44347         if(!noLayout){
44348             this.layout();
44349         }    
44350     },
44351     
44352     layout: function(){
44353         
44354     },
44355     
44356     onRegionResized : function(region, newSize){
44357         this.fireEvent("regionresized", region, newSize);
44358         this.layout();
44359     },
44360     
44361     onRegionCollapsed : function(region){
44362         this.fireEvent("regioncollapsed", region);
44363     },
44364     
44365     onRegionExpanded : function(region){
44366         this.fireEvent("regionexpanded", region);
44367     },
44368         
44369     /**
44370      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44371      * performs box-model adjustments.
44372      * @return {Object} The size as an object {width: (the width), height: (the height)}
44373      */
44374     getViewSize : function(){
44375         var size;
44376         if(this.el.dom != document.body){
44377             size = this.el.getSize();
44378         }else{
44379             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44380         }
44381         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44382         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44383         return size;
44384     },
44385     
44386     /**
44387      * Returns the Element this layout is bound to.
44388      * @return {Roo.Element}
44389      */
44390     getEl : function(){
44391         return this.el;
44392     },
44393     
44394     /**
44395      * Returns the specified region.
44396      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44397      * @return {Roo.LayoutRegion}
44398      */
44399     getRegion : function(target){
44400         return this.regions[target.toLowerCase()];
44401     },
44402     
44403     onWindowResize : function(){
44404         if(this.monitorWindowResize){
44405             this.layout();
44406         }
44407     }
44408 });/*
44409  * Based on:
44410  * Ext JS Library 1.1.1
44411  * Copyright(c) 2006-2007, Ext JS, LLC.
44412  *
44413  * Originally Released Under LGPL - original licence link has changed is not relivant.
44414  *
44415  * Fork - LGPL
44416  * <script type="text/javascript">
44417  */
44418 /**
44419  * @class Roo.BorderLayout
44420  * @extends Roo.LayoutManager
44421  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44422  * please see: <br><br>
44423  * <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>
44424  * <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>
44425  * Example:
44426  <pre><code>
44427  var layout = new Roo.BorderLayout(document.body, {
44428     north: {
44429         initialSize: 25,
44430         titlebar: false
44431     },
44432     west: {
44433         split:true,
44434         initialSize: 200,
44435         minSize: 175,
44436         maxSize: 400,
44437         titlebar: true,
44438         collapsible: true
44439     },
44440     east: {
44441         split:true,
44442         initialSize: 202,
44443         minSize: 175,
44444         maxSize: 400,
44445         titlebar: true,
44446         collapsible: true
44447     },
44448     south: {
44449         split:true,
44450         initialSize: 100,
44451         minSize: 100,
44452         maxSize: 200,
44453         titlebar: true,
44454         collapsible: true
44455     },
44456     center: {
44457         titlebar: true,
44458         autoScroll:true,
44459         resizeTabs: true,
44460         minTabWidth: 50,
44461         preferredTabWidth: 150
44462     }
44463 });
44464
44465 // shorthand
44466 var CP = Roo.ContentPanel;
44467
44468 layout.beginUpdate();
44469 layout.add("north", new CP("north", "North"));
44470 layout.add("south", new CP("south", {title: "South", closable: true}));
44471 layout.add("west", new CP("west", {title: "West"}));
44472 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44473 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44474 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44475 layout.getRegion("center").showPanel("center1");
44476 layout.endUpdate();
44477 </code></pre>
44478
44479 <b>The container the layout is rendered into can be either the body element or any other element.
44480 If it is not the body element, the container needs to either be an absolute positioned element,
44481 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44482 the container size if it is not the body element.</b>
44483
44484 * @constructor
44485 * Create a new BorderLayout
44486 * @param {String/HTMLElement/Element} container The container this layout is bound to
44487 * @param {Object} config Configuration options
44488  */
44489 Roo.BorderLayout = function(container, config){
44490     config = config || {};
44491     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44492     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44493     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44494         var target = this.factory.validRegions[i];
44495         if(config[target]){
44496             this.addRegion(target, config[target]);
44497         }
44498     }
44499 };
44500
44501 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44502     /**
44503      * Creates and adds a new region if it doesn't already exist.
44504      * @param {String} target The target region key (north, south, east, west or center).
44505      * @param {Object} config The regions config object
44506      * @return {BorderLayoutRegion} The new region
44507      */
44508     addRegion : function(target, config){
44509         if(!this.regions[target]){
44510             var r = this.factory.create(target, this, config);
44511             this.bindRegion(target, r);
44512         }
44513         return this.regions[target];
44514     },
44515
44516     // private (kinda)
44517     bindRegion : function(name, r){
44518         this.regions[name] = r;
44519         r.on("visibilitychange", this.layout, this);
44520         r.on("paneladded", this.layout, this);
44521         r.on("panelremoved", this.layout, this);
44522         r.on("invalidated", this.layout, this);
44523         r.on("resized", this.onRegionResized, this);
44524         r.on("collapsed", this.onRegionCollapsed, this);
44525         r.on("expanded", this.onRegionExpanded, this);
44526     },
44527
44528     /**
44529      * Performs a layout update.
44530      */
44531     layout : function(){
44532         if(this.updating) return;
44533         var size = this.getViewSize();
44534         var w = size.width;
44535         var h = size.height;
44536         var centerW = w;
44537         var centerH = h;
44538         var centerY = 0;
44539         var centerX = 0;
44540         //var x = 0, y = 0;
44541
44542         var rs = this.regions;
44543         var north = rs["north"];
44544         var south = rs["south"]; 
44545         var west = rs["west"];
44546         var east = rs["east"];
44547         var center = rs["center"];
44548         //if(this.hideOnLayout){ // not supported anymore
44549             //c.el.setStyle("display", "none");
44550         //}
44551         if(north && north.isVisible()){
44552             var b = north.getBox();
44553             var m = north.getMargins();
44554             b.width = w - (m.left+m.right);
44555             b.x = m.left;
44556             b.y = m.top;
44557             centerY = b.height + b.y + m.bottom;
44558             centerH -= centerY;
44559             north.updateBox(this.safeBox(b));
44560         }
44561         if(south && south.isVisible()){
44562             var b = south.getBox();
44563             var m = south.getMargins();
44564             b.width = w - (m.left+m.right);
44565             b.x = m.left;
44566             var totalHeight = (b.height + m.top + m.bottom);
44567             b.y = h - totalHeight + m.top;
44568             centerH -= totalHeight;
44569             south.updateBox(this.safeBox(b));
44570         }
44571         if(west && west.isVisible()){
44572             var b = west.getBox();
44573             var m = west.getMargins();
44574             b.height = centerH - (m.top+m.bottom);
44575             b.x = m.left;
44576             b.y = centerY + m.top;
44577             var totalWidth = (b.width + m.left + m.right);
44578             centerX += totalWidth;
44579             centerW -= totalWidth;
44580             west.updateBox(this.safeBox(b));
44581         }
44582         if(east && east.isVisible()){
44583             var b = east.getBox();
44584             var m = east.getMargins();
44585             b.height = centerH - (m.top+m.bottom);
44586             var totalWidth = (b.width + m.left + m.right);
44587             b.x = w - totalWidth + m.left;
44588             b.y = centerY + m.top;
44589             centerW -= totalWidth;
44590             east.updateBox(this.safeBox(b));
44591         }
44592         if(center){
44593             var m = center.getMargins();
44594             var centerBox = {
44595                 x: centerX + m.left,
44596                 y: centerY + m.top,
44597                 width: centerW - (m.left+m.right),
44598                 height: centerH - (m.top+m.bottom)
44599             };
44600             //if(this.hideOnLayout){
44601                 //center.el.setStyle("display", "block");
44602             //}
44603             center.updateBox(this.safeBox(centerBox));
44604         }
44605         this.el.repaint();
44606         this.fireEvent("layout", this);
44607     },
44608
44609     // private
44610     safeBox : function(box){
44611         box.width = Math.max(0, box.width);
44612         box.height = Math.max(0, box.height);
44613         return box;
44614     },
44615
44616     /**
44617      * Adds a ContentPanel (or subclass) to this layout.
44618      * @param {String} target The target region key (north, south, east, west or center).
44619      * @param {Roo.ContentPanel} panel The panel to add
44620      * @return {Roo.ContentPanel} The added panel
44621      */
44622     add : function(target, panel){
44623          
44624         target = target.toLowerCase();
44625         return this.regions[target].add(panel);
44626     },
44627
44628     /**
44629      * Remove a ContentPanel (or subclass) to this layout.
44630      * @param {String} target The target region key (north, south, east, west or center).
44631      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44632      * @return {Roo.ContentPanel} The removed panel
44633      */
44634     remove : function(target, panel){
44635         target = target.toLowerCase();
44636         return this.regions[target].remove(panel);
44637     },
44638
44639     /**
44640      * Searches all regions for a panel with the specified id
44641      * @param {String} panelId
44642      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44643      */
44644     findPanel : function(panelId){
44645         var rs = this.regions;
44646         for(var target in rs){
44647             if(typeof rs[target] != "function"){
44648                 var p = rs[target].getPanel(panelId);
44649                 if(p){
44650                     return p;
44651                 }
44652             }
44653         }
44654         return null;
44655     },
44656
44657     /**
44658      * Searches all regions for a panel with the specified id and activates (shows) it.
44659      * @param {String/ContentPanel} panelId The panels id or the panel itself
44660      * @return {Roo.ContentPanel} The shown panel or null
44661      */
44662     showPanel : function(panelId) {
44663       var rs = this.regions;
44664       for(var target in rs){
44665          var r = rs[target];
44666          if(typeof r != "function"){
44667             if(r.hasPanel(panelId)){
44668                return r.showPanel(panelId);
44669             }
44670          }
44671       }
44672       return null;
44673    },
44674
44675    /**
44676      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44677      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44678      */
44679     restoreState : function(provider){
44680         if(!provider){
44681             provider = Roo.state.Manager;
44682         }
44683         var sm = new Roo.LayoutStateManager();
44684         sm.init(this, provider);
44685     },
44686
44687     /**
44688      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44689      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44690      * a valid ContentPanel config object.  Example:
44691      * <pre><code>
44692 // Create the main layout
44693 var layout = new Roo.BorderLayout('main-ct', {
44694     west: {
44695         split:true,
44696         minSize: 175,
44697         titlebar: true
44698     },
44699     center: {
44700         title:'Components'
44701     }
44702 }, 'main-ct');
44703
44704 // Create and add multiple ContentPanels at once via configs
44705 layout.batchAdd({
44706    west: {
44707        id: 'source-files',
44708        autoCreate:true,
44709        title:'Ext Source Files',
44710        autoScroll:true,
44711        fitToFrame:true
44712    },
44713    center : {
44714        el: cview,
44715        autoScroll:true,
44716        fitToFrame:true,
44717        toolbar: tb,
44718        resizeEl:'cbody'
44719    }
44720 });
44721 </code></pre>
44722      * @param {Object} regions An object containing ContentPanel configs by region name
44723      */
44724     batchAdd : function(regions){
44725         this.beginUpdate();
44726         for(var rname in regions){
44727             var lr = this.regions[rname];
44728             if(lr){
44729                 this.addTypedPanels(lr, regions[rname]);
44730             }
44731         }
44732         this.endUpdate();
44733     },
44734
44735     // private
44736     addTypedPanels : function(lr, ps){
44737         if(typeof ps == 'string'){
44738             lr.add(new Roo.ContentPanel(ps));
44739         }
44740         else if(ps instanceof Array){
44741             for(var i =0, len = ps.length; i < len; i++){
44742                 this.addTypedPanels(lr, ps[i]);
44743             }
44744         }
44745         else if(!ps.events){ // raw config?
44746             var el = ps.el;
44747             delete ps.el; // prevent conflict
44748             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44749         }
44750         else {  // panel object assumed!
44751             lr.add(ps);
44752         }
44753     },
44754     /**
44755      * Adds a xtype elements to the layout.
44756      * <pre><code>
44757
44758 layout.addxtype({
44759        xtype : 'ContentPanel',
44760        region: 'west',
44761        items: [ .... ]
44762    }
44763 );
44764
44765 layout.addxtype({
44766         xtype : 'NestedLayoutPanel',
44767         region: 'west',
44768         layout: {
44769            center: { },
44770            west: { }   
44771         },
44772         items : [ ... list of content panels or nested layout panels.. ]
44773    }
44774 );
44775 </code></pre>
44776      * @param {Object} cfg Xtype definition of item to add.
44777      */
44778     addxtype : function(cfg)
44779     {
44780         // basically accepts a pannel...
44781         // can accept a layout region..!?!?
44782         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44783         
44784         if (!cfg.xtype.match(/Panel$/)) {
44785             return false;
44786         }
44787         var ret = false;
44788         
44789         if (typeof(cfg.region) == 'undefined') {
44790             Roo.log("Failed to add Panel, region was not set");
44791             Roo.log(cfg);
44792             return false;
44793         }
44794         var region = cfg.region;
44795         delete cfg.region;
44796         
44797           
44798         var xitems = [];
44799         if (cfg.items) {
44800             xitems = cfg.items;
44801             delete cfg.items;
44802         }
44803         var nb = false;
44804         
44805         switch(cfg.xtype) 
44806         {
44807             case 'ContentPanel':  // ContentPanel (el, cfg)
44808             case 'ScrollPanel':  // ContentPanel (el, cfg)
44809                 if(cfg.autoCreate) {
44810                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44811                 } else {
44812                     var el = this.el.createChild();
44813                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44814                 }
44815                 
44816                 this.add(region, ret);
44817                 break;
44818             
44819             
44820             case 'TreePanel': // our new panel!
44821                 cfg.el = this.el.createChild();
44822                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44823                 this.add(region, ret);
44824                 break;
44825             
44826             case 'NestedLayoutPanel': 
44827                 // create a new Layout (which is  a Border Layout...
44828                 var el = this.el.createChild();
44829                 var clayout = cfg.layout;
44830                 delete cfg.layout;
44831                 clayout.items   = clayout.items  || [];
44832                 // replace this exitems with the clayout ones..
44833                 xitems = clayout.items;
44834                  
44835                 
44836                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44837                     cfg.background = false;
44838                 }
44839                 var layout = new Roo.BorderLayout(el, clayout);
44840                 
44841                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44842                 //console.log('adding nested layout panel '  + cfg.toSource());
44843                 this.add(region, ret);
44844                 nb = {}; /// find first...
44845                 break;
44846                 
44847             case 'GridPanel': 
44848             
44849                 // needs grid and region
44850                 
44851                 //var el = this.getRegion(region).el.createChild();
44852                 var el = this.el.createChild();
44853                 // create the grid first...
44854                 
44855                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44856                 delete cfg.grid;
44857                 if (region == 'center' && this.active ) {
44858                     cfg.background = false;
44859                 }
44860                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44861                 
44862                 this.add(region, ret);
44863                 if (cfg.background) {
44864                     ret.on('activate', function(gp) {
44865                         if (!gp.grid.rendered) {
44866                             gp.grid.render();
44867                         }
44868                     });
44869                 } else {
44870                     grid.render();
44871                 }
44872                 break;
44873            
44874                
44875                 
44876                 
44877             default: 
44878                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44879                 return null;
44880              // GridPanel (grid, cfg)
44881             
44882         }
44883         this.beginUpdate();
44884         // add children..
44885         var region = '';
44886         var abn = {};
44887         Roo.each(xitems, function(i)  {
44888             region = nb && i.region ? i.region : false;
44889             
44890             var add = ret.addxtype(i);
44891            
44892             if (region) {
44893                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44894                 if (!i.background) {
44895                     abn[region] = nb[region] ;
44896                 }
44897             }
44898             
44899         });
44900         this.endUpdate();
44901
44902         // make the last non-background panel active..
44903         //if (nb) { Roo.log(abn); }
44904         if (nb) {
44905             
44906             for(var r in abn) {
44907                 region = this.getRegion(r);
44908                 if (region) {
44909                     // tried using nb[r], but it does not work..
44910                      
44911                     region.showPanel(abn[r]);
44912                    
44913                 }
44914             }
44915         }
44916         return ret;
44917         
44918     }
44919 });
44920
44921 /**
44922  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44923  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44924  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44925  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44926  * <pre><code>
44927 // shorthand
44928 var CP = Roo.ContentPanel;
44929
44930 var layout = Roo.BorderLayout.create({
44931     north: {
44932         initialSize: 25,
44933         titlebar: false,
44934         panels: [new CP("north", "North")]
44935     },
44936     west: {
44937         split:true,
44938         initialSize: 200,
44939         minSize: 175,
44940         maxSize: 400,
44941         titlebar: true,
44942         collapsible: true,
44943         panels: [new CP("west", {title: "West"})]
44944     },
44945     east: {
44946         split:true,
44947         initialSize: 202,
44948         minSize: 175,
44949         maxSize: 400,
44950         titlebar: true,
44951         collapsible: true,
44952         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44953     },
44954     south: {
44955         split:true,
44956         initialSize: 100,
44957         minSize: 100,
44958         maxSize: 200,
44959         titlebar: true,
44960         collapsible: true,
44961         panels: [new CP("south", {title: "South", closable: true})]
44962     },
44963     center: {
44964         titlebar: true,
44965         autoScroll:true,
44966         resizeTabs: true,
44967         minTabWidth: 50,
44968         preferredTabWidth: 150,
44969         panels: [
44970             new CP("center1", {title: "Close Me", closable: true}),
44971             new CP("center2", {title: "Center Panel", closable: false})
44972         ]
44973     }
44974 }, document.body);
44975
44976 layout.getRegion("center").showPanel("center1");
44977 </code></pre>
44978  * @param config
44979  * @param targetEl
44980  */
44981 Roo.BorderLayout.create = function(config, targetEl){
44982     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44983     layout.beginUpdate();
44984     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44985     for(var j = 0, jlen = regions.length; j < jlen; j++){
44986         var lr = regions[j];
44987         if(layout.regions[lr] && config[lr].panels){
44988             var r = layout.regions[lr];
44989             var ps = config[lr].panels;
44990             layout.addTypedPanels(r, ps);
44991         }
44992     }
44993     layout.endUpdate();
44994     return layout;
44995 };
44996
44997 // private
44998 Roo.BorderLayout.RegionFactory = {
44999     // private
45000     validRegions : ["north","south","east","west","center"],
45001
45002     // private
45003     create : function(target, mgr, config){
45004         target = target.toLowerCase();
45005         if(config.lightweight || config.basic){
45006             return new Roo.BasicLayoutRegion(mgr, config, target);
45007         }
45008         switch(target){
45009             case "north":
45010                 return new Roo.NorthLayoutRegion(mgr, config);
45011             case "south":
45012                 return new Roo.SouthLayoutRegion(mgr, config);
45013             case "east":
45014                 return new Roo.EastLayoutRegion(mgr, config);
45015             case "west":
45016                 return new Roo.WestLayoutRegion(mgr, config);
45017             case "center":
45018                 return new Roo.CenterLayoutRegion(mgr, config);
45019         }
45020         throw 'Layout region "'+target+'" not supported.';
45021     }
45022 };/*
45023  * Based on:
45024  * Ext JS Library 1.1.1
45025  * Copyright(c) 2006-2007, Ext JS, LLC.
45026  *
45027  * Originally Released Under LGPL - original licence link has changed is not relivant.
45028  *
45029  * Fork - LGPL
45030  * <script type="text/javascript">
45031  */
45032  
45033 /**
45034  * @class Roo.BasicLayoutRegion
45035  * @extends Roo.util.Observable
45036  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45037  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45038  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45039  */
45040 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45041     this.mgr = mgr;
45042     this.position  = pos;
45043     this.events = {
45044         /**
45045          * @scope Roo.BasicLayoutRegion
45046          */
45047         
45048         /**
45049          * @event beforeremove
45050          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45051          * @param {Roo.LayoutRegion} this
45052          * @param {Roo.ContentPanel} panel The panel
45053          * @param {Object} e The cancel event object
45054          */
45055         "beforeremove" : true,
45056         /**
45057          * @event invalidated
45058          * Fires when the layout for this region is changed.
45059          * @param {Roo.LayoutRegion} this
45060          */
45061         "invalidated" : true,
45062         /**
45063          * @event visibilitychange
45064          * Fires when this region is shown or hidden 
45065          * @param {Roo.LayoutRegion} this
45066          * @param {Boolean} visibility true or false
45067          */
45068         "visibilitychange" : true,
45069         /**
45070          * @event paneladded
45071          * Fires when a panel is added. 
45072          * @param {Roo.LayoutRegion} this
45073          * @param {Roo.ContentPanel} panel The panel
45074          */
45075         "paneladded" : true,
45076         /**
45077          * @event panelremoved
45078          * Fires when a panel is removed. 
45079          * @param {Roo.LayoutRegion} this
45080          * @param {Roo.ContentPanel} panel The panel
45081          */
45082         "panelremoved" : true,
45083         /**
45084          * @event collapsed
45085          * Fires when this region is collapsed.
45086          * @param {Roo.LayoutRegion} this
45087          */
45088         "collapsed" : true,
45089         /**
45090          * @event expanded
45091          * Fires when this region is expanded.
45092          * @param {Roo.LayoutRegion} this
45093          */
45094         "expanded" : true,
45095         /**
45096          * @event slideshow
45097          * Fires when this region is slid into view.
45098          * @param {Roo.LayoutRegion} this
45099          */
45100         "slideshow" : true,
45101         /**
45102          * @event slidehide
45103          * Fires when this region slides out of view. 
45104          * @param {Roo.LayoutRegion} this
45105          */
45106         "slidehide" : true,
45107         /**
45108          * @event panelactivated
45109          * Fires when a panel is activated. 
45110          * @param {Roo.LayoutRegion} this
45111          * @param {Roo.ContentPanel} panel The activated panel
45112          */
45113         "panelactivated" : true,
45114         /**
45115          * @event resized
45116          * Fires when the user resizes this region. 
45117          * @param {Roo.LayoutRegion} this
45118          * @param {Number} newSize The new size (width for east/west, height for north/south)
45119          */
45120         "resized" : true
45121     };
45122     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45123     this.panels = new Roo.util.MixedCollection();
45124     this.panels.getKey = this.getPanelId.createDelegate(this);
45125     this.box = null;
45126     this.activePanel = null;
45127     // ensure listeners are added...
45128     
45129     if (config.listeners || config.events) {
45130         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45131             listeners : config.listeners || {},
45132             events : config.events || {}
45133         });
45134     }
45135     
45136     if(skipConfig !== true){
45137         this.applyConfig(config);
45138     }
45139 };
45140
45141 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45142     getPanelId : function(p){
45143         return p.getId();
45144     },
45145     
45146     applyConfig : function(config){
45147         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45148         this.config = config;
45149         
45150     },
45151     
45152     /**
45153      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45154      * the width, for horizontal (north, south) the height.
45155      * @param {Number} newSize The new width or height
45156      */
45157     resizeTo : function(newSize){
45158         var el = this.el ? this.el :
45159                  (this.activePanel ? this.activePanel.getEl() : null);
45160         if(el){
45161             switch(this.position){
45162                 case "east":
45163                 case "west":
45164                     el.setWidth(newSize);
45165                     this.fireEvent("resized", this, newSize);
45166                 break;
45167                 case "north":
45168                 case "south":
45169                     el.setHeight(newSize);
45170                     this.fireEvent("resized", this, newSize);
45171                 break;                
45172             }
45173         }
45174     },
45175     
45176     getBox : function(){
45177         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45178     },
45179     
45180     getMargins : function(){
45181         return this.margins;
45182     },
45183     
45184     updateBox : function(box){
45185         this.box = box;
45186         var el = this.activePanel.getEl();
45187         el.dom.style.left = box.x + "px";
45188         el.dom.style.top = box.y + "px";
45189         this.activePanel.setSize(box.width, box.height);
45190     },
45191     
45192     /**
45193      * Returns the container element for this region.
45194      * @return {Roo.Element}
45195      */
45196     getEl : function(){
45197         return this.activePanel;
45198     },
45199     
45200     /**
45201      * Returns true if this region is currently visible.
45202      * @return {Boolean}
45203      */
45204     isVisible : function(){
45205         return this.activePanel ? true : false;
45206     },
45207     
45208     setActivePanel : function(panel){
45209         panel = this.getPanel(panel);
45210         if(this.activePanel && this.activePanel != panel){
45211             this.activePanel.setActiveState(false);
45212             this.activePanel.getEl().setLeftTop(-10000,-10000);
45213         }
45214         this.activePanel = panel;
45215         panel.setActiveState(true);
45216         if(this.box){
45217             panel.setSize(this.box.width, this.box.height);
45218         }
45219         this.fireEvent("panelactivated", this, panel);
45220         this.fireEvent("invalidated");
45221     },
45222     
45223     /**
45224      * Show the specified panel.
45225      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45226      * @return {Roo.ContentPanel} The shown panel or null
45227      */
45228     showPanel : function(panel){
45229         if(panel = this.getPanel(panel)){
45230             this.setActivePanel(panel);
45231         }
45232         return panel;
45233     },
45234     
45235     /**
45236      * Get the active panel for this region.
45237      * @return {Roo.ContentPanel} The active panel or null
45238      */
45239     getActivePanel : function(){
45240         return this.activePanel;
45241     },
45242     
45243     /**
45244      * Add the passed ContentPanel(s)
45245      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45246      * @return {Roo.ContentPanel} The panel added (if only one was added)
45247      */
45248     add : function(panel){
45249         if(arguments.length > 1){
45250             for(var i = 0, len = arguments.length; i < len; i++) {
45251                 this.add(arguments[i]);
45252             }
45253             return null;
45254         }
45255         if(this.hasPanel(panel)){
45256             this.showPanel(panel);
45257             return panel;
45258         }
45259         var el = panel.getEl();
45260         if(el.dom.parentNode != this.mgr.el.dom){
45261             this.mgr.el.dom.appendChild(el.dom);
45262         }
45263         if(panel.setRegion){
45264             panel.setRegion(this);
45265         }
45266         this.panels.add(panel);
45267         el.setStyle("position", "absolute");
45268         if(!panel.background){
45269             this.setActivePanel(panel);
45270             if(this.config.initialSize && this.panels.getCount()==1){
45271                 this.resizeTo(this.config.initialSize);
45272             }
45273         }
45274         this.fireEvent("paneladded", this, panel);
45275         return panel;
45276     },
45277     
45278     /**
45279      * Returns true if the panel is in this region.
45280      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45281      * @return {Boolean}
45282      */
45283     hasPanel : function(panel){
45284         if(typeof panel == "object"){ // must be panel obj
45285             panel = panel.getId();
45286         }
45287         return this.getPanel(panel) ? true : false;
45288     },
45289     
45290     /**
45291      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45292      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45293      * @param {Boolean} preservePanel Overrides the config preservePanel option
45294      * @return {Roo.ContentPanel} The panel that was removed
45295      */
45296     remove : function(panel, preservePanel){
45297         panel = this.getPanel(panel);
45298         if(!panel){
45299             return null;
45300         }
45301         var e = {};
45302         this.fireEvent("beforeremove", this, panel, e);
45303         if(e.cancel === true){
45304             return null;
45305         }
45306         var panelId = panel.getId();
45307         this.panels.removeKey(panelId);
45308         return panel;
45309     },
45310     
45311     /**
45312      * Returns the panel specified or null if it's not in this region.
45313      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45314      * @return {Roo.ContentPanel}
45315      */
45316     getPanel : function(id){
45317         if(typeof id == "object"){ // must be panel obj
45318             return id;
45319         }
45320         return this.panels.get(id);
45321     },
45322     
45323     /**
45324      * Returns this regions position (north/south/east/west/center).
45325      * @return {String} 
45326      */
45327     getPosition: function(){
45328         return this.position;    
45329     }
45330 });/*
45331  * Based on:
45332  * Ext JS Library 1.1.1
45333  * Copyright(c) 2006-2007, Ext JS, LLC.
45334  *
45335  * Originally Released Under LGPL - original licence link has changed is not relivant.
45336  *
45337  * Fork - LGPL
45338  * <script type="text/javascript">
45339  */
45340  
45341 /**
45342  * @class Roo.LayoutRegion
45343  * @extends Roo.BasicLayoutRegion
45344  * This class represents a region in a layout manager.
45345  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45346  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45347  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45348  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45349  * @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})
45350  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45351  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45352  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45353  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45354  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45355  * @cfg {String}    title           The title for the region (overrides panel titles)
45356  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45357  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45358  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45359  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45360  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45361  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45362  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45363  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45364  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45365  * @cfg {Boolean}   showPin         True to show a pin button
45366  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45367  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45368  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45369  * @cfg {Number}    width           For East/West panels
45370  * @cfg {Number}    height          For North/South panels
45371  * @cfg {Boolean}   split           To show the splitter
45372  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45373  */
45374 Roo.LayoutRegion = function(mgr, config, pos){
45375     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45376     var dh = Roo.DomHelper;
45377     /** This region's container element 
45378     * @type Roo.Element */
45379     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45380     /** This region's title element 
45381     * @type Roo.Element */
45382
45383     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45384         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45385         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45386     ]}, true);
45387     this.titleEl.enableDisplayMode();
45388     /** This region's title text element 
45389     * @type HTMLElement */
45390     this.titleTextEl = this.titleEl.dom.firstChild;
45391     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45392     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45393     this.closeBtn.enableDisplayMode();
45394     this.closeBtn.on("click", this.closeClicked, this);
45395     this.closeBtn.hide();
45396
45397     this.createBody(config);
45398     this.visible = true;
45399     this.collapsed = false;
45400
45401     if(config.hideWhenEmpty){
45402         this.hide();
45403         this.on("paneladded", this.validateVisibility, this);
45404         this.on("panelremoved", this.validateVisibility, this);
45405     }
45406     this.applyConfig(config);
45407 };
45408
45409 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45410
45411     createBody : function(){
45412         /** This region's body element 
45413         * @type Roo.Element */
45414         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45415     },
45416
45417     applyConfig : function(c){
45418         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45419             var dh = Roo.DomHelper;
45420             if(c.titlebar !== false){
45421                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45422                 this.collapseBtn.on("click", this.collapse, this);
45423                 this.collapseBtn.enableDisplayMode();
45424
45425                 if(c.showPin === true || this.showPin){
45426                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45427                     this.stickBtn.enableDisplayMode();
45428                     this.stickBtn.on("click", this.expand, this);
45429                     this.stickBtn.hide();
45430                 }
45431             }
45432             /** This region's collapsed element
45433             * @type Roo.Element */
45434             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45435                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45436             ]}, true);
45437             if(c.floatable !== false){
45438                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45439                this.collapsedEl.on("click", this.collapseClick, this);
45440             }
45441
45442             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45443                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45444                    id: "message", unselectable: "on", style:{"float":"left"}});
45445                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45446              }
45447             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45448             this.expandBtn.on("click", this.expand, this);
45449         }
45450         if(this.collapseBtn){
45451             this.collapseBtn.setVisible(c.collapsible == true);
45452         }
45453         this.cmargins = c.cmargins || this.cmargins ||
45454                          (this.position == "west" || this.position == "east" ?
45455                              {top: 0, left: 2, right:2, bottom: 0} :
45456                              {top: 2, left: 0, right:0, bottom: 2});
45457         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45458         this.bottomTabs = c.tabPosition != "top";
45459         this.autoScroll = c.autoScroll || false;
45460         if(this.autoScroll){
45461             this.bodyEl.setStyle("overflow", "auto");
45462         }else{
45463             this.bodyEl.setStyle("overflow", "hidden");
45464         }
45465         //if(c.titlebar !== false){
45466             if((!c.titlebar && !c.title) || c.titlebar === false){
45467                 this.titleEl.hide();
45468             }else{
45469                 this.titleEl.show();
45470                 if(c.title){
45471                     this.titleTextEl.innerHTML = c.title;
45472                 }
45473             }
45474         //}
45475         this.duration = c.duration || .30;
45476         this.slideDuration = c.slideDuration || .45;
45477         this.config = c;
45478         if(c.collapsed){
45479             this.collapse(true);
45480         }
45481         if(c.hidden){
45482             this.hide();
45483         }
45484     },
45485     /**
45486      * Returns true if this region is currently visible.
45487      * @return {Boolean}
45488      */
45489     isVisible : function(){
45490         return this.visible;
45491     },
45492
45493     /**
45494      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45495      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45496      */
45497     setCollapsedTitle : function(title){
45498         title = title || "&#160;";
45499         if(this.collapsedTitleTextEl){
45500             this.collapsedTitleTextEl.innerHTML = title;
45501         }
45502     },
45503
45504     getBox : function(){
45505         var b;
45506         if(!this.collapsed){
45507             b = this.el.getBox(false, true);
45508         }else{
45509             b = this.collapsedEl.getBox(false, true);
45510         }
45511         return b;
45512     },
45513
45514     getMargins : function(){
45515         return this.collapsed ? this.cmargins : this.margins;
45516     },
45517
45518     highlight : function(){
45519         this.el.addClass("x-layout-panel-dragover");
45520     },
45521
45522     unhighlight : function(){
45523         this.el.removeClass("x-layout-panel-dragover");
45524     },
45525
45526     updateBox : function(box){
45527         this.box = box;
45528         if(!this.collapsed){
45529             this.el.dom.style.left = box.x + "px";
45530             this.el.dom.style.top = box.y + "px";
45531             this.updateBody(box.width, box.height);
45532         }else{
45533             this.collapsedEl.dom.style.left = box.x + "px";
45534             this.collapsedEl.dom.style.top = box.y + "px";
45535             this.collapsedEl.setSize(box.width, box.height);
45536         }
45537         if(this.tabs){
45538             this.tabs.autoSizeTabs();
45539         }
45540     },
45541
45542     updateBody : function(w, h){
45543         if(w !== null){
45544             this.el.setWidth(w);
45545             w -= this.el.getBorderWidth("rl");
45546             if(this.config.adjustments){
45547                 w += this.config.adjustments[0];
45548             }
45549         }
45550         if(h !== null){
45551             this.el.setHeight(h);
45552             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45553             h -= this.el.getBorderWidth("tb");
45554             if(this.config.adjustments){
45555                 h += this.config.adjustments[1];
45556             }
45557             this.bodyEl.setHeight(h);
45558             if(this.tabs){
45559                 h = this.tabs.syncHeight(h);
45560             }
45561         }
45562         if(this.panelSize){
45563             w = w !== null ? w : this.panelSize.width;
45564             h = h !== null ? h : this.panelSize.height;
45565         }
45566         if(this.activePanel){
45567             var el = this.activePanel.getEl();
45568             w = w !== null ? w : el.getWidth();
45569             h = h !== null ? h : el.getHeight();
45570             this.panelSize = {width: w, height: h};
45571             this.activePanel.setSize(w, h);
45572         }
45573         if(Roo.isIE && this.tabs){
45574             this.tabs.el.repaint();
45575         }
45576     },
45577
45578     /**
45579      * Returns the container element for this region.
45580      * @return {Roo.Element}
45581      */
45582     getEl : function(){
45583         return this.el;
45584     },
45585
45586     /**
45587      * Hides this region.
45588      */
45589     hide : function(){
45590         if(!this.collapsed){
45591             this.el.dom.style.left = "-2000px";
45592             this.el.hide();
45593         }else{
45594             this.collapsedEl.dom.style.left = "-2000px";
45595             this.collapsedEl.hide();
45596         }
45597         this.visible = false;
45598         this.fireEvent("visibilitychange", this, false);
45599     },
45600
45601     /**
45602      * Shows this region if it was previously hidden.
45603      */
45604     show : function(){
45605         if(!this.collapsed){
45606             this.el.show();
45607         }else{
45608             this.collapsedEl.show();
45609         }
45610         this.visible = true;
45611         this.fireEvent("visibilitychange", this, true);
45612     },
45613
45614     closeClicked : function(){
45615         if(this.activePanel){
45616             this.remove(this.activePanel);
45617         }
45618     },
45619
45620     collapseClick : function(e){
45621         if(this.isSlid){
45622            e.stopPropagation();
45623            this.slideIn();
45624         }else{
45625            e.stopPropagation();
45626            this.slideOut();
45627         }
45628     },
45629
45630     /**
45631      * Collapses this region.
45632      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45633      */
45634     collapse : function(skipAnim){
45635         if(this.collapsed) return;
45636         this.collapsed = true;
45637         if(this.split){
45638             this.split.el.hide();
45639         }
45640         if(this.config.animate && skipAnim !== true){
45641             this.fireEvent("invalidated", this);
45642             this.animateCollapse();
45643         }else{
45644             this.el.setLocation(-20000,-20000);
45645             this.el.hide();
45646             this.collapsedEl.show();
45647             this.fireEvent("collapsed", this);
45648             this.fireEvent("invalidated", this);
45649         }
45650     },
45651
45652     animateCollapse : function(){
45653         // overridden
45654     },
45655
45656     /**
45657      * Expands this region if it was previously collapsed.
45658      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45659      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45660      */
45661     expand : function(e, skipAnim){
45662         if(e) e.stopPropagation();
45663         if(!this.collapsed || this.el.hasActiveFx()) return;
45664         if(this.isSlid){
45665             this.afterSlideIn();
45666             skipAnim = true;
45667         }
45668         this.collapsed = false;
45669         if(this.config.animate && skipAnim !== true){
45670             this.animateExpand();
45671         }else{
45672             this.el.show();
45673             if(this.split){
45674                 this.split.el.show();
45675             }
45676             this.collapsedEl.setLocation(-2000,-2000);
45677             this.collapsedEl.hide();
45678             this.fireEvent("invalidated", this);
45679             this.fireEvent("expanded", this);
45680         }
45681     },
45682
45683     animateExpand : function(){
45684         // overridden
45685     },
45686
45687     initTabs : function()
45688     {
45689         this.bodyEl.setStyle("overflow", "hidden");
45690         var ts = new Roo.TabPanel(
45691                 this.bodyEl.dom,
45692                 {
45693                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45694                     disableTooltips: this.config.disableTabTips,
45695                     toolbar : this.config.toolbar
45696                 }
45697         );
45698         if(this.config.hideTabs){
45699             ts.stripWrap.setDisplayed(false);
45700         }
45701         this.tabs = ts;
45702         ts.resizeTabs = this.config.resizeTabs === true;
45703         ts.minTabWidth = this.config.minTabWidth || 40;
45704         ts.maxTabWidth = this.config.maxTabWidth || 250;
45705         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45706         ts.monitorResize = false;
45707         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45708         ts.bodyEl.addClass('x-layout-tabs-body');
45709         this.panels.each(this.initPanelAsTab, this);
45710     },
45711
45712     initPanelAsTab : function(panel){
45713         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45714                     this.config.closeOnTab && panel.isClosable());
45715         if(panel.tabTip !== undefined){
45716             ti.setTooltip(panel.tabTip);
45717         }
45718         ti.on("activate", function(){
45719               this.setActivePanel(panel);
45720         }, this);
45721         if(this.config.closeOnTab){
45722             ti.on("beforeclose", function(t, e){
45723                 e.cancel = true;
45724                 this.remove(panel);
45725             }, this);
45726         }
45727         return ti;
45728     },
45729
45730     updatePanelTitle : function(panel, title){
45731         if(this.activePanel == panel){
45732             this.updateTitle(title);
45733         }
45734         if(this.tabs){
45735             var ti = this.tabs.getTab(panel.getEl().id);
45736             ti.setText(title);
45737             if(panel.tabTip !== undefined){
45738                 ti.setTooltip(panel.tabTip);
45739             }
45740         }
45741     },
45742
45743     updateTitle : function(title){
45744         if(this.titleTextEl && !this.config.title){
45745             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45746         }
45747     },
45748
45749     setActivePanel : function(panel){
45750         panel = this.getPanel(panel);
45751         if(this.activePanel && this.activePanel != panel){
45752             this.activePanel.setActiveState(false);
45753         }
45754         this.activePanel = panel;
45755         panel.setActiveState(true);
45756         if(this.panelSize){
45757             panel.setSize(this.panelSize.width, this.panelSize.height);
45758         }
45759         if(this.closeBtn){
45760             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45761         }
45762         this.updateTitle(panel.getTitle());
45763         if(this.tabs){
45764             this.fireEvent("invalidated", this);
45765         }
45766         this.fireEvent("panelactivated", this, panel);
45767     },
45768
45769     /**
45770      * Shows the specified panel.
45771      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45772      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45773      */
45774     showPanel : function(panel){
45775         if(panel = this.getPanel(panel)){
45776             if(this.tabs){
45777                 var tab = this.tabs.getTab(panel.getEl().id);
45778                 if(tab.isHidden()){
45779                     this.tabs.unhideTab(tab.id);
45780                 }
45781                 tab.activate();
45782             }else{
45783                 this.setActivePanel(panel);
45784             }
45785         }
45786         return panel;
45787     },
45788
45789     /**
45790      * Get the active panel for this region.
45791      * @return {Roo.ContentPanel} The active panel or null
45792      */
45793     getActivePanel : function(){
45794         return this.activePanel;
45795     },
45796
45797     validateVisibility : function(){
45798         if(this.panels.getCount() < 1){
45799             this.updateTitle("&#160;");
45800             this.closeBtn.hide();
45801             this.hide();
45802         }else{
45803             if(!this.isVisible()){
45804                 this.show();
45805             }
45806         }
45807     },
45808
45809     /**
45810      * Adds the passed ContentPanel(s) to this region.
45811      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45812      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45813      */
45814     add : function(panel){
45815         if(arguments.length > 1){
45816             for(var i = 0, len = arguments.length; i < len; i++) {
45817                 this.add(arguments[i]);
45818             }
45819             return null;
45820         }
45821         if(this.hasPanel(panel)){
45822             this.showPanel(panel);
45823             return panel;
45824         }
45825         panel.setRegion(this);
45826         this.panels.add(panel);
45827         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45828             this.bodyEl.dom.appendChild(panel.getEl().dom);
45829             if(panel.background !== true){
45830                 this.setActivePanel(panel);
45831             }
45832             this.fireEvent("paneladded", this, panel);
45833             return panel;
45834         }
45835         if(!this.tabs){
45836             this.initTabs();
45837         }else{
45838             this.initPanelAsTab(panel);
45839         }
45840         if(panel.background !== true){
45841             this.tabs.activate(panel.getEl().id);
45842         }
45843         this.fireEvent("paneladded", this, panel);
45844         return panel;
45845     },
45846
45847     /**
45848      * Hides the tab for the specified panel.
45849      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45850      */
45851     hidePanel : function(panel){
45852         if(this.tabs && (panel = this.getPanel(panel))){
45853             this.tabs.hideTab(panel.getEl().id);
45854         }
45855     },
45856
45857     /**
45858      * Unhides the tab for a previously hidden panel.
45859      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45860      */
45861     unhidePanel : function(panel){
45862         if(this.tabs && (panel = this.getPanel(panel))){
45863             this.tabs.unhideTab(panel.getEl().id);
45864         }
45865     },
45866
45867     clearPanels : function(){
45868         while(this.panels.getCount() > 0){
45869              this.remove(this.panels.first());
45870         }
45871     },
45872
45873     /**
45874      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45875      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45876      * @param {Boolean} preservePanel Overrides the config preservePanel option
45877      * @return {Roo.ContentPanel} The panel that was removed
45878      */
45879     remove : function(panel, preservePanel){
45880         panel = this.getPanel(panel);
45881         if(!panel){
45882             return null;
45883         }
45884         var e = {};
45885         this.fireEvent("beforeremove", this, panel, e);
45886         if(e.cancel === true){
45887             return null;
45888         }
45889         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45890         var panelId = panel.getId();
45891         this.panels.removeKey(panelId);
45892         if(preservePanel){
45893             document.body.appendChild(panel.getEl().dom);
45894         }
45895         if(this.tabs){
45896             this.tabs.removeTab(panel.getEl().id);
45897         }else if (!preservePanel){
45898             this.bodyEl.dom.removeChild(panel.getEl().dom);
45899         }
45900         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45901             var p = this.panels.first();
45902             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45903             tempEl.appendChild(p.getEl().dom);
45904             this.bodyEl.update("");
45905             this.bodyEl.dom.appendChild(p.getEl().dom);
45906             tempEl = null;
45907             this.updateTitle(p.getTitle());
45908             this.tabs = null;
45909             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45910             this.setActivePanel(p);
45911         }
45912         panel.setRegion(null);
45913         if(this.activePanel == panel){
45914             this.activePanel = null;
45915         }
45916         if(this.config.autoDestroy !== false && preservePanel !== true){
45917             try{panel.destroy();}catch(e){}
45918         }
45919         this.fireEvent("panelremoved", this, panel);
45920         return panel;
45921     },
45922
45923     /**
45924      * Returns the TabPanel component used by this region
45925      * @return {Roo.TabPanel}
45926      */
45927     getTabs : function(){
45928         return this.tabs;
45929     },
45930
45931     createTool : function(parentEl, className){
45932         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45933             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45934         btn.addClassOnOver("x-layout-tools-button-over");
45935         return btn;
45936     }
45937 });/*
45938  * Based on:
45939  * Ext JS Library 1.1.1
45940  * Copyright(c) 2006-2007, Ext JS, LLC.
45941  *
45942  * Originally Released Under LGPL - original licence link has changed is not relivant.
45943  *
45944  * Fork - LGPL
45945  * <script type="text/javascript">
45946  */
45947  
45948
45949
45950 /**
45951  * @class Roo.SplitLayoutRegion
45952  * @extends Roo.LayoutRegion
45953  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45954  */
45955 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45956     this.cursor = cursor;
45957     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45958 };
45959
45960 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45961     splitTip : "Drag to resize.",
45962     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45963     useSplitTips : false,
45964
45965     applyConfig : function(config){
45966         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45967         if(config.split){
45968             if(!this.split){
45969                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45970                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45971                 /** The SplitBar for this region 
45972                 * @type Roo.SplitBar */
45973                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45974                 this.split.on("moved", this.onSplitMove, this);
45975                 this.split.useShim = config.useShim === true;
45976                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45977                 if(this.useSplitTips){
45978                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45979                 }
45980                 if(config.collapsible){
45981                     this.split.el.on("dblclick", this.collapse,  this);
45982                 }
45983             }
45984             if(typeof config.minSize != "undefined"){
45985                 this.split.minSize = config.minSize;
45986             }
45987             if(typeof config.maxSize != "undefined"){
45988                 this.split.maxSize = config.maxSize;
45989             }
45990             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45991                 this.hideSplitter();
45992             }
45993         }
45994     },
45995
45996     getHMaxSize : function(){
45997          var cmax = this.config.maxSize || 10000;
45998          var center = this.mgr.getRegion("center");
45999          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46000     },
46001
46002     getVMaxSize : function(){
46003          var cmax = this.config.maxSize || 10000;
46004          var center = this.mgr.getRegion("center");
46005          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46006     },
46007
46008     onSplitMove : function(split, newSize){
46009         this.fireEvent("resized", this, newSize);
46010     },
46011     
46012     /** 
46013      * Returns the {@link Roo.SplitBar} for this region.
46014      * @return {Roo.SplitBar}
46015      */
46016     getSplitBar : function(){
46017         return this.split;
46018     },
46019     
46020     hide : function(){
46021         this.hideSplitter();
46022         Roo.SplitLayoutRegion.superclass.hide.call(this);
46023     },
46024
46025     hideSplitter : function(){
46026         if(this.split){
46027             this.split.el.setLocation(-2000,-2000);
46028             this.split.el.hide();
46029         }
46030     },
46031
46032     show : function(){
46033         if(this.split){
46034             this.split.el.show();
46035         }
46036         Roo.SplitLayoutRegion.superclass.show.call(this);
46037     },
46038     
46039     beforeSlide: function(){
46040         if(Roo.isGecko){// firefox overflow auto bug workaround
46041             this.bodyEl.clip();
46042             if(this.tabs) this.tabs.bodyEl.clip();
46043             if(this.activePanel){
46044                 this.activePanel.getEl().clip();
46045                 
46046                 if(this.activePanel.beforeSlide){
46047                     this.activePanel.beforeSlide();
46048                 }
46049             }
46050         }
46051     },
46052     
46053     afterSlide : function(){
46054         if(Roo.isGecko){// firefox overflow auto bug workaround
46055             this.bodyEl.unclip();
46056             if(this.tabs) this.tabs.bodyEl.unclip();
46057             if(this.activePanel){
46058                 this.activePanel.getEl().unclip();
46059                 if(this.activePanel.afterSlide){
46060                     this.activePanel.afterSlide();
46061                 }
46062             }
46063         }
46064     },
46065
46066     initAutoHide : function(){
46067         if(this.autoHide !== false){
46068             if(!this.autoHideHd){
46069                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46070                 this.autoHideHd = {
46071                     "mouseout": function(e){
46072                         if(!e.within(this.el, true)){
46073                             st.delay(500);
46074                         }
46075                     },
46076                     "mouseover" : function(e){
46077                         st.cancel();
46078                     },
46079                     scope : this
46080                 };
46081             }
46082             this.el.on(this.autoHideHd);
46083         }
46084     },
46085
46086     clearAutoHide : function(){
46087         if(this.autoHide !== false){
46088             this.el.un("mouseout", this.autoHideHd.mouseout);
46089             this.el.un("mouseover", this.autoHideHd.mouseover);
46090         }
46091     },
46092
46093     clearMonitor : function(){
46094         Roo.get(document).un("click", this.slideInIf, this);
46095     },
46096
46097     // these names are backwards but not changed for compat
46098     slideOut : function(){
46099         if(this.isSlid || this.el.hasActiveFx()){
46100             return;
46101         }
46102         this.isSlid = true;
46103         if(this.collapseBtn){
46104             this.collapseBtn.hide();
46105         }
46106         this.closeBtnState = this.closeBtn.getStyle('display');
46107         this.closeBtn.hide();
46108         if(this.stickBtn){
46109             this.stickBtn.show();
46110         }
46111         this.el.show();
46112         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46113         this.beforeSlide();
46114         this.el.setStyle("z-index", 10001);
46115         this.el.slideIn(this.getSlideAnchor(), {
46116             callback: function(){
46117                 this.afterSlide();
46118                 this.initAutoHide();
46119                 Roo.get(document).on("click", this.slideInIf, this);
46120                 this.fireEvent("slideshow", this);
46121             },
46122             scope: this,
46123             block: true
46124         });
46125     },
46126
46127     afterSlideIn : function(){
46128         this.clearAutoHide();
46129         this.isSlid = false;
46130         this.clearMonitor();
46131         this.el.setStyle("z-index", "");
46132         if(this.collapseBtn){
46133             this.collapseBtn.show();
46134         }
46135         this.closeBtn.setStyle('display', this.closeBtnState);
46136         if(this.stickBtn){
46137             this.stickBtn.hide();
46138         }
46139         this.fireEvent("slidehide", this);
46140     },
46141
46142     slideIn : function(cb){
46143         if(!this.isSlid || this.el.hasActiveFx()){
46144             Roo.callback(cb);
46145             return;
46146         }
46147         this.isSlid = false;
46148         this.beforeSlide();
46149         this.el.slideOut(this.getSlideAnchor(), {
46150             callback: function(){
46151                 this.el.setLeftTop(-10000, -10000);
46152                 this.afterSlide();
46153                 this.afterSlideIn();
46154                 Roo.callback(cb);
46155             },
46156             scope: this,
46157             block: true
46158         });
46159     },
46160     
46161     slideInIf : function(e){
46162         if(!e.within(this.el)){
46163             this.slideIn();
46164         }
46165     },
46166
46167     animateCollapse : function(){
46168         this.beforeSlide();
46169         this.el.setStyle("z-index", 20000);
46170         var anchor = this.getSlideAnchor();
46171         this.el.slideOut(anchor, {
46172             callback : function(){
46173                 this.el.setStyle("z-index", "");
46174                 this.collapsedEl.slideIn(anchor, {duration:.3});
46175                 this.afterSlide();
46176                 this.el.setLocation(-10000,-10000);
46177                 this.el.hide();
46178                 this.fireEvent("collapsed", this);
46179             },
46180             scope: this,
46181             block: true
46182         });
46183     },
46184
46185     animateExpand : function(){
46186         this.beforeSlide();
46187         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46188         this.el.setStyle("z-index", 20000);
46189         this.collapsedEl.hide({
46190             duration:.1
46191         });
46192         this.el.slideIn(this.getSlideAnchor(), {
46193             callback : function(){
46194                 this.el.setStyle("z-index", "");
46195                 this.afterSlide();
46196                 if(this.split){
46197                     this.split.el.show();
46198                 }
46199                 this.fireEvent("invalidated", this);
46200                 this.fireEvent("expanded", this);
46201             },
46202             scope: this,
46203             block: true
46204         });
46205     },
46206
46207     anchors : {
46208         "west" : "left",
46209         "east" : "right",
46210         "north" : "top",
46211         "south" : "bottom"
46212     },
46213
46214     sanchors : {
46215         "west" : "l",
46216         "east" : "r",
46217         "north" : "t",
46218         "south" : "b"
46219     },
46220
46221     canchors : {
46222         "west" : "tl-tr",
46223         "east" : "tr-tl",
46224         "north" : "tl-bl",
46225         "south" : "bl-tl"
46226     },
46227
46228     getAnchor : function(){
46229         return this.anchors[this.position];
46230     },
46231
46232     getCollapseAnchor : function(){
46233         return this.canchors[this.position];
46234     },
46235
46236     getSlideAnchor : function(){
46237         return this.sanchors[this.position];
46238     },
46239
46240     getAlignAdj : function(){
46241         var cm = this.cmargins;
46242         switch(this.position){
46243             case "west":
46244                 return [0, 0];
46245             break;
46246             case "east":
46247                 return [0, 0];
46248             break;
46249             case "north":
46250                 return [0, 0];
46251             break;
46252             case "south":
46253                 return [0, 0];
46254             break;
46255         }
46256     },
46257
46258     getExpandAdj : function(){
46259         var c = this.collapsedEl, cm = this.cmargins;
46260         switch(this.position){
46261             case "west":
46262                 return [-(cm.right+c.getWidth()+cm.left), 0];
46263             break;
46264             case "east":
46265                 return [cm.right+c.getWidth()+cm.left, 0];
46266             break;
46267             case "north":
46268                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46269             break;
46270             case "south":
46271                 return [0, cm.top+cm.bottom+c.getHeight()];
46272             break;
46273         }
46274     }
46275 });/*
46276  * Based on:
46277  * Ext JS Library 1.1.1
46278  * Copyright(c) 2006-2007, Ext JS, LLC.
46279  *
46280  * Originally Released Under LGPL - original licence link has changed is not relivant.
46281  *
46282  * Fork - LGPL
46283  * <script type="text/javascript">
46284  */
46285 /*
46286  * These classes are private internal classes
46287  */
46288 Roo.CenterLayoutRegion = function(mgr, config){
46289     Roo.LayoutRegion.call(this, mgr, config, "center");
46290     this.visible = true;
46291     this.minWidth = config.minWidth || 20;
46292     this.minHeight = config.minHeight || 20;
46293 };
46294
46295 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46296     hide : function(){
46297         // center panel can't be hidden
46298     },
46299     
46300     show : function(){
46301         // center panel can't be hidden
46302     },
46303     
46304     getMinWidth: function(){
46305         return this.minWidth;
46306     },
46307     
46308     getMinHeight: function(){
46309         return this.minHeight;
46310     }
46311 });
46312
46313
46314 Roo.NorthLayoutRegion = function(mgr, config){
46315     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46316     if(this.split){
46317         this.split.placement = Roo.SplitBar.TOP;
46318         this.split.orientation = Roo.SplitBar.VERTICAL;
46319         this.split.el.addClass("x-layout-split-v");
46320     }
46321     var size = config.initialSize || config.height;
46322     if(typeof size != "undefined"){
46323         this.el.setHeight(size);
46324     }
46325 };
46326 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46327     orientation: Roo.SplitBar.VERTICAL,
46328     getBox : function(){
46329         if(this.collapsed){
46330             return this.collapsedEl.getBox();
46331         }
46332         var box = this.el.getBox();
46333         if(this.split){
46334             box.height += this.split.el.getHeight();
46335         }
46336         return box;
46337     },
46338     
46339     updateBox : function(box){
46340         if(this.split && !this.collapsed){
46341             box.height -= this.split.el.getHeight();
46342             this.split.el.setLeft(box.x);
46343             this.split.el.setTop(box.y+box.height);
46344             this.split.el.setWidth(box.width);
46345         }
46346         if(this.collapsed){
46347             this.updateBody(box.width, null);
46348         }
46349         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46350     }
46351 });
46352
46353 Roo.SouthLayoutRegion = function(mgr, config){
46354     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46355     if(this.split){
46356         this.split.placement = Roo.SplitBar.BOTTOM;
46357         this.split.orientation = Roo.SplitBar.VERTICAL;
46358         this.split.el.addClass("x-layout-split-v");
46359     }
46360     var size = config.initialSize || config.height;
46361     if(typeof size != "undefined"){
46362         this.el.setHeight(size);
46363     }
46364 };
46365 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46366     orientation: Roo.SplitBar.VERTICAL,
46367     getBox : function(){
46368         if(this.collapsed){
46369             return this.collapsedEl.getBox();
46370         }
46371         var box = this.el.getBox();
46372         if(this.split){
46373             var sh = this.split.el.getHeight();
46374             box.height += sh;
46375             box.y -= sh;
46376         }
46377         return box;
46378     },
46379     
46380     updateBox : function(box){
46381         if(this.split && !this.collapsed){
46382             var sh = this.split.el.getHeight();
46383             box.height -= sh;
46384             box.y += sh;
46385             this.split.el.setLeft(box.x);
46386             this.split.el.setTop(box.y-sh);
46387             this.split.el.setWidth(box.width);
46388         }
46389         if(this.collapsed){
46390             this.updateBody(box.width, null);
46391         }
46392         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46393     }
46394 });
46395
46396 Roo.EastLayoutRegion = function(mgr, config){
46397     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46398     if(this.split){
46399         this.split.placement = Roo.SplitBar.RIGHT;
46400         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46401         this.split.el.addClass("x-layout-split-h");
46402     }
46403     var size = config.initialSize || config.width;
46404     if(typeof size != "undefined"){
46405         this.el.setWidth(size);
46406     }
46407 };
46408 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46409     orientation: Roo.SplitBar.HORIZONTAL,
46410     getBox : function(){
46411         if(this.collapsed){
46412             return this.collapsedEl.getBox();
46413         }
46414         var box = this.el.getBox();
46415         if(this.split){
46416             var sw = this.split.el.getWidth();
46417             box.width += sw;
46418             box.x -= sw;
46419         }
46420         return box;
46421     },
46422
46423     updateBox : function(box){
46424         if(this.split && !this.collapsed){
46425             var sw = this.split.el.getWidth();
46426             box.width -= sw;
46427             this.split.el.setLeft(box.x);
46428             this.split.el.setTop(box.y);
46429             this.split.el.setHeight(box.height);
46430             box.x += sw;
46431         }
46432         if(this.collapsed){
46433             this.updateBody(null, box.height);
46434         }
46435         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46436     }
46437 });
46438
46439 Roo.WestLayoutRegion = function(mgr, config){
46440     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46441     if(this.split){
46442         this.split.placement = Roo.SplitBar.LEFT;
46443         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46444         this.split.el.addClass("x-layout-split-h");
46445     }
46446     var size = config.initialSize || config.width;
46447     if(typeof size != "undefined"){
46448         this.el.setWidth(size);
46449     }
46450 };
46451 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46452     orientation: Roo.SplitBar.HORIZONTAL,
46453     getBox : function(){
46454         if(this.collapsed){
46455             return this.collapsedEl.getBox();
46456         }
46457         var box = this.el.getBox();
46458         if(this.split){
46459             box.width += this.split.el.getWidth();
46460         }
46461         return box;
46462     },
46463     
46464     updateBox : function(box){
46465         if(this.split && !this.collapsed){
46466             var sw = this.split.el.getWidth();
46467             box.width -= sw;
46468             this.split.el.setLeft(box.x+box.width);
46469             this.split.el.setTop(box.y);
46470             this.split.el.setHeight(box.height);
46471         }
46472         if(this.collapsed){
46473             this.updateBody(null, box.height);
46474         }
46475         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46476     }
46477 });
46478 /*
46479  * Based on:
46480  * Ext JS Library 1.1.1
46481  * Copyright(c) 2006-2007, Ext JS, LLC.
46482  *
46483  * Originally Released Under LGPL - original licence link has changed is not relivant.
46484  *
46485  * Fork - LGPL
46486  * <script type="text/javascript">
46487  */
46488  
46489  
46490 /*
46491  * Private internal class for reading and applying state
46492  */
46493 Roo.LayoutStateManager = function(layout){
46494      // default empty state
46495      this.state = {
46496         north: {},
46497         south: {},
46498         east: {},
46499         west: {}       
46500     };
46501 };
46502
46503 Roo.LayoutStateManager.prototype = {
46504     init : function(layout, provider){
46505         this.provider = provider;
46506         var state = provider.get(layout.id+"-layout-state");
46507         if(state){
46508             var wasUpdating = layout.isUpdating();
46509             if(!wasUpdating){
46510                 layout.beginUpdate();
46511             }
46512             for(var key in state){
46513                 if(typeof state[key] != "function"){
46514                     var rstate = state[key];
46515                     var r = layout.getRegion(key);
46516                     if(r && rstate){
46517                         if(rstate.size){
46518                             r.resizeTo(rstate.size);
46519                         }
46520                         if(rstate.collapsed == true){
46521                             r.collapse(true);
46522                         }else{
46523                             r.expand(null, true);
46524                         }
46525                     }
46526                 }
46527             }
46528             if(!wasUpdating){
46529                 layout.endUpdate();
46530             }
46531             this.state = state; 
46532         }
46533         this.layout = layout;
46534         layout.on("regionresized", this.onRegionResized, this);
46535         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46536         layout.on("regionexpanded", this.onRegionExpanded, this);
46537     },
46538     
46539     storeState : function(){
46540         this.provider.set(this.layout.id+"-layout-state", this.state);
46541     },
46542     
46543     onRegionResized : function(region, newSize){
46544         this.state[region.getPosition()].size = newSize;
46545         this.storeState();
46546     },
46547     
46548     onRegionCollapsed : function(region){
46549         this.state[region.getPosition()].collapsed = true;
46550         this.storeState();
46551     },
46552     
46553     onRegionExpanded : function(region){
46554         this.state[region.getPosition()].collapsed = false;
46555         this.storeState();
46556     }
46557 };/*
46558  * Based on:
46559  * Ext JS Library 1.1.1
46560  * Copyright(c) 2006-2007, Ext JS, LLC.
46561  *
46562  * Originally Released Under LGPL - original licence link has changed is not relivant.
46563  *
46564  * Fork - LGPL
46565  * <script type="text/javascript">
46566  */
46567 /**
46568  * @class Roo.ContentPanel
46569  * @extends Roo.util.Observable
46570  * A basic ContentPanel element.
46571  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46572  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46573  * @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
46574  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46575  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46576  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46577  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46578  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46579  * @cfg {String} title          The title for this panel
46580  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46581  * @cfg {String} url            Calls {@link #setUrl} with this value
46582  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46583  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46584  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46585  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46586
46587  * @constructor
46588  * Create a new ContentPanel.
46589  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46590  * @param {String/Object} config A string to set only the title or a config object
46591  * @param {String} content (optional) Set the HTML content for this panel
46592  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46593  */
46594 Roo.ContentPanel = function(el, config, content){
46595     
46596      
46597     /*
46598     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46599         config = el;
46600         el = Roo.id();
46601     }
46602     if (config && config.parentLayout) { 
46603         el = config.parentLayout.el.createChild(); 
46604     }
46605     */
46606     if(el.autoCreate){ // xtype is available if this is called from factory
46607         config = el;
46608         el = Roo.id();
46609     }
46610     this.el = Roo.get(el);
46611     if(!this.el && config && config.autoCreate){
46612         if(typeof config.autoCreate == "object"){
46613             if(!config.autoCreate.id){
46614                 config.autoCreate.id = config.id||el;
46615             }
46616             this.el = Roo.DomHelper.append(document.body,
46617                         config.autoCreate, true);
46618         }else{
46619             this.el = Roo.DomHelper.append(document.body,
46620                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46621         }
46622     }
46623     this.closable = false;
46624     this.loaded = false;
46625     this.active = false;
46626     if(typeof config == "string"){
46627         this.title = config;
46628     }else{
46629         Roo.apply(this, config);
46630     }
46631     
46632     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46633         this.wrapEl = this.el.wrap();
46634         this.toolbar.container = this.el.insertSibling(false, 'before');
46635         this.toolbar = new Roo.Toolbar(this.toolbar);
46636     }
46637     
46638     
46639     
46640     if(this.resizeEl){
46641         this.resizeEl = Roo.get(this.resizeEl, true);
46642     }else{
46643         this.resizeEl = this.el;
46644     }
46645     this.addEvents({
46646         /**
46647          * @event activate
46648          * Fires when this panel is activated. 
46649          * @param {Roo.ContentPanel} this
46650          */
46651         "activate" : true,
46652         /**
46653          * @event deactivate
46654          * Fires when this panel is activated. 
46655          * @param {Roo.ContentPanel} this
46656          */
46657         "deactivate" : true,
46658
46659         /**
46660          * @event resize
46661          * Fires when this panel is resized if fitToFrame is true.
46662          * @param {Roo.ContentPanel} this
46663          * @param {Number} width The width after any component adjustments
46664          * @param {Number} height The height after any component adjustments
46665          */
46666         "resize" : true,
46667         
46668          /**
46669          * @event render
46670          * Fires when this tab is created
46671          * @param {Roo.ContentPanel} this
46672          */
46673         "render" : true
46674         
46675         
46676         
46677     });
46678     if(this.autoScroll){
46679         this.resizeEl.setStyle("overflow", "auto");
46680     } else {
46681         // fix randome scrolling
46682         this.el.on('scroll', function() {
46683             Roo.log('fix random scolling');
46684             this.scrollTo('top',0); 
46685         });
46686     }
46687     content = content || this.content;
46688     if(content){
46689         this.setContent(content);
46690     }
46691     if(config && config.url){
46692         this.setUrl(this.url, this.params, this.loadOnce);
46693     }
46694     
46695     
46696     
46697     Roo.ContentPanel.superclass.constructor.call(this);
46698     
46699     this.fireEvent('render', this);
46700 };
46701
46702 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46703     tabTip:'',
46704     setRegion : function(region){
46705         this.region = region;
46706         if(region){
46707            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46708         }else{
46709            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46710         } 
46711     },
46712     
46713     /**
46714      * Returns the toolbar for this Panel if one was configured. 
46715      * @return {Roo.Toolbar} 
46716      */
46717     getToolbar : function(){
46718         return this.toolbar;
46719     },
46720     
46721     setActiveState : function(active){
46722         this.active = active;
46723         if(!active){
46724             this.fireEvent("deactivate", this);
46725         }else{
46726             this.fireEvent("activate", this);
46727         }
46728     },
46729     /**
46730      * Updates this panel's element
46731      * @param {String} content The new content
46732      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46733     */
46734     setContent : function(content, loadScripts){
46735         this.el.update(content, loadScripts);
46736     },
46737
46738     ignoreResize : function(w, h){
46739         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46740             return true;
46741         }else{
46742             this.lastSize = {width: w, height: h};
46743             return false;
46744         }
46745     },
46746     /**
46747      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46748      * @return {Roo.UpdateManager} The UpdateManager
46749      */
46750     getUpdateManager : function(){
46751         return this.el.getUpdateManager();
46752     },
46753      /**
46754      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46755      * @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:
46756 <pre><code>
46757 panel.load({
46758     url: "your-url.php",
46759     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46760     callback: yourFunction,
46761     scope: yourObject, //(optional scope)
46762     discardUrl: false,
46763     nocache: false,
46764     text: "Loading...",
46765     timeout: 30,
46766     scripts: false
46767 });
46768 </code></pre>
46769      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46770      * 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.
46771      * @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}
46772      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46773      * @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.
46774      * @return {Roo.ContentPanel} this
46775      */
46776     load : function(){
46777         var um = this.el.getUpdateManager();
46778         um.update.apply(um, arguments);
46779         return this;
46780     },
46781
46782
46783     /**
46784      * 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.
46785      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46786      * @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)
46787      * @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)
46788      * @return {Roo.UpdateManager} The UpdateManager
46789      */
46790     setUrl : function(url, params, loadOnce){
46791         if(this.refreshDelegate){
46792             this.removeListener("activate", this.refreshDelegate);
46793         }
46794         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46795         this.on("activate", this.refreshDelegate);
46796         return this.el.getUpdateManager();
46797     },
46798     
46799     _handleRefresh : function(url, params, loadOnce){
46800         if(!loadOnce || !this.loaded){
46801             var updater = this.el.getUpdateManager();
46802             updater.update(url, params, this._setLoaded.createDelegate(this));
46803         }
46804     },
46805     
46806     _setLoaded : function(){
46807         this.loaded = true;
46808     }, 
46809     
46810     /**
46811      * Returns this panel's id
46812      * @return {String} 
46813      */
46814     getId : function(){
46815         return this.el.id;
46816     },
46817     
46818     /** 
46819      * Returns this panel's element - used by regiosn to add.
46820      * @return {Roo.Element} 
46821      */
46822     getEl : function(){
46823         return this.wrapEl || this.el;
46824     },
46825     
46826     adjustForComponents : function(width, height){
46827         if(this.resizeEl != this.el){
46828             width -= this.el.getFrameWidth('lr');
46829             height -= this.el.getFrameWidth('tb');
46830         }
46831         if(this.toolbar){
46832             var te = this.toolbar.getEl();
46833             height -= te.getHeight();
46834             te.setWidth(width);
46835         }
46836         if(this.adjustments){
46837             width += this.adjustments[0];
46838             height += this.adjustments[1];
46839         }
46840         return {"width": width, "height": height};
46841     },
46842     
46843     setSize : function(width, height){
46844         if(this.fitToFrame && !this.ignoreResize(width, height)){
46845             if(this.fitContainer && this.resizeEl != this.el){
46846                 this.el.setSize(width, height);
46847             }
46848             var size = this.adjustForComponents(width, height);
46849             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46850             this.fireEvent('resize', this, size.width, size.height);
46851         }
46852     },
46853     
46854     /**
46855      * Returns this panel's title
46856      * @return {String} 
46857      */
46858     getTitle : function(){
46859         return this.title;
46860     },
46861     
46862     /**
46863      * Set this panel's title
46864      * @param {String} title
46865      */
46866     setTitle : function(title){
46867         this.title = title;
46868         if(this.region){
46869             this.region.updatePanelTitle(this, title);
46870         }
46871     },
46872     
46873     /**
46874      * Returns true is this panel was configured to be closable
46875      * @return {Boolean} 
46876      */
46877     isClosable : function(){
46878         return this.closable;
46879     },
46880     
46881     beforeSlide : function(){
46882         this.el.clip();
46883         this.resizeEl.clip();
46884     },
46885     
46886     afterSlide : function(){
46887         this.el.unclip();
46888         this.resizeEl.unclip();
46889     },
46890     
46891     /**
46892      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46893      *   Will fail silently if the {@link #setUrl} method has not been called.
46894      *   This does not activate the panel, just updates its content.
46895      */
46896     refresh : function(){
46897         if(this.refreshDelegate){
46898            this.loaded = false;
46899            this.refreshDelegate();
46900         }
46901     },
46902     
46903     /**
46904      * Destroys this panel
46905      */
46906     destroy : function(){
46907         this.el.removeAllListeners();
46908         var tempEl = document.createElement("span");
46909         tempEl.appendChild(this.el.dom);
46910         tempEl.innerHTML = "";
46911         this.el.remove();
46912         this.el = null;
46913     },
46914     
46915     /**
46916      * form - if the content panel contains a form - this is a reference to it.
46917      * @type {Roo.form.Form}
46918      */
46919     form : false,
46920     /**
46921      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46922      *    This contains a reference to it.
46923      * @type {Roo.View}
46924      */
46925     view : false,
46926     
46927       /**
46928      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46929      * <pre><code>
46930
46931 layout.addxtype({
46932        xtype : 'Form',
46933        items: [ .... ]
46934    }
46935 );
46936
46937 </code></pre>
46938      * @param {Object} cfg Xtype definition of item to add.
46939      */
46940     
46941     addxtype : function(cfg) {
46942         // add form..
46943         if (cfg.xtype.match(/^Form$/)) {
46944             var el = this.el.createChild();
46945
46946             this.form = new  Roo.form.Form(cfg);
46947             
46948             
46949             if ( this.form.allItems.length) this.form.render(el.dom);
46950             return this.form;
46951         }
46952         // should only have one of theses..
46953         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46954             // views..
46955             cfg.el = this.el.appendChild(document.createElement("div"));
46956             // factory?
46957             
46958             var ret = new Roo.factory(cfg);
46959             ret.render && ret.render(false, ''); // render blank..
46960             this.view = ret;
46961             return ret;
46962         }
46963         return false;
46964     }
46965 });
46966
46967 /**
46968  * @class Roo.GridPanel
46969  * @extends Roo.ContentPanel
46970  * @constructor
46971  * Create a new GridPanel.
46972  * @param {Roo.grid.Grid} grid The grid for this panel
46973  * @param {String/Object} config A string to set only the panel's title, or a config object
46974  */
46975 Roo.GridPanel = function(grid, config){
46976     
46977   
46978     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46979         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46980         
46981     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46982     
46983     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46984     
46985     if(this.toolbar){
46986         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46987     }
46988     // xtype created footer. - not sure if will work as we normally have to render first..
46989     if (this.footer && !this.footer.el && this.footer.xtype) {
46990         
46991         this.footer.container = this.grid.getView().getFooterPanel(true);
46992         this.footer.dataSource = this.grid.dataSource;
46993         this.footer = Roo.factory(this.footer, Roo);
46994         
46995     }
46996     
46997     grid.monitorWindowResize = false; // turn off autosizing
46998     grid.autoHeight = false;
46999     grid.autoWidth = false;
47000     this.grid = grid;
47001     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47002 };
47003
47004 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47005     getId : function(){
47006         return this.grid.id;
47007     },
47008     
47009     /**
47010      * Returns the grid for this panel
47011      * @return {Roo.grid.Grid} 
47012      */
47013     getGrid : function(){
47014         return this.grid;    
47015     },
47016     
47017     setSize : function(width, height){
47018         if(!this.ignoreResize(width, height)){
47019             var grid = this.grid;
47020             var size = this.adjustForComponents(width, height);
47021             grid.getGridEl().setSize(size.width, size.height);
47022             grid.autoSize();
47023         }
47024     },
47025     
47026     beforeSlide : function(){
47027         this.grid.getView().scroller.clip();
47028     },
47029     
47030     afterSlide : function(){
47031         this.grid.getView().scroller.unclip();
47032     },
47033     
47034     destroy : function(){
47035         this.grid.destroy();
47036         delete this.grid;
47037         Roo.GridPanel.superclass.destroy.call(this); 
47038     }
47039 });
47040
47041
47042 /**
47043  * @class Roo.NestedLayoutPanel
47044  * @extends Roo.ContentPanel
47045  * @constructor
47046  * Create a new NestedLayoutPanel.
47047  * 
47048  * 
47049  * @param {Roo.BorderLayout} layout The layout for this panel
47050  * @param {String/Object} config A string to set only the title or a config object
47051  */
47052 Roo.NestedLayoutPanel = function(layout, config)
47053 {
47054     // construct with only one argument..
47055     /* FIXME - implement nicer consturctors
47056     if (layout.layout) {
47057         config = layout;
47058         layout = config.layout;
47059         delete config.layout;
47060     }
47061     if (layout.xtype && !layout.getEl) {
47062         // then layout needs constructing..
47063         layout = Roo.factory(layout, Roo);
47064     }
47065     */
47066     
47067     
47068     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47069     
47070     layout.monitorWindowResize = false; // turn off autosizing
47071     this.layout = layout;
47072     this.layout.getEl().addClass("x-layout-nested-layout");
47073     
47074     
47075     
47076     
47077 };
47078
47079 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47080
47081     setSize : function(width, height){
47082         if(!this.ignoreResize(width, height)){
47083             var size = this.adjustForComponents(width, height);
47084             var el = this.layout.getEl();
47085             el.setSize(size.width, size.height);
47086             var touch = el.dom.offsetWidth;
47087             this.layout.layout();
47088             // ie requires a double layout on the first pass
47089             if(Roo.isIE && !this.initialized){
47090                 this.initialized = true;
47091                 this.layout.layout();
47092             }
47093         }
47094     },
47095     
47096     // activate all subpanels if not currently active..
47097     
47098     setActiveState : function(active){
47099         this.active = active;
47100         if(!active){
47101             this.fireEvent("deactivate", this);
47102             return;
47103         }
47104         
47105         this.fireEvent("activate", this);
47106         // not sure if this should happen before or after..
47107         if (!this.layout) {
47108             return; // should not happen..
47109         }
47110         var reg = false;
47111         for (var r in this.layout.regions) {
47112             reg = this.layout.getRegion(r);
47113             if (reg.getActivePanel()) {
47114                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47115                 reg.setActivePanel(reg.getActivePanel());
47116                 continue;
47117             }
47118             if (!reg.panels.length) {
47119                 continue;
47120             }
47121             reg.showPanel(reg.getPanel(0));
47122         }
47123         
47124         
47125         
47126         
47127     },
47128     
47129     /**
47130      * Returns the nested BorderLayout for this panel
47131      * @return {Roo.BorderLayout} 
47132      */
47133     getLayout : function(){
47134         return this.layout;
47135     },
47136     
47137      /**
47138      * Adds a xtype elements to the layout of the nested panel
47139      * <pre><code>
47140
47141 panel.addxtype({
47142        xtype : 'ContentPanel',
47143        region: 'west',
47144        items: [ .... ]
47145    }
47146 );
47147
47148 panel.addxtype({
47149         xtype : 'NestedLayoutPanel',
47150         region: 'west',
47151         layout: {
47152            center: { },
47153            west: { }   
47154         },
47155         items : [ ... list of content panels or nested layout panels.. ]
47156    }
47157 );
47158 </code></pre>
47159      * @param {Object} cfg Xtype definition of item to add.
47160      */
47161     addxtype : function(cfg) {
47162         return this.layout.addxtype(cfg);
47163     
47164     }
47165 });
47166
47167 Roo.ScrollPanel = function(el, config, content){
47168     config = config || {};
47169     config.fitToFrame = true;
47170     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47171     
47172     this.el.dom.style.overflow = "hidden";
47173     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47174     this.el.removeClass("x-layout-inactive-content");
47175     this.el.on("mousewheel", this.onWheel, this);
47176
47177     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47178     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47179     up.unselectable(); down.unselectable();
47180     up.on("click", this.scrollUp, this);
47181     down.on("click", this.scrollDown, this);
47182     up.addClassOnOver("x-scroller-btn-over");
47183     down.addClassOnOver("x-scroller-btn-over");
47184     up.addClassOnClick("x-scroller-btn-click");
47185     down.addClassOnClick("x-scroller-btn-click");
47186     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47187
47188     this.resizeEl = this.el;
47189     this.el = wrap; this.up = up; this.down = down;
47190 };
47191
47192 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47193     increment : 100,
47194     wheelIncrement : 5,
47195     scrollUp : function(){
47196         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47197     },
47198
47199     scrollDown : function(){
47200         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47201     },
47202
47203     afterScroll : function(){
47204         var el = this.resizeEl;
47205         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47206         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47207         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47208     },
47209
47210     setSize : function(){
47211         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47212         this.afterScroll();
47213     },
47214
47215     onWheel : function(e){
47216         var d = e.getWheelDelta();
47217         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47218         this.afterScroll();
47219         e.stopEvent();
47220     },
47221
47222     setContent : function(content, loadScripts){
47223         this.resizeEl.update(content, loadScripts);
47224     }
47225
47226 });
47227
47228
47229
47230
47231
47232
47233
47234
47235
47236 /**
47237  * @class Roo.TreePanel
47238  * @extends Roo.ContentPanel
47239  * @constructor
47240  * Create a new TreePanel. - defaults to fit/scoll contents.
47241  * @param {String/Object} config A string to set only the panel's title, or a config object
47242  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47243  */
47244 Roo.TreePanel = function(config){
47245     var el = config.el;
47246     var tree = config.tree;
47247     delete config.tree; 
47248     delete config.el; // hopefull!
47249     
47250     // wrapper for IE7 strict & safari scroll issue
47251     
47252     var treeEl = el.createChild();
47253     config.resizeEl = treeEl;
47254     
47255     
47256     
47257     Roo.TreePanel.superclass.constructor.call(this, el, config);
47258  
47259  
47260     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47261     //console.log(tree);
47262     this.on('activate', function()
47263     {
47264         if (this.tree.rendered) {
47265             return;
47266         }
47267         //console.log('render tree');
47268         this.tree.render();
47269     });
47270     
47271     this.on('resize',  function (cp, w, h) {
47272             this.tree.innerCt.setWidth(w);
47273             this.tree.innerCt.setHeight(h);
47274             this.tree.innerCt.setStyle('overflow-y', 'auto');
47275     });
47276
47277         
47278     
47279 };
47280
47281 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47282     fitToFrame : true,
47283     autoScroll : true
47284 });
47285
47286
47287
47288
47289
47290
47291
47292
47293
47294
47295
47296 /*
47297  * Based on:
47298  * Ext JS Library 1.1.1
47299  * Copyright(c) 2006-2007, Ext JS, LLC.
47300  *
47301  * Originally Released Under LGPL - original licence link has changed is not relivant.
47302  *
47303  * Fork - LGPL
47304  * <script type="text/javascript">
47305  */
47306  
47307
47308 /**
47309  * @class Roo.ReaderLayout
47310  * @extends Roo.BorderLayout
47311  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47312  * center region containing two nested regions (a top one for a list view and one for item preview below),
47313  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47314  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47315  * expedites the setup of the overall layout and regions for this common application style.
47316  * Example:
47317  <pre><code>
47318 var reader = new Roo.ReaderLayout();
47319 var CP = Roo.ContentPanel;  // shortcut for adding
47320
47321 reader.beginUpdate();
47322 reader.add("north", new CP("north", "North"));
47323 reader.add("west", new CP("west", {title: "West"}));
47324 reader.add("east", new CP("east", {title: "East"}));
47325
47326 reader.regions.listView.add(new CP("listView", "List"));
47327 reader.regions.preview.add(new CP("preview", "Preview"));
47328 reader.endUpdate();
47329 </code></pre>
47330 * @constructor
47331 * Create a new ReaderLayout
47332 * @param {Object} config Configuration options
47333 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47334 * document.body if omitted)
47335 */
47336 Roo.ReaderLayout = function(config, renderTo){
47337     var c = config || {size:{}};
47338     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47339         north: c.north !== false ? Roo.apply({
47340             split:false,
47341             initialSize: 32,
47342             titlebar: false
47343         }, c.north) : false,
47344         west: c.west !== false ? Roo.apply({
47345             split:true,
47346             initialSize: 200,
47347             minSize: 175,
47348             maxSize: 400,
47349             titlebar: true,
47350             collapsible: true,
47351             animate: true,
47352             margins:{left:5,right:0,bottom:5,top:5},
47353             cmargins:{left:5,right:5,bottom:5,top:5}
47354         }, c.west) : false,
47355         east: c.east !== false ? Roo.apply({
47356             split:true,
47357             initialSize: 200,
47358             minSize: 175,
47359             maxSize: 400,
47360             titlebar: true,
47361             collapsible: true,
47362             animate: true,
47363             margins:{left:0,right:5,bottom:5,top:5},
47364             cmargins:{left:5,right:5,bottom:5,top:5}
47365         }, c.east) : false,
47366         center: Roo.apply({
47367             tabPosition: 'top',
47368             autoScroll:false,
47369             closeOnTab: true,
47370             titlebar:false,
47371             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47372         }, c.center)
47373     });
47374
47375     this.el.addClass('x-reader');
47376
47377     this.beginUpdate();
47378
47379     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47380         south: c.preview !== false ? Roo.apply({
47381             split:true,
47382             initialSize: 200,
47383             minSize: 100,
47384             autoScroll:true,
47385             collapsible:true,
47386             titlebar: true,
47387             cmargins:{top:5,left:0, right:0, bottom:0}
47388         }, c.preview) : false,
47389         center: Roo.apply({
47390             autoScroll:false,
47391             titlebar:false,
47392             minHeight:200
47393         }, c.listView)
47394     });
47395     this.add('center', new Roo.NestedLayoutPanel(inner,
47396             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47397
47398     this.endUpdate();
47399
47400     this.regions.preview = inner.getRegion('south');
47401     this.regions.listView = inner.getRegion('center');
47402 };
47403
47404 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47405  * Based on:
47406  * Ext JS Library 1.1.1
47407  * Copyright(c) 2006-2007, Ext JS, LLC.
47408  *
47409  * Originally Released Under LGPL - original licence link has changed is not relivant.
47410  *
47411  * Fork - LGPL
47412  * <script type="text/javascript">
47413  */
47414  
47415 /**
47416  * @class Roo.grid.Grid
47417  * @extends Roo.util.Observable
47418  * This class represents the primary interface of a component based grid control.
47419  * <br><br>Usage:<pre><code>
47420  var grid = new Roo.grid.Grid("my-container-id", {
47421      ds: myDataStore,
47422      cm: myColModel,
47423      selModel: mySelectionModel,
47424      autoSizeColumns: true,
47425      monitorWindowResize: false,
47426      trackMouseOver: true
47427  });
47428  // set any options
47429  grid.render();
47430  * </code></pre>
47431  * <b>Common Problems:</b><br/>
47432  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47433  * element will correct this<br/>
47434  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47435  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47436  * are unpredictable.<br/>
47437  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47438  * grid to calculate dimensions/offsets.<br/>
47439   * @constructor
47440  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47441  * The container MUST have some type of size defined for the grid to fill. The container will be
47442  * automatically set to position relative if it isn't already.
47443  * @param {Object} config A config object that sets properties on this grid.
47444  */
47445 Roo.grid.Grid = function(container, config){
47446         // initialize the container
47447         this.container = Roo.get(container);
47448         this.container.update("");
47449         this.container.setStyle("overflow", "hidden");
47450     this.container.addClass('x-grid-container');
47451
47452     this.id = this.container.id;
47453
47454     Roo.apply(this, config);
47455     // check and correct shorthanded configs
47456     if(this.ds){
47457         this.dataSource = this.ds;
47458         delete this.ds;
47459     }
47460     if(this.cm){
47461         this.colModel = this.cm;
47462         delete this.cm;
47463     }
47464     if(this.sm){
47465         this.selModel = this.sm;
47466         delete this.sm;
47467     }
47468
47469     if (this.selModel) {
47470         this.selModel = Roo.factory(this.selModel, Roo.grid);
47471         this.sm = this.selModel;
47472         this.sm.xmodule = this.xmodule || false;
47473     }
47474     if (typeof(this.colModel.config) == 'undefined') {
47475         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47476         this.cm = this.colModel;
47477         this.cm.xmodule = this.xmodule || false;
47478     }
47479     if (this.dataSource) {
47480         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47481         this.ds = this.dataSource;
47482         this.ds.xmodule = this.xmodule || false;
47483          
47484     }
47485     
47486     
47487     
47488     if(this.width){
47489         this.container.setWidth(this.width);
47490     }
47491
47492     if(this.height){
47493         this.container.setHeight(this.height);
47494     }
47495     /** @private */
47496         this.addEvents({
47497         // raw events
47498         /**
47499          * @event click
47500          * The raw click event for the entire grid.
47501          * @param {Roo.EventObject} e
47502          */
47503         "click" : true,
47504         /**
47505          * @event dblclick
47506          * The raw dblclick event for the entire grid.
47507          * @param {Roo.EventObject} e
47508          */
47509         "dblclick" : true,
47510         /**
47511          * @event contextmenu
47512          * The raw contextmenu event for the entire grid.
47513          * @param {Roo.EventObject} e
47514          */
47515         "contextmenu" : true,
47516         /**
47517          * @event mousedown
47518          * The raw mousedown event for the entire grid.
47519          * @param {Roo.EventObject} e
47520          */
47521         "mousedown" : true,
47522         /**
47523          * @event mouseup
47524          * The raw mouseup event for the entire grid.
47525          * @param {Roo.EventObject} e
47526          */
47527         "mouseup" : true,
47528         /**
47529          * @event mouseover
47530          * The raw mouseover event for the entire grid.
47531          * @param {Roo.EventObject} e
47532          */
47533         "mouseover" : true,
47534         /**
47535          * @event mouseout
47536          * The raw mouseout event for the entire grid.
47537          * @param {Roo.EventObject} e
47538          */
47539         "mouseout" : true,
47540         /**
47541          * @event keypress
47542          * The raw keypress event for the entire grid.
47543          * @param {Roo.EventObject} e
47544          */
47545         "keypress" : true,
47546         /**
47547          * @event keydown
47548          * The raw keydown event for the entire grid.
47549          * @param {Roo.EventObject} e
47550          */
47551         "keydown" : true,
47552
47553         // custom events
47554
47555         /**
47556          * @event cellclick
47557          * Fires when a cell is clicked
47558          * @param {Grid} this
47559          * @param {Number} rowIndex
47560          * @param {Number} columnIndex
47561          * @param {Roo.EventObject} e
47562          */
47563         "cellclick" : true,
47564         /**
47565          * @event celldblclick
47566          * Fires when a cell is double clicked
47567          * @param {Grid} this
47568          * @param {Number} rowIndex
47569          * @param {Number} columnIndex
47570          * @param {Roo.EventObject} e
47571          */
47572         "celldblclick" : true,
47573         /**
47574          * @event rowclick
47575          * Fires when a row is clicked
47576          * @param {Grid} this
47577          * @param {Number} rowIndex
47578          * @param {Roo.EventObject} e
47579          */
47580         "rowclick" : true,
47581         /**
47582          * @event rowdblclick
47583          * Fires when a row is double clicked
47584          * @param {Grid} this
47585          * @param {Number} rowIndex
47586          * @param {Roo.EventObject} e
47587          */
47588         "rowdblclick" : true,
47589         /**
47590          * @event headerclick
47591          * Fires when a header is clicked
47592          * @param {Grid} this
47593          * @param {Number} columnIndex
47594          * @param {Roo.EventObject} e
47595          */
47596         "headerclick" : true,
47597         /**
47598          * @event headerdblclick
47599          * Fires when a header cell is double clicked
47600          * @param {Grid} this
47601          * @param {Number} columnIndex
47602          * @param {Roo.EventObject} e
47603          */
47604         "headerdblclick" : true,
47605         /**
47606          * @event rowcontextmenu
47607          * Fires when a row is right clicked
47608          * @param {Grid} this
47609          * @param {Number} rowIndex
47610          * @param {Roo.EventObject} e
47611          */
47612         "rowcontextmenu" : true,
47613         /**
47614          * @event cellcontextmenu
47615          * Fires when a cell is right clicked
47616          * @param {Grid} this
47617          * @param {Number} rowIndex
47618          * @param {Number} cellIndex
47619          * @param {Roo.EventObject} e
47620          */
47621          "cellcontextmenu" : true,
47622         /**
47623          * @event headercontextmenu
47624          * Fires when a header is right clicked
47625          * @param {Grid} this
47626          * @param {Number} columnIndex
47627          * @param {Roo.EventObject} e
47628          */
47629         "headercontextmenu" : true,
47630         /**
47631          * @event bodyscroll
47632          * Fires when the body element is scrolled
47633          * @param {Number} scrollLeft
47634          * @param {Number} scrollTop
47635          */
47636         "bodyscroll" : true,
47637         /**
47638          * @event columnresize
47639          * Fires when the user resizes a column
47640          * @param {Number} columnIndex
47641          * @param {Number} newSize
47642          */
47643         "columnresize" : true,
47644         /**
47645          * @event columnmove
47646          * Fires when the user moves a column
47647          * @param {Number} oldIndex
47648          * @param {Number} newIndex
47649          */
47650         "columnmove" : true,
47651         /**
47652          * @event startdrag
47653          * Fires when row(s) start being dragged
47654          * @param {Grid} this
47655          * @param {Roo.GridDD} dd The drag drop object
47656          * @param {event} e The raw browser event
47657          */
47658         "startdrag" : true,
47659         /**
47660          * @event enddrag
47661          * Fires when a drag operation is complete
47662          * @param {Grid} this
47663          * @param {Roo.GridDD} dd The drag drop object
47664          * @param {event} e The raw browser event
47665          */
47666         "enddrag" : true,
47667         /**
47668          * @event dragdrop
47669          * Fires when dragged row(s) are dropped on a valid DD target
47670          * @param {Grid} this
47671          * @param {Roo.GridDD} dd The drag drop object
47672          * @param {String} targetId The target drag drop object
47673          * @param {event} e The raw browser event
47674          */
47675         "dragdrop" : true,
47676         /**
47677          * @event dragover
47678          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47679          * @param {Grid} this
47680          * @param {Roo.GridDD} dd The drag drop object
47681          * @param {String} targetId The target drag drop object
47682          * @param {event} e The raw browser event
47683          */
47684         "dragover" : true,
47685         /**
47686          * @event dragenter
47687          *  Fires when the dragged row(s) first cross another DD target while being dragged
47688          * @param {Grid} this
47689          * @param {Roo.GridDD} dd The drag drop object
47690          * @param {String} targetId The target drag drop object
47691          * @param {event} e The raw browser event
47692          */
47693         "dragenter" : true,
47694         /**
47695          * @event dragout
47696          * Fires when the dragged row(s) leave another DD target while being dragged
47697          * @param {Grid} this
47698          * @param {Roo.GridDD} dd The drag drop object
47699          * @param {String} targetId The target drag drop object
47700          * @param {event} e The raw browser event
47701          */
47702         "dragout" : true,
47703         /**
47704          * @event rowclass
47705          * Fires when a row is rendered, so you can change add a style to it.
47706          * @param {GridView} gridview   The grid view
47707          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47708          */
47709         'rowclass' : true,
47710
47711         /**
47712          * @event render
47713          * Fires when the grid is rendered
47714          * @param {Grid} grid
47715          */
47716         'render' : true
47717     });
47718
47719     Roo.grid.Grid.superclass.constructor.call(this);
47720 };
47721 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47722     
47723     /**
47724      * @cfg {String} ddGroup - drag drop group.
47725      */
47726
47727     /**
47728      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47729      */
47730     minColumnWidth : 25,
47731
47732     /**
47733      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47734      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47735      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47736      */
47737     autoSizeColumns : false,
47738
47739     /**
47740      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47741      */
47742     autoSizeHeaders : true,
47743
47744     /**
47745      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47746      */
47747     monitorWindowResize : true,
47748
47749     /**
47750      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47751      * rows measured to get a columns size. Default is 0 (all rows).
47752      */
47753     maxRowsToMeasure : 0,
47754
47755     /**
47756      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47757      */
47758     trackMouseOver : true,
47759
47760     /**
47761     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47762     */
47763     
47764     /**
47765     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47766     */
47767     enableDragDrop : false,
47768     
47769     /**
47770     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47771     */
47772     enableColumnMove : true,
47773     
47774     /**
47775     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47776     */
47777     enableColumnHide : true,
47778     
47779     /**
47780     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47781     */
47782     enableRowHeightSync : false,
47783     
47784     /**
47785     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47786     */
47787     stripeRows : true,
47788     
47789     /**
47790     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47791     */
47792     autoHeight : false,
47793
47794     /**
47795      * @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.
47796      */
47797     autoExpandColumn : false,
47798
47799     /**
47800     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47801     * Default is 50.
47802     */
47803     autoExpandMin : 50,
47804
47805     /**
47806     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47807     */
47808     autoExpandMax : 1000,
47809
47810     /**
47811     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47812     */
47813     view : null,
47814
47815     /**
47816     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47817     */
47818     loadMask : false,
47819     /**
47820     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47821     */
47822     dropTarget: false,
47823     
47824    
47825     
47826     // private
47827     rendered : false,
47828
47829     /**
47830     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47831     * of a fixed width. Default is false.
47832     */
47833     /**
47834     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47835     */
47836     /**
47837      * Called once after all setup has been completed and the grid is ready to be rendered.
47838      * @return {Roo.grid.Grid} this
47839      */
47840     render : function()
47841     {
47842         var c = this.container;
47843         // try to detect autoHeight/width mode
47844         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47845             this.autoHeight = true;
47846         }
47847         var view = this.getView();
47848         view.init(this);
47849
47850         c.on("click", this.onClick, this);
47851         c.on("dblclick", this.onDblClick, this);
47852         c.on("contextmenu", this.onContextMenu, this);
47853         c.on("keydown", this.onKeyDown, this);
47854
47855         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47856
47857         this.getSelectionModel().init(this);
47858
47859         view.render();
47860
47861         if(this.loadMask){
47862             this.loadMask = new Roo.LoadMask(this.container,
47863                     Roo.apply({store:this.dataSource}, this.loadMask));
47864         }
47865         
47866         
47867         if (this.toolbar && this.toolbar.xtype) {
47868             this.toolbar.container = this.getView().getHeaderPanel(true);
47869             this.toolbar = new Roo.Toolbar(this.toolbar);
47870         }
47871         if (this.footer && this.footer.xtype) {
47872             this.footer.dataSource = this.getDataSource();
47873             this.footer.container = this.getView().getFooterPanel(true);
47874             this.footer = Roo.factory(this.footer, Roo);
47875         }
47876         if (this.dropTarget && this.dropTarget.xtype) {
47877             delete this.dropTarget.xtype;
47878             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47879         }
47880         
47881         
47882         this.rendered = true;
47883         this.fireEvent('render', this);
47884         return this;
47885     },
47886
47887         /**
47888          * Reconfigures the grid to use a different Store and Column Model.
47889          * The View will be bound to the new objects and refreshed.
47890          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47891          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47892          */
47893     reconfigure : function(dataSource, colModel){
47894         if(this.loadMask){
47895             this.loadMask.destroy();
47896             this.loadMask = new Roo.LoadMask(this.container,
47897                     Roo.apply({store:dataSource}, this.loadMask));
47898         }
47899         this.view.bind(dataSource, colModel);
47900         this.dataSource = dataSource;
47901         this.colModel = colModel;
47902         this.view.refresh(true);
47903     },
47904
47905     // private
47906     onKeyDown : function(e){
47907         this.fireEvent("keydown", e);
47908     },
47909
47910     /**
47911      * Destroy this grid.
47912      * @param {Boolean} removeEl True to remove the element
47913      */
47914     destroy : function(removeEl, keepListeners){
47915         if(this.loadMask){
47916             this.loadMask.destroy();
47917         }
47918         var c = this.container;
47919         c.removeAllListeners();
47920         this.view.destroy();
47921         this.colModel.purgeListeners();
47922         if(!keepListeners){
47923             this.purgeListeners();
47924         }
47925         c.update("");
47926         if(removeEl === true){
47927             c.remove();
47928         }
47929     },
47930
47931     // private
47932     processEvent : function(name, e){
47933         this.fireEvent(name, e);
47934         var t = e.getTarget();
47935         var v = this.view;
47936         var header = v.findHeaderIndex(t);
47937         if(header !== false){
47938             this.fireEvent("header" + name, this, header, e);
47939         }else{
47940             var row = v.findRowIndex(t);
47941             var cell = v.findCellIndex(t);
47942             if(row !== false){
47943                 this.fireEvent("row" + name, this, row, e);
47944                 if(cell !== false){
47945                     this.fireEvent("cell" + name, this, row, cell, e);
47946                 }
47947             }
47948         }
47949     },
47950
47951     // private
47952     onClick : function(e){
47953         this.processEvent("click", e);
47954     },
47955
47956     // private
47957     onContextMenu : function(e, t){
47958         this.processEvent("contextmenu", e);
47959     },
47960
47961     // private
47962     onDblClick : function(e){
47963         this.processEvent("dblclick", e);
47964     },
47965
47966     // private
47967     walkCells : function(row, col, step, fn, scope){
47968         var cm = this.colModel, clen = cm.getColumnCount();
47969         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47970         if(step < 0){
47971             if(col < 0){
47972                 row--;
47973                 first = false;
47974             }
47975             while(row >= 0){
47976                 if(!first){
47977                     col = clen-1;
47978                 }
47979                 first = false;
47980                 while(col >= 0){
47981                     if(fn.call(scope || this, row, col, cm) === true){
47982                         return [row, col];
47983                     }
47984                     col--;
47985                 }
47986                 row--;
47987             }
47988         } else {
47989             if(col >= clen){
47990                 row++;
47991                 first = false;
47992             }
47993             while(row < rlen){
47994                 if(!first){
47995                     col = 0;
47996                 }
47997                 first = false;
47998                 while(col < clen){
47999                     if(fn.call(scope || this, row, col, cm) === true){
48000                         return [row, col];
48001                     }
48002                     col++;
48003                 }
48004                 row++;
48005             }
48006         }
48007         return null;
48008     },
48009
48010     // private
48011     getSelections : function(){
48012         return this.selModel.getSelections();
48013     },
48014
48015     /**
48016      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48017      * but if manual update is required this method will initiate it.
48018      */
48019     autoSize : function(){
48020         if(this.rendered){
48021             this.view.layout();
48022             if(this.view.adjustForScroll){
48023                 this.view.adjustForScroll();
48024             }
48025         }
48026     },
48027
48028     /**
48029      * Returns the grid's underlying element.
48030      * @return {Element} The element
48031      */
48032     getGridEl : function(){
48033         return this.container;
48034     },
48035
48036     // private for compatibility, overridden by editor grid
48037     stopEditing : function(){},
48038
48039     /**
48040      * Returns the grid's SelectionModel.
48041      * @return {SelectionModel}
48042      */
48043     getSelectionModel : function(){
48044         if(!this.selModel){
48045             this.selModel = new Roo.grid.RowSelectionModel();
48046         }
48047         return this.selModel;
48048     },
48049
48050     /**
48051      * Returns the grid's DataSource.
48052      * @return {DataSource}
48053      */
48054     getDataSource : function(){
48055         return this.dataSource;
48056     },
48057
48058     /**
48059      * Returns the grid's ColumnModel.
48060      * @return {ColumnModel}
48061      */
48062     getColumnModel : function(){
48063         return this.colModel;
48064     },
48065
48066     /**
48067      * Returns the grid's GridView object.
48068      * @return {GridView}
48069      */
48070     getView : function(){
48071         if(!this.view){
48072             this.view = new Roo.grid.GridView(this.viewConfig);
48073         }
48074         return this.view;
48075     },
48076     /**
48077      * Called to get grid's drag proxy text, by default returns this.ddText.
48078      * @return {String}
48079      */
48080     getDragDropText : function(){
48081         var count = this.selModel.getCount();
48082         return String.format(this.ddText, count, count == 1 ? '' : 's');
48083     }
48084 });
48085 /**
48086  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48087  * %0 is replaced with the number of selected rows.
48088  * @type String
48089  */
48090 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48091  * Based on:
48092  * Ext JS Library 1.1.1
48093  * Copyright(c) 2006-2007, Ext JS, LLC.
48094  *
48095  * Originally Released Under LGPL - original licence link has changed is not relivant.
48096  *
48097  * Fork - LGPL
48098  * <script type="text/javascript">
48099  */
48100  
48101 Roo.grid.AbstractGridView = function(){
48102         this.grid = null;
48103         
48104         this.events = {
48105             "beforerowremoved" : true,
48106             "beforerowsinserted" : true,
48107             "beforerefresh" : true,
48108             "rowremoved" : true,
48109             "rowsinserted" : true,
48110             "rowupdated" : true,
48111             "refresh" : true
48112         };
48113     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48114 };
48115
48116 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48117     rowClass : "x-grid-row",
48118     cellClass : "x-grid-cell",
48119     tdClass : "x-grid-td",
48120     hdClass : "x-grid-hd",
48121     splitClass : "x-grid-hd-split",
48122     
48123         init: function(grid){
48124         this.grid = grid;
48125                 var cid = this.grid.getGridEl().id;
48126         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48127         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48128         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48129         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48130         },
48131         
48132         getColumnRenderers : function(){
48133         var renderers = [];
48134         var cm = this.grid.colModel;
48135         var colCount = cm.getColumnCount();
48136         for(var i = 0; i < colCount; i++){
48137             renderers[i] = cm.getRenderer(i);
48138         }
48139         return renderers;
48140     },
48141     
48142     getColumnIds : function(){
48143         var ids = [];
48144         var cm = this.grid.colModel;
48145         var colCount = cm.getColumnCount();
48146         for(var i = 0; i < colCount; i++){
48147             ids[i] = cm.getColumnId(i);
48148         }
48149         return ids;
48150     },
48151     
48152     getDataIndexes : function(){
48153         if(!this.indexMap){
48154             this.indexMap = this.buildIndexMap();
48155         }
48156         return this.indexMap.colToData;
48157     },
48158     
48159     getColumnIndexByDataIndex : function(dataIndex){
48160         if(!this.indexMap){
48161             this.indexMap = this.buildIndexMap();
48162         }
48163         return this.indexMap.dataToCol[dataIndex];
48164     },
48165     
48166     /**
48167      * Set a css style for a column dynamically. 
48168      * @param {Number} colIndex The index of the column
48169      * @param {String} name The css property name
48170      * @param {String} value The css value
48171      */
48172     setCSSStyle : function(colIndex, name, value){
48173         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48174         Roo.util.CSS.updateRule(selector, name, value);
48175     },
48176     
48177     generateRules : function(cm){
48178         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48179         Roo.util.CSS.removeStyleSheet(rulesId);
48180         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48181             var cid = cm.getColumnId(i);
48182             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48183                          this.tdSelector, cid, " {\n}\n",
48184                          this.hdSelector, cid, " {\n}\n",
48185                          this.splitSelector, cid, " {\n}\n");
48186         }
48187         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48188     }
48189 });/*
48190  * Based on:
48191  * Ext JS Library 1.1.1
48192  * Copyright(c) 2006-2007, Ext JS, LLC.
48193  *
48194  * Originally Released Under LGPL - original licence link has changed is not relivant.
48195  *
48196  * Fork - LGPL
48197  * <script type="text/javascript">
48198  */
48199
48200 // private
48201 // This is a support class used internally by the Grid components
48202 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48203     this.grid = grid;
48204     this.view = grid.getView();
48205     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48206     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48207     if(hd2){
48208         this.setHandleElId(Roo.id(hd));
48209         this.setOuterHandleElId(Roo.id(hd2));
48210     }
48211     this.scroll = false;
48212 };
48213 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48214     maxDragWidth: 120,
48215     getDragData : function(e){
48216         var t = Roo.lib.Event.getTarget(e);
48217         var h = this.view.findHeaderCell(t);
48218         if(h){
48219             return {ddel: h.firstChild, header:h};
48220         }
48221         return false;
48222     },
48223
48224     onInitDrag : function(e){
48225         this.view.headersDisabled = true;
48226         var clone = this.dragData.ddel.cloneNode(true);
48227         clone.id = Roo.id();
48228         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48229         this.proxy.update(clone);
48230         return true;
48231     },
48232
48233     afterValidDrop : function(){
48234         var v = this.view;
48235         setTimeout(function(){
48236             v.headersDisabled = false;
48237         }, 50);
48238     },
48239
48240     afterInvalidDrop : function(){
48241         var v = this.view;
48242         setTimeout(function(){
48243             v.headersDisabled = false;
48244         }, 50);
48245     }
48246 });
48247 /*
48248  * Based on:
48249  * Ext JS Library 1.1.1
48250  * Copyright(c) 2006-2007, Ext JS, LLC.
48251  *
48252  * Originally Released Under LGPL - original licence link has changed is not relivant.
48253  *
48254  * Fork - LGPL
48255  * <script type="text/javascript">
48256  */
48257 // private
48258 // This is a support class used internally by the Grid components
48259 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48260     this.grid = grid;
48261     this.view = grid.getView();
48262     // split the proxies so they don't interfere with mouse events
48263     this.proxyTop = Roo.DomHelper.append(document.body, {
48264         cls:"col-move-top", html:"&#160;"
48265     }, true);
48266     this.proxyBottom = Roo.DomHelper.append(document.body, {
48267         cls:"col-move-bottom", html:"&#160;"
48268     }, true);
48269     this.proxyTop.hide = this.proxyBottom.hide = function(){
48270         this.setLeftTop(-100,-100);
48271         this.setStyle("visibility", "hidden");
48272     };
48273     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48274     // temporarily disabled
48275     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48276     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48277 };
48278 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48279     proxyOffsets : [-4, -9],
48280     fly: Roo.Element.fly,
48281
48282     getTargetFromEvent : function(e){
48283         var t = Roo.lib.Event.getTarget(e);
48284         var cindex = this.view.findCellIndex(t);
48285         if(cindex !== false){
48286             return this.view.getHeaderCell(cindex);
48287         }
48288         return null;
48289     },
48290
48291     nextVisible : function(h){
48292         var v = this.view, cm = this.grid.colModel;
48293         h = h.nextSibling;
48294         while(h){
48295             if(!cm.isHidden(v.getCellIndex(h))){
48296                 return h;
48297             }
48298             h = h.nextSibling;
48299         }
48300         return null;
48301     },
48302
48303     prevVisible : function(h){
48304         var v = this.view, cm = this.grid.colModel;
48305         h = h.prevSibling;
48306         while(h){
48307             if(!cm.isHidden(v.getCellIndex(h))){
48308                 return h;
48309             }
48310             h = h.prevSibling;
48311         }
48312         return null;
48313     },
48314
48315     positionIndicator : function(h, n, e){
48316         var x = Roo.lib.Event.getPageX(e);
48317         var r = Roo.lib.Dom.getRegion(n.firstChild);
48318         var px, pt, py = r.top + this.proxyOffsets[1];
48319         if((r.right - x) <= (r.right-r.left)/2){
48320             px = r.right+this.view.borderWidth;
48321             pt = "after";
48322         }else{
48323             px = r.left;
48324             pt = "before";
48325         }
48326         var oldIndex = this.view.getCellIndex(h);
48327         var newIndex = this.view.getCellIndex(n);
48328
48329         if(this.grid.colModel.isFixed(newIndex)){
48330             return false;
48331         }
48332
48333         var locked = this.grid.colModel.isLocked(newIndex);
48334
48335         if(pt == "after"){
48336             newIndex++;
48337         }
48338         if(oldIndex < newIndex){
48339             newIndex--;
48340         }
48341         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48342             return false;
48343         }
48344         px +=  this.proxyOffsets[0];
48345         this.proxyTop.setLeftTop(px, py);
48346         this.proxyTop.show();
48347         if(!this.bottomOffset){
48348             this.bottomOffset = this.view.mainHd.getHeight();
48349         }
48350         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48351         this.proxyBottom.show();
48352         return pt;
48353     },
48354
48355     onNodeEnter : function(n, dd, e, data){
48356         if(data.header != n){
48357             this.positionIndicator(data.header, n, e);
48358         }
48359     },
48360
48361     onNodeOver : function(n, dd, e, data){
48362         var result = false;
48363         if(data.header != n){
48364             result = this.positionIndicator(data.header, n, e);
48365         }
48366         if(!result){
48367             this.proxyTop.hide();
48368             this.proxyBottom.hide();
48369         }
48370         return result ? this.dropAllowed : this.dropNotAllowed;
48371     },
48372
48373     onNodeOut : function(n, dd, e, data){
48374         this.proxyTop.hide();
48375         this.proxyBottom.hide();
48376     },
48377
48378     onNodeDrop : function(n, dd, e, data){
48379         var h = data.header;
48380         if(h != n){
48381             var cm = this.grid.colModel;
48382             var x = Roo.lib.Event.getPageX(e);
48383             var r = Roo.lib.Dom.getRegion(n.firstChild);
48384             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48385             var oldIndex = this.view.getCellIndex(h);
48386             var newIndex = this.view.getCellIndex(n);
48387             var locked = cm.isLocked(newIndex);
48388             if(pt == "after"){
48389                 newIndex++;
48390             }
48391             if(oldIndex < newIndex){
48392                 newIndex--;
48393             }
48394             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48395                 return false;
48396             }
48397             cm.setLocked(oldIndex, locked, true);
48398             cm.moveColumn(oldIndex, newIndex);
48399             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48400             return true;
48401         }
48402         return false;
48403     }
48404 });
48405 /*
48406  * Based on:
48407  * Ext JS Library 1.1.1
48408  * Copyright(c) 2006-2007, Ext JS, LLC.
48409  *
48410  * Originally Released Under LGPL - original licence link has changed is not relivant.
48411  *
48412  * Fork - LGPL
48413  * <script type="text/javascript">
48414  */
48415   
48416 /**
48417  * @class Roo.grid.GridView
48418  * @extends Roo.util.Observable
48419  *
48420  * @constructor
48421  * @param {Object} config
48422  */
48423 Roo.grid.GridView = function(config){
48424     Roo.grid.GridView.superclass.constructor.call(this);
48425     this.el = null;
48426
48427     Roo.apply(this, config);
48428 };
48429
48430 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48431
48432     
48433     rowClass : "x-grid-row",
48434
48435     cellClass : "x-grid-col",
48436
48437     tdClass : "x-grid-td",
48438
48439     hdClass : "x-grid-hd",
48440
48441     splitClass : "x-grid-split",
48442
48443     sortClasses : ["sort-asc", "sort-desc"],
48444
48445     enableMoveAnim : false,
48446
48447     hlColor: "C3DAF9",
48448
48449     dh : Roo.DomHelper,
48450
48451     fly : Roo.Element.fly,
48452
48453     css : Roo.util.CSS,
48454
48455     borderWidth: 1,
48456
48457     splitOffset: 3,
48458
48459     scrollIncrement : 22,
48460
48461     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48462
48463     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48464
48465     bind : function(ds, cm){
48466         if(this.ds){
48467             this.ds.un("load", this.onLoad, this);
48468             this.ds.un("datachanged", this.onDataChange, this);
48469             this.ds.un("add", this.onAdd, this);
48470             this.ds.un("remove", this.onRemove, this);
48471             this.ds.un("update", this.onUpdate, this);
48472             this.ds.un("clear", this.onClear, this);
48473         }
48474         if(ds){
48475             ds.on("load", this.onLoad, this);
48476             ds.on("datachanged", this.onDataChange, this);
48477             ds.on("add", this.onAdd, this);
48478             ds.on("remove", this.onRemove, this);
48479             ds.on("update", this.onUpdate, this);
48480             ds.on("clear", this.onClear, this);
48481         }
48482         this.ds = ds;
48483
48484         if(this.cm){
48485             this.cm.un("widthchange", this.onColWidthChange, this);
48486             this.cm.un("headerchange", this.onHeaderChange, this);
48487             this.cm.un("hiddenchange", this.onHiddenChange, this);
48488             this.cm.un("columnmoved", this.onColumnMove, this);
48489             this.cm.un("columnlockchange", this.onColumnLock, this);
48490         }
48491         if(cm){
48492             this.generateRules(cm);
48493             cm.on("widthchange", this.onColWidthChange, this);
48494             cm.on("headerchange", this.onHeaderChange, this);
48495             cm.on("hiddenchange", this.onHiddenChange, this);
48496             cm.on("columnmoved", this.onColumnMove, this);
48497             cm.on("columnlockchange", this.onColumnLock, this);
48498         }
48499         this.cm = cm;
48500     },
48501
48502     init: function(grid){
48503         Roo.grid.GridView.superclass.init.call(this, grid);
48504
48505         this.bind(grid.dataSource, grid.colModel);
48506
48507         grid.on("headerclick", this.handleHeaderClick, this);
48508
48509         if(grid.trackMouseOver){
48510             grid.on("mouseover", this.onRowOver, this);
48511             grid.on("mouseout", this.onRowOut, this);
48512         }
48513         grid.cancelTextSelection = function(){};
48514         this.gridId = grid.id;
48515
48516         var tpls = this.templates || {};
48517
48518         if(!tpls.master){
48519             tpls.master = new Roo.Template(
48520                '<div class="x-grid" hidefocus="true">',
48521                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48522                   '<div class="x-grid-topbar"></div>',
48523                   '<div class="x-grid-scroller"><div></div></div>',
48524                   '<div class="x-grid-locked">',
48525                       '<div class="x-grid-header">{lockedHeader}</div>',
48526                       '<div class="x-grid-body">{lockedBody}</div>',
48527                   "</div>",
48528                   '<div class="x-grid-viewport">',
48529                       '<div class="x-grid-header">{header}</div>',
48530                       '<div class="x-grid-body">{body}</div>',
48531                   "</div>",
48532                   '<div class="x-grid-bottombar"></div>',
48533                  
48534                   '<div class="x-grid-resize-proxy">&#160;</div>',
48535                "</div>"
48536             );
48537             tpls.master.disableformats = true;
48538         }
48539
48540         if(!tpls.header){
48541             tpls.header = new Roo.Template(
48542                '<table border="0" cellspacing="0" cellpadding="0">',
48543                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48544                "</table>{splits}"
48545             );
48546             tpls.header.disableformats = true;
48547         }
48548         tpls.header.compile();
48549
48550         if(!tpls.hcell){
48551             tpls.hcell = new Roo.Template(
48552                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48553                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48554                 "</div></td>"
48555              );
48556              tpls.hcell.disableFormats = true;
48557         }
48558         tpls.hcell.compile();
48559
48560         if(!tpls.hsplit){
48561             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48562             tpls.hsplit.disableFormats = true;
48563         }
48564         tpls.hsplit.compile();
48565
48566         if(!tpls.body){
48567             tpls.body = new Roo.Template(
48568                '<table border="0" cellspacing="0" cellpadding="0">',
48569                "<tbody>{rows}</tbody>",
48570                "</table>"
48571             );
48572             tpls.body.disableFormats = true;
48573         }
48574         tpls.body.compile();
48575
48576         if(!tpls.row){
48577             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48578             tpls.row.disableFormats = true;
48579         }
48580         tpls.row.compile();
48581
48582         if(!tpls.cell){
48583             tpls.cell = new Roo.Template(
48584                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48585                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48586                 "</td>"
48587             );
48588             tpls.cell.disableFormats = true;
48589         }
48590         tpls.cell.compile();
48591
48592         this.templates = tpls;
48593     },
48594
48595     // remap these for backwards compat
48596     onColWidthChange : function(){
48597         this.updateColumns.apply(this, arguments);
48598     },
48599     onHeaderChange : function(){
48600         this.updateHeaders.apply(this, arguments);
48601     }, 
48602     onHiddenChange : function(){
48603         this.handleHiddenChange.apply(this, arguments);
48604     },
48605     onColumnMove : function(){
48606         this.handleColumnMove.apply(this, arguments);
48607     },
48608     onColumnLock : function(){
48609         this.handleLockChange.apply(this, arguments);
48610     },
48611
48612     onDataChange : function(){
48613         this.refresh();
48614         this.updateHeaderSortState();
48615     },
48616
48617     onClear : function(){
48618         this.refresh();
48619     },
48620
48621     onUpdate : function(ds, record){
48622         this.refreshRow(record);
48623     },
48624
48625     refreshRow : function(record){
48626         var ds = this.ds, index;
48627         if(typeof record == 'number'){
48628             index = record;
48629             record = ds.getAt(index);
48630         }else{
48631             index = ds.indexOf(record);
48632         }
48633         this.insertRows(ds, index, index, true);
48634         this.onRemove(ds, record, index+1, true);
48635         this.syncRowHeights(index, index);
48636         this.layout();
48637         this.fireEvent("rowupdated", this, index, record);
48638     },
48639
48640     onAdd : function(ds, records, index){
48641         this.insertRows(ds, index, index + (records.length-1));
48642     },
48643
48644     onRemove : function(ds, record, index, isUpdate){
48645         if(isUpdate !== true){
48646             this.fireEvent("beforerowremoved", this, index, record);
48647         }
48648         var bt = this.getBodyTable(), lt = this.getLockedTable();
48649         if(bt.rows[index]){
48650             bt.firstChild.removeChild(bt.rows[index]);
48651         }
48652         if(lt.rows[index]){
48653             lt.firstChild.removeChild(lt.rows[index]);
48654         }
48655         if(isUpdate !== true){
48656             this.stripeRows(index);
48657             this.syncRowHeights(index, index);
48658             this.layout();
48659             this.fireEvent("rowremoved", this, index, record);
48660         }
48661     },
48662
48663     onLoad : function(){
48664         this.scrollToTop();
48665     },
48666
48667     /**
48668      * Scrolls the grid to the top
48669      */
48670     scrollToTop : function(){
48671         if(this.scroller){
48672             this.scroller.dom.scrollTop = 0;
48673             this.syncScroll();
48674         }
48675     },
48676
48677     /**
48678      * Gets a panel in the header of the grid that can be used for toolbars etc.
48679      * After modifying the contents of this panel a call to grid.autoSize() may be
48680      * required to register any changes in size.
48681      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48682      * @return Roo.Element
48683      */
48684     getHeaderPanel : function(doShow){
48685         if(doShow){
48686             this.headerPanel.show();
48687         }
48688         return this.headerPanel;
48689     },
48690
48691     /**
48692      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48693      * After modifying the contents of this panel a call to grid.autoSize() may be
48694      * required to register any changes in size.
48695      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48696      * @return Roo.Element
48697      */
48698     getFooterPanel : function(doShow){
48699         if(doShow){
48700             this.footerPanel.show();
48701         }
48702         return this.footerPanel;
48703     },
48704
48705     initElements : function(){
48706         var E = Roo.Element;
48707         var el = this.grid.getGridEl().dom.firstChild;
48708         var cs = el.childNodes;
48709
48710         this.el = new E(el);
48711         
48712          this.focusEl = new E(el.firstChild);
48713         this.focusEl.swallowEvent("click", true);
48714         
48715         this.headerPanel = new E(cs[1]);
48716         this.headerPanel.enableDisplayMode("block");
48717
48718         this.scroller = new E(cs[2]);
48719         this.scrollSizer = new E(this.scroller.dom.firstChild);
48720
48721         this.lockedWrap = new E(cs[3]);
48722         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48723         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48724
48725         this.mainWrap = new E(cs[4]);
48726         this.mainHd = new E(this.mainWrap.dom.firstChild);
48727         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48728
48729         this.footerPanel = new E(cs[5]);
48730         this.footerPanel.enableDisplayMode("block");
48731
48732         this.resizeProxy = new E(cs[6]);
48733
48734         this.headerSelector = String.format(
48735            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48736            this.lockedHd.id, this.mainHd.id
48737         );
48738
48739         this.splitterSelector = String.format(
48740            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48741            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48742         );
48743     },
48744     idToCssName : function(s)
48745     {
48746         return s.replace(/[^a-z0-9]+/ig, '-');
48747     },
48748
48749     getHeaderCell : function(index){
48750         return Roo.DomQuery.select(this.headerSelector)[index];
48751     },
48752
48753     getHeaderCellMeasure : function(index){
48754         return this.getHeaderCell(index).firstChild;
48755     },
48756
48757     getHeaderCellText : function(index){
48758         return this.getHeaderCell(index).firstChild.firstChild;
48759     },
48760
48761     getLockedTable : function(){
48762         return this.lockedBody.dom.firstChild;
48763     },
48764
48765     getBodyTable : function(){
48766         return this.mainBody.dom.firstChild;
48767     },
48768
48769     getLockedRow : function(index){
48770         return this.getLockedTable().rows[index];
48771     },
48772
48773     getRow : function(index){
48774         return this.getBodyTable().rows[index];
48775     },
48776
48777     getRowComposite : function(index){
48778         if(!this.rowEl){
48779             this.rowEl = new Roo.CompositeElementLite();
48780         }
48781         var els = [], lrow, mrow;
48782         if(lrow = this.getLockedRow(index)){
48783             els.push(lrow);
48784         }
48785         if(mrow = this.getRow(index)){
48786             els.push(mrow);
48787         }
48788         this.rowEl.elements = els;
48789         return this.rowEl;
48790     },
48791     /**
48792      * Gets the 'td' of the cell
48793      * 
48794      * @param {Integer} rowIndex row to select
48795      * @param {Integer} colIndex column to select
48796      * 
48797      * @return {Object} 
48798      */
48799     getCell : function(rowIndex, colIndex){
48800         var locked = this.cm.getLockedCount();
48801         var source;
48802         if(colIndex < locked){
48803             source = this.lockedBody.dom.firstChild;
48804         }else{
48805             source = this.mainBody.dom.firstChild;
48806             colIndex -= locked;
48807         }
48808         return source.rows[rowIndex].childNodes[colIndex];
48809     },
48810
48811     getCellText : function(rowIndex, colIndex){
48812         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48813     },
48814
48815     getCellBox : function(cell){
48816         var b = this.fly(cell).getBox();
48817         if(Roo.isOpera){ // opera fails to report the Y
48818             b.y = cell.offsetTop + this.mainBody.getY();
48819         }
48820         return b;
48821     },
48822
48823     getCellIndex : function(cell){
48824         var id = String(cell.className).match(this.cellRE);
48825         if(id){
48826             return parseInt(id[1], 10);
48827         }
48828         return 0;
48829     },
48830
48831     findHeaderIndex : function(n){
48832         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48833         return r ? this.getCellIndex(r) : false;
48834     },
48835
48836     findHeaderCell : function(n){
48837         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48838         return r ? r : false;
48839     },
48840
48841     findRowIndex : function(n){
48842         if(!n){
48843             return false;
48844         }
48845         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48846         return r ? r.rowIndex : false;
48847     },
48848
48849     findCellIndex : function(node){
48850         var stop = this.el.dom;
48851         while(node && node != stop){
48852             if(this.findRE.test(node.className)){
48853                 return this.getCellIndex(node);
48854             }
48855             node = node.parentNode;
48856         }
48857         return false;
48858     },
48859
48860     getColumnId : function(index){
48861         return this.cm.getColumnId(index);
48862     },
48863
48864     getSplitters : function()
48865     {
48866         if(this.splitterSelector){
48867            return Roo.DomQuery.select(this.splitterSelector);
48868         }else{
48869             return null;
48870       }
48871     },
48872
48873     getSplitter : function(index){
48874         return this.getSplitters()[index];
48875     },
48876
48877     onRowOver : function(e, t){
48878         var row;
48879         if((row = this.findRowIndex(t)) !== false){
48880             this.getRowComposite(row).addClass("x-grid-row-over");
48881         }
48882     },
48883
48884     onRowOut : function(e, t){
48885         var row;
48886         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48887             this.getRowComposite(row).removeClass("x-grid-row-over");
48888         }
48889     },
48890
48891     renderHeaders : function(){
48892         var cm = this.cm;
48893         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48894         var cb = [], lb = [], sb = [], lsb = [], p = {};
48895         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48896             p.cellId = "x-grid-hd-0-" + i;
48897             p.splitId = "x-grid-csplit-0-" + i;
48898             p.id = cm.getColumnId(i);
48899             p.title = cm.getColumnTooltip(i) || "";
48900             p.value = cm.getColumnHeader(i) || "";
48901             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48902             if(!cm.isLocked(i)){
48903                 cb[cb.length] = ct.apply(p);
48904                 sb[sb.length] = st.apply(p);
48905             }else{
48906                 lb[lb.length] = ct.apply(p);
48907                 lsb[lsb.length] = st.apply(p);
48908             }
48909         }
48910         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48911                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48912     },
48913
48914     updateHeaders : function(){
48915         var html = this.renderHeaders();
48916         this.lockedHd.update(html[0]);
48917         this.mainHd.update(html[1]);
48918     },
48919
48920     /**
48921      * Focuses the specified row.
48922      * @param {Number} row The row index
48923      */
48924     focusRow : function(row)
48925     {
48926         //Roo.log('GridView.focusRow');
48927         var x = this.scroller.dom.scrollLeft;
48928         this.focusCell(row, 0, false);
48929         this.scroller.dom.scrollLeft = x;
48930     },
48931
48932     /**
48933      * Focuses the specified cell.
48934      * @param {Number} row The row index
48935      * @param {Number} col The column index
48936      * @param {Boolean} hscroll false to disable horizontal scrolling
48937      */
48938     focusCell : function(row, col, hscroll)
48939     {
48940         //Roo.log('GridView.focusCell');
48941         var el = this.ensureVisible(row, col, hscroll);
48942         this.focusEl.alignTo(el, "tl-tl");
48943         if(Roo.isGecko){
48944             this.focusEl.focus();
48945         }else{
48946             this.focusEl.focus.defer(1, this.focusEl);
48947         }
48948     },
48949
48950     /**
48951      * Scrolls the specified cell into view
48952      * @param {Number} row The row index
48953      * @param {Number} col The column index
48954      * @param {Boolean} hscroll false to disable horizontal scrolling
48955      */
48956     ensureVisible : function(row, col, hscroll)
48957     {
48958         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48959         //return null; //disable for testing.
48960         if(typeof row != "number"){
48961             row = row.rowIndex;
48962         }
48963         if(row < 0 && row >= this.ds.getCount()){
48964             return  null;
48965         }
48966         col = (col !== undefined ? col : 0);
48967         var cm = this.grid.colModel;
48968         while(cm.isHidden(col)){
48969             col++;
48970         }
48971
48972         var el = this.getCell(row, col);
48973         if(!el){
48974             return null;
48975         }
48976         var c = this.scroller.dom;
48977
48978         var ctop = parseInt(el.offsetTop, 10);
48979         var cleft = parseInt(el.offsetLeft, 10);
48980         var cbot = ctop + el.offsetHeight;
48981         var cright = cleft + el.offsetWidth;
48982         
48983         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48984         var stop = parseInt(c.scrollTop, 10);
48985         var sleft = parseInt(c.scrollLeft, 10);
48986         var sbot = stop + ch;
48987         var sright = sleft + c.clientWidth;
48988         /*
48989         Roo.log('GridView.ensureVisible:' +
48990                 ' ctop:' + ctop +
48991                 ' c.clientHeight:' + c.clientHeight +
48992                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48993                 ' stop:' + stop +
48994                 ' cbot:' + cbot +
48995                 ' sbot:' + sbot +
48996                 ' ch:' + ch  
48997                 );
48998         */
48999         if(ctop < stop){
49000              c.scrollTop = ctop;
49001             //Roo.log("set scrolltop to ctop DISABLE?");
49002         }else if(cbot > sbot){
49003             //Roo.log("set scrolltop to cbot-ch");
49004             c.scrollTop = cbot-ch;
49005         }
49006         
49007         if(hscroll !== false){
49008             if(cleft < sleft){
49009                 c.scrollLeft = cleft;
49010             }else if(cright > sright){
49011                 c.scrollLeft = cright-c.clientWidth;
49012             }
49013         }
49014          
49015         return el;
49016     },
49017
49018     updateColumns : function(){
49019         this.grid.stopEditing();
49020         var cm = this.grid.colModel, colIds = this.getColumnIds();
49021         //var totalWidth = cm.getTotalWidth();
49022         var pos = 0;
49023         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49024             //if(cm.isHidden(i)) continue;
49025             var w = cm.getColumnWidth(i);
49026             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49027             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49028         }
49029         this.updateSplitters();
49030     },
49031
49032     generateRules : function(cm){
49033         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49034         Roo.util.CSS.removeStyleSheet(rulesId);
49035         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49036             var cid = cm.getColumnId(i);
49037             var align = '';
49038             if(cm.config[i].align){
49039                 align = 'text-align:'+cm.config[i].align+';';
49040             }
49041             var hidden = '';
49042             if(cm.isHidden(i)){
49043                 hidden = 'display:none;';
49044             }
49045             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49046             ruleBuf.push(
49047                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49048                     this.hdSelector, cid, " {\n", align, width, "}\n",
49049                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49050                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49051         }
49052         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49053     },
49054
49055     updateSplitters : function(){
49056         var cm = this.cm, s = this.getSplitters();
49057         if(s){ // splitters not created yet
49058             var pos = 0, locked = true;
49059             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49060                 if(cm.isHidden(i)) continue;
49061                 var w = cm.getColumnWidth(i); // make sure it's a number
49062                 if(!cm.isLocked(i) && locked){
49063                     pos = 0;
49064                     locked = false;
49065                 }
49066                 pos += w;
49067                 s[i].style.left = (pos-this.splitOffset) + "px";
49068             }
49069         }
49070     },
49071
49072     handleHiddenChange : function(colModel, colIndex, hidden){
49073         if(hidden){
49074             this.hideColumn(colIndex);
49075         }else{
49076             this.unhideColumn(colIndex);
49077         }
49078     },
49079
49080     hideColumn : function(colIndex){
49081         var cid = this.getColumnId(colIndex);
49082         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49083         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49084         if(Roo.isSafari){
49085             this.updateHeaders();
49086         }
49087         this.updateSplitters();
49088         this.layout();
49089     },
49090
49091     unhideColumn : function(colIndex){
49092         var cid = this.getColumnId(colIndex);
49093         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49094         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49095
49096         if(Roo.isSafari){
49097             this.updateHeaders();
49098         }
49099         this.updateSplitters();
49100         this.layout();
49101     },
49102
49103     insertRows : function(dm, firstRow, lastRow, isUpdate){
49104         if(firstRow == 0 && lastRow == dm.getCount()-1){
49105             this.refresh();
49106         }else{
49107             if(!isUpdate){
49108                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49109             }
49110             var s = this.getScrollState();
49111             var markup = this.renderRows(firstRow, lastRow);
49112             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49113             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49114             this.restoreScroll(s);
49115             if(!isUpdate){
49116                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49117                 this.syncRowHeights(firstRow, lastRow);
49118                 this.stripeRows(firstRow);
49119                 this.layout();
49120             }
49121         }
49122     },
49123
49124     bufferRows : function(markup, target, index){
49125         var before = null, trows = target.rows, tbody = target.tBodies[0];
49126         if(index < trows.length){
49127             before = trows[index];
49128         }
49129         var b = document.createElement("div");
49130         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49131         var rows = b.firstChild.rows;
49132         for(var i = 0, len = rows.length; i < len; i++){
49133             if(before){
49134                 tbody.insertBefore(rows[0], before);
49135             }else{
49136                 tbody.appendChild(rows[0]);
49137             }
49138         }
49139         b.innerHTML = "";
49140         b = null;
49141     },
49142
49143     deleteRows : function(dm, firstRow, lastRow){
49144         if(dm.getRowCount()<1){
49145             this.fireEvent("beforerefresh", this);
49146             this.mainBody.update("");
49147             this.lockedBody.update("");
49148             this.fireEvent("refresh", this);
49149         }else{
49150             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49151             var bt = this.getBodyTable();
49152             var tbody = bt.firstChild;
49153             var rows = bt.rows;
49154             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49155                 tbody.removeChild(rows[firstRow]);
49156             }
49157             this.stripeRows(firstRow);
49158             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49159         }
49160     },
49161
49162     updateRows : function(dataSource, firstRow, lastRow){
49163         var s = this.getScrollState();
49164         this.refresh();
49165         this.restoreScroll(s);
49166     },
49167
49168     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49169         if(!noRefresh){
49170            this.refresh();
49171         }
49172         this.updateHeaderSortState();
49173     },
49174
49175     getScrollState : function(){
49176         
49177         var sb = this.scroller.dom;
49178         return {left: sb.scrollLeft, top: sb.scrollTop};
49179     },
49180
49181     stripeRows : function(startRow){
49182         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49183             return;
49184         }
49185         startRow = startRow || 0;
49186         var rows = this.getBodyTable().rows;
49187         var lrows = this.getLockedTable().rows;
49188         var cls = ' x-grid-row-alt ';
49189         for(var i = startRow, len = rows.length; i < len; i++){
49190             var row = rows[i], lrow = lrows[i];
49191             var isAlt = ((i+1) % 2 == 0);
49192             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49193             if(isAlt == hasAlt){
49194                 continue;
49195             }
49196             if(isAlt){
49197                 row.className += " x-grid-row-alt";
49198             }else{
49199                 row.className = row.className.replace("x-grid-row-alt", "");
49200             }
49201             if(lrow){
49202                 lrow.className = row.className;
49203             }
49204         }
49205     },
49206
49207     restoreScroll : function(state){
49208         //Roo.log('GridView.restoreScroll');
49209         var sb = this.scroller.dom;
49210         sb.scrollLeft = state.left;
49211         sb.scrollTop = state.top;
49212         this.syncScroll();
49213     },
49214
49215     syncScroll : function(){
49216         //Roo.log('GridView.syncScroll');
49217         var sb = this.scroller.dom;
49218         var sh = this.mainHd.dom;
49219         var bs = this.mainBody.dom;
49220         var lv = this.lockedBody.dom;
49221         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49222         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49223     },
49224
49225     handleScroll : function(e){
49226         this.syncScroll();
49227         var sb = this.scroller.dom;
49228         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49229         e.stopEvent();
49230     },
49231
49232     handleWheel : function(e){
49233         var d = e.getWheelDelta();
49234         this.scroller.dom.scrollTop -= d*22;
49235         // set this here to prevent jumpy scrolling on large tables
49236         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49237         e.stopEvent();
49238     },
49239
49240     renderRows : function(startRow, endRow){
49241         // pull in all the crap needed to render rows
49242         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49243         var colCount = cm.getColumnCount();
49244
49245         if(ds.getCount() < 1){
49246             return ["", ""];
49247         }
49248
49249         // build a map for all the columns
49250         var cs = [];
49251         for(var i = 0; i < colCount; i++){
49252             var name = cm.getDataIndex(i);
49253             cs[i] = {
49254                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49255                 renderer : cm.getRenderer(i),
49256                 id : cm.getColumnId(i),
49257                 locked : cm.isLocked(i)
49258             };
49259         }
49260
49261         startRow = startRow || 0;
49262         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49263
49264         // records to render
49265         var rs = ds.getRange(startRow, endRow);
49266
49267         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49268     },
49269
49270     // As much as I hate to duplicate code, this was branched because FireFox really hates
49271     // [].join("") on strings. The performance difference was substantial enough to
49272     // branch this function
49273     doRender : Roo.isGecko ?
49274             function(cs, rs, ds, startRow, colCount, stripe){
49275                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49276                 // buffers
49277                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49278                 
49279                 var hasListener = this.grid.hasListener('rowclass');
49280                 var rowcfg = {};
49281                 for(var j = 0, len = rs.length; j < len; j++){
49282                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49283                     for(var i = 0; i < colCount; i++){
49284                         c = cs[i];
49285                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49286                         p.id = c.id;
49287                         p.css = p.attr = "";
49288                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49289                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49290                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49291                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49292                         }
49293                         var markup = ct.apply(p);
49294                         if(!c.locked){
49295                             cb+= markup;
49296                         }else{
49297                             lcb+= markup;
49298                         }
49299                     }
49300                     var alt = [];
49301                     if(stripe && ((rowIndex+1) % 2 == 0)){
49302                         alt.push("x-grid-row-alt")
49303                     }
49304                     if(r.dirty){
49305                         alt.push(  " x-grid-dirty-row");
49306                     }
49307                     rp.cells = lcb;
49308                     if(this.getRowClass){
49309                         alt.push(this.getRowClass(r, rowIndex));
49310                     }
49311                     if (hasListener) {
49312                         rowcfg = {
49313                              
49314                             record: r,
49315                             rowIndex : rowIndex,
49316                             rowClass : ''
49317                         }
49318                         this.grid.fireEvent('rowclass', this, rowcfg);
49319                         alt.push(rowcfg.rowClass);
49320                     }
49321                     rp.alt = alt.join(" ");
49322                     lbuf+= rt.apply(rp);
49323                     rp.cells = cb;
49324                     buf+=  rt.apply(rp);
49325                 }
49326                 return [lbuf, buf];
49327             } :
49328             function(cs, rs, ds, startRow, colCount, stripe){
49329                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49330                 // buffers
49331                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49332                 var hasListener = this.grid.hasListener('rowclass');
49333  
49334                 var rowcfg = {};
49335                 for(var j = 0, len = rs.length; j < len; j++){
49336                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49337                     for(var i = 0; i < colCount; i++){
49338                         c = cs[i];
49339                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49340                         p.id = c.id;
49341                         p.css = p.attr = "";
49342                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49343                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49344                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49345                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49346                         }
49347                         
49348                         var markup = ct.apply(p);
49349                         if(!c.locked){
49350                             cb[cb.length] = markup;
49351                         }else{
49352                             lcb[lcb.length] = markup;
49353                         }
49354                     }
49355                     var alt = [];
49356                     if(stripe && ((rowIndex+1) % 2 == 0)){
49357                         alt.push( "x-grid-row-alt");
49358                     }
49359                     if(r.dirty){
49360                         alt.push(" x-grid-dirty-row");
49361                     }
49362                     rp.cells = lcb;
49363                     if(this.getRowClass){
49364                         alt.push( this.getRowClass(r, rowIndex));
49365                     }
49366                     if (hasListener) {
49367                         rowcfg = {
49368                              
49369                             record: r,
49370                             rowIndex : rowIndex,
49371                             rowClass : ''
49372                         }
49373                         this.grid.fireEvent('rowclass', this, rowcfg);
49374                         alt.push(rowcfg.rowClass);
49375                     }
49376                     rp.alt = alt.join(" ");
49377                     rp.cells = lcb.join("");
49378                     lbuf[lbuf.length] = rt.apply(rp);
49379                     rp.cells = cb.join("");
49380                     buf[buf.length] =  rt.apply(rp);
49381                 }
49382                 return [lbuf.join(""), buf.join("")];
49383             },
49384
49385     renderBody : function(){
49386         var markup = this.renderRows();
49387         var bt = this.templates.body;
49388         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49389     },
49390
49391     /**
49392      * Refreshes the grid
49393      * @param {Boolean} headersToo
49394      */
49395     refresh : function(headersToo){
49396         this.fireEvent("beforerefresh", this);
49397         this.grid.stopEditing();
49398         var result = this.renderBody();
49399         this.lockedBody.update(result[0]);
49400         this.mainBody.update(result[1]);
49401         if(headersToo === true){
49402             this.updateHeaders();
49403             this.updateColumns();
49404             this.updateSplitters();
49405             this.updateHeaderSortState();
49406         }
49407         this.syncRowHeights();
49408         this.layout();
49409         this.fireEvent("refresh", this);
49410     },
49411
49412     handleColumnMove : function(cm, oldIndex, newIndex){
49413         this.indexMap = null;
49414         var s = this.getScrollState();
49415         this.refresh(true);
49416         this.restoreScroll(s);
49417         this.afterMove(newIndex);
49418     },
49419
49420     afterMove : function(colIndex){
49421         if(this.enableMoveAnim && Roo.enableFx){
49422             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49423         }
49424         // if multisort - fix sortOrder, and reload..
49425         if (this.grid.dataSource.multiSort) {
49426             // the we can call sort again..
49427             var dm = this.grid.dataSource;
49428             var cm = this.grid.colModel;
49429             var so = [];
49430             for(var i = 0; i < cm.config.length; i++ ) {
49431                 
49432                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49433                     continue; // dont' bother, it's not in sort list or being set.
49434                 }
49435                 
49436                 so.push(cm.config[i].dataIndex);
49437             };
49438             dm.sortOrder = so;
49439             dm.load(dm.lastOptions);
49440             
49441             
49442         }
49443         
49444     },
49445
49446     updateCell : function(dm, rowIndex, dataIndex){
49447         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49448         if(typeof colIndex == "undefined"){ // not present in grid
49449             return;
49450         }
49451         var cm = this.grid.colModel;
49452         var cell = this.getCell(rowIndex, colIndex);
49453         var cellText = this.getCellText(rowIndex, colIndex);
49454
49455         var p = {
49456             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49457             id : cm.getColumnId(colIndex),
49458             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49459         };
49460         var renderer = cm.getRenderer(colIndex);
49461         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49462         if(typeof val == "undefined" || val === "") val = "&#160;";
49463         cellText.innerHTML = val;
49464         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49465         this.syncRowHeights(rowIndex, rowIndex);
49466     },
49467
49468     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49469         var maxWidth = 0;
49470         if(this.grid.autoSizeHeaders){
49471             var h = this.getHeaderCellMeasure(colIndex);
49472             maxWidth = Math.max(maxWidth, h.scrollWidth);
49473         }
49474         var tb, index;
49475         if(this.cm.isLocked(colIndex)){
49476             tb = this.getLockedTable();
49477             index = colIndex;
49478         }else{
49479             tb = this.getBodyTable();
49480             index = colIndex - this.cm.getLockedCount();
49481         }
49482         if(tb && tb.rows){
49483             var rows = tb.rows;
49484             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49485             for(var i = 0; i < stopIndex; i++){
49486                 var cell = rows[i].childNodes[index].firstChild;
49487                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49488             }
49489         }
49490         return maxWidth + /*margin for error in IE*/ 5;
49491     },
49492     /**
49493      * Autofit a column to its content.
49494      * @param {Number} colIndex
49495      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49496      */
49497      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49498          if(this.cm.isHidden(colIndex)){
49499              return; // can't calc a hidden column
49500          }
49501         if(forceMinSize){
49502             var cid = this.cm.getColumnId(colIndex);
49503             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49504            if(this.grid.autoSizeHeaders){
49505                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49506            }
49507         }
49508         var newWidth = this.calcColumnWidth(colIndex);
49509         this.cm.setColumnWidth(colIndex,
49510             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49511         if(!suppressEvent){
49512             this.grid.fireEvent("columnresize", colIndex, newWidth);
49513         }
49514     },
49515
49516     /**
49517      * Autofits all columns to their content and then expands to fit any extra space in the grid
49518      */
49519      autoSizeColumns : function(){
49520         var cm = this.grid.colModel;
49521         var colCount = cm.getColumnCount();
49522         for(var i = 0; i < colCount; i++){
49523             this.autoSizeColumn(i, true, true);
49524         }
49525         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49526             this.fitColumns();
49527         }else{
49528             this.updateColumns();
49529             this.layout();
49530         }
49531     },
49532
49533     /**
49534      * Autofits all columns to the grid's width proportionate with their current size
49535      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49536      */
49537     fitColumns : function(reserveScrollSpace){
49538         var cm = this.grid.colModel;
49539         var colCount = cm.getColumnCount();
49540         var cols = [];
49541         var width = 0;
49542         var i, w;
49543         for (i = 0; i < colCount; i++){
49544             if(!cm.isHidden(i) && !cm.isFixed(i)){
49545                 w = cm.getColumnWidth(i);
49546                 cols.push(i);
49547                 cols.push(w);
49548                 width += w;
49549             }
49550         }
49551         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49552         if(reserveScrollSpace){
49553             avail -= 17;
49554         }
49555         var frac = (avail - cm.getTotalWidth())/width;
49556         while (cols.length){
49557             w = cols.pop();
49558             i = cols.pop();
49559             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49560         }
49561         this.updateColumns();
49562         this.layout();
49563     },
49564
49565     onRowSelect : function(rowIndex){
49566         var row = this.getRowComposite(rowIndex);
49567         row.addClass("x-grid-row-selected");
49568     },
49569
49570     onRowDeselect : function(rowIndex){
49571         var row = this.getRowComposite(rowIndex);
49572         row.removeClass("x-grid-row-selected");
49573     },
49574
49575     onCellSelect : function(row, col){
49576         var cell = this.getCell(row, col);
49577         if(cell){
49578             Roo.fly(cell).addClass("x-grid-cell-selected");
49579         }
49580     },
49581
49582     onCellDeselect : function(row, col){
49583         var cell = this.getCell(row, col);
49584         if(cell){
49585             Roo.fly(cell).removeClass("x-grid-cell-selected");
49586         }
49587     },
49588
49589     updateHeaderSortState : function(){
49590         
49591         // sort state can be single { field: xxx, direction : yyy}
49592         // or   { xxx=>ASC , yyy : DESC ..... }
49593         
49594         var mstate = {};
49595         if (!this.ds.multiSort) { 
49596             var state = this.ds.getSortState();
49597             if(!state){
49598                 return;
49599             }
49600             mstate[state.field] = state.direction;
49601             // FIXME... - this is not used here.. but might be elsewhere..
49602             this.sortState = state;
49603             
49604         } else {
49605             mstate = this.ds.sortToggle;
49606         }
49607         //remove existing sort classes..
49608         
49609         var sc = this.sortClasses;
49610         var hds = this.el.select(this.headerSelector).removeClass(sc);
49611         
49612         for(var f in mstate) {
49613         
49614             var sortColumn = this.cm.findColumnIndex(f);
49615             
49616             if(sortColumn != -1){
49617                 var sortDir = mstate[f];        
49618                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49619             }
49620         }
49621         
49622          
49623         
49624     },
49625
49626
49627     handleHeaderClick : function(g, index){
49628         if(this.headersDisabled){
49629             return;
49630         }
49631         var dm = g.dataSource, cm = g.colModel;
49632         if(!cm.isSortable(index)){
49633             return;
49634         }
49635         g.stopEditing();
49636         
49637         if (dm.multiSort) {
49638             // update the sortOrder
49639             var so = [];
49640             for(var i = 0; i < cm.config.length; i++ ) {
49641                 
49642                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49643                     continue; // dont' bother, it's not in sort list or being set.
49644                 }
49645                 
49646                 so.push(cm.config[i].dataIndex);
49647             };
49648             dm.sortOrder = so;
49649         }
49650         
49651         
49652         dm.sort(cm.getDataIndex(index));
49653     },
49654
49655
49656     destroy : function(){
49657         if(this.colMenu){
49658             this.colMenu.removeAll();
49659             Roo.menu.MenuMgr.unregister(this.colMenu);
49660             this.colMenu.getEl().remove();
49661             delete this.colMenu;
49662         }
49663         if(this.hmenu){
49664             this.hmenu.removeAll();
49665             Roo.menu.MenuMgr.unregister(this.hmenu);
49666             this.hmenu.getEl().remove();
49667             delete this.hmenu;
49668         }
49669         if(this.grid.enableColumnMove){
49670             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49671             if(dds){
49672                 for(var dd in dds){
49673                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49674                         var elid = dds[dd].dragElId;
49675                         dds[dd].unreg();
49676                         Roo.get(elid).remove();
49677                     } else if(dds[dd].config.isTarget){
49678                         dds[dd].proxyTop.remove();
49679                         dds[dd].proxyBottom.remove();
49680                         dds[dd].unreg();
49681                     }
49682                     if(Roo.dd.DDM.locationCache[dd]){
49683                         delete Roo.dd.DDM.locationCache[dd];
49684                     }
49685                 }
49686                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49687             }
49688         }
49689         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49690         this.bind(null, null);
49691         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49692     },
49693
49694     handleLockChange : function(){
49695         this.refresh(true);
49696     },
49697
49698     onDenyColumnLock : function(){
49699
49700     },
49701
49702     onDenyColumnHide : function(){
49703
49704     },
49705
49706     handleHdMenuClick : function(item){
49707         var index = this.hdCtxIndex;
49708         var cm = this.cm, ds = this.ds;
49709         switch(item.id){
49710             case "asc":
49711                 ds.sort(cm.getDataIndex(index), "ASC");
49712                 break;
49713             case "desc":
49714                 ds.sort(cm.getDataIndex(index), "DESC");
49715                 break;
49716             case "lock":
49717                 var lc = cm.getLockedCount();
49718                 if(cm.getColumnCount(true) <= lc+1){
49719                     this.onDenyColumnLock();
49720                     return;
49721                 }
49722                 if(lc != index){
49723                     cm.setLocked(index, true, true);
49724                     cm.moveColumn(index, lc);
49725                     this.grid.fireEvent("columnmove", index, lc);
49726                 }else{
49727                     cm.setLocked(index, true);
49728                 }
49729             break;
49730             case "unlock":
49731                 var lc = cm.getLockedCount();
49732                 if((lc-1) != index){
49733                     cm.setLocked(index, false, true);
49734                     cm.moveColumn(index, lc-1);
49735                     this.grid.fireEvent("columnmove", index, lc-1);
49736                 }else{
49737                     cm.setLocked(index, false);
49738                 }
49739             break;
49740             default:
49741                 index = cm.getIndexById(item.id.substr(4));
49742                 if(index != -1){
49743                     if(item.checked && cm.getColumnCount(true) <= 1){
49744                         this.onDenyColumnHide();
49745                         return false;
49746                     }
49747                     cm.setHidden(index, item.checked);
49748                 }
49749         }
49750         return true;
49751     },
49752
49753     beforeColMenuShow : function(){
49754         var cm = this.cm,  colCount = cm.getColumnCount();
49755         this.colMenu.removeAll();
49756         for(var i = 0; i < colCount; i++){
49757             this.colMenu.add(new Roo.menu.CheckItem({
49758                 id: "col-"+cm.getColumnId(i),
49759                 text: cm.getColumnHeader(i),
49760                 checked: !cm.isHidden(i),
49761                 hideOnClick:false
49762             }));
49763         }
49764     },
49765
49766     handleHdCtx : function(g, index, e){
49767         e.stopEvent();
49768         var hd = this.getHeaderCell(index);
49769         this.hdCtxIndex = index;
49770         var ms = this.hmenu.items, cm = this.cm;
49771         ms.get("asc").setDisabled(!cm.isSortable(index));
49772         ms.get("desc").setDisabled(!cm.isSortable(index));
49773         if(this.grid.enableColLock !== false){
49774             ms.get("lock").setDisabled(cm.isLocked(index));
49775             ms.get("unlock").setDisabled(!cm.isLocked(index));
49776         }
49777         this.hmenu.show(hd, "tl-bl");
49778     },
49779
49780     handleHdOver : function(e){
49781         var hd = this.findHeaderCell(e.getTarget());
49782         if(hd && !this.headersDisabled){
49783             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49784                this.fly(hd).addClass("x-grid-hd-over");
49785             }
49786         }
49787     },
49788
49789     handleHdOut : function(e){
49790         var hd = this.findHeaderCell(e.getTarget());
49791         if(hd){
49792             this.fly(hd).removeClass("x-grid-hd-over");
49793         }
49794     },
49795
49796     handleSplitDblClick : function(e, t){
49797         var i = this.getCellIndex(t);
49798         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49799             this.autoSizeColumn(i, true);
49800             this.layout();
49801         }
49802     },
49803
49804     render : function(){
49805
49806         var cm = this.cm;
49807         var colCount = cm.getColumnCount();
49808
49809         if(this.grid.monitorWindowResize === true){
49810             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49811         }
49812         var header = this.renderHeaders();
49813         var body = this.templates.body.apply({rows:""});
49814         var html = this.templates.master.apply({
49815             lockedBody: body,
49816             body: body,
49817             lockedHeader: header[0],
49818             header: header[1]
49819         });
49820
49821         //this.updateColumns();
49822
49823         this.grid.getGridEl().dom.innerHTML = html;
49824
49825         this.initElements();
49826         
49827         // a kludge to fix the random scolling effect in webkit
49828         this.el.on("scroll", function() {
49829             this.el.dom.scrollTop=0; // hopefully not recursive..
49830         },this);
49831
49832         this.scroller.on("scroll", this.handleScroll, this);
49833         this.lockedBody.on("mousewheel", this.handleWheel, this);
49834         this.mainBody.on("mousewheel", this.handleWheel, this);
49835
49836         this.mainHd.on("mouseover", this.handleHdOver, this);
49837         this.mainHd.on("mouseout", this.handleHdOut, this);
49838         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49839                 {delegate: "."+this.splitClass});
49840
49841         this.lockedHd.on("mouseover", this.handleHdOver, this);
49842         this.lockedHd.on("mouseout", this.handleHdOut, this);
49843         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49844                 {delegate: "."+this.splitClass});
49845
49846         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49847             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49848         }
49849
49850         this.updateSplitters();
49851
49852         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49853             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49854             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49855         }
49856
49857         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49858             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49859             this.hmenu.add(
49860                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49861                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49862             );
49863             if(this.grid.enableColLock !== false){
49864                 this.hmenu.add('-',
49865                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49866                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49867                 );
49868             }
49869             if(this.grid.enableColumnHide !== false){
49870
49871                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49872                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49873                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49874
49875                 this.hmenu.add('-',
49876                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49877                 );
49878             }
49879             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49880
49881             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49882         }
49883
49884         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49885             this.dd = new Roo.grid.GridDragZone(this.grid, {
49886                 ddGroup : this.grid.ddGroup || 'GridDD'
49887             });
49888         }
49889
49890         /*
49891         for(var i = 0; i < colCount; i++){
49892             if(cm.isHidden(i)){
49893                 this.hideColumn(i);
49894             }
49895             if(cm.config[i].align){
49896                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49897                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49898             }
49899         }*/
49900         
49901         this.updateHeaderSortState();
49902
49903         this.beforeInitialResize();
49904         this.layout(true);
49905
49906         // two part rendering gives faster view to the user
49907         this.renderPhase2.defer(1, this);
49908     },
49909
49910     renderPhase2 : function(){
49911         // render the rows now
49912         this.refresh();
49913         if(this.grid.autoSizeColumns){
49914             this.autoSizeColumns();
49915         }
49916     },
49917
49918     beforeInitialResize : function(){
49919
49920     },
49921
49922     onColumnSplitterMoved : function(i, w){
49923         this.userResized = true;
49924         var cm = this.grid.colModel;
49925         cm.setColumnWidth(i, w, true);
49926         var cid = cm.getColumnId(i);
49927         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49928         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49929         this.updateSplitters();
49930         this.layout();
49931         this.grid.fireEvent("columnresize", i, w);
49932     },
49933
49934     syncRowHeights : function(startIndex, endIndex){
49935         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49936             startIndex = startIndex || 0;
49937             var mrows = this.getBodyTable().rows;
49938             var lrows = this.getLockedTable().rows;
49939             var len = mrows.length-1;
49940             endIndex = Math.min(endIndex || len, len);
49941             for(var i = startIndex; i <= endIndex; i++){
49942                 var m = mrows[i], l = lrows[i];
49943                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49944                 m.style.height = l.style.height = h + "px";
49945             }
49946         }
49947     },
49948
49949     layout : function(initialRender, is2ndPass){
49950         var g = this.grid;
49951         var auto = g.autoHeight;
49952         var scrollOffset = 16;
49953         var c = g.getGridEl(), cm = this.cm,
49954                 expandCol = g.autoExpandColumn,
49955                 gv = this;
49956         //c.beginMeasure();
49957
49958         if(!c.dom.offsetWidth){ // display:none?
49959             if(initialRender){
49960                 this.lockedWrap.show();
49961                 this.mainWrap.show();
49962             }
49963             return;
49964         }
49965
49966         var hasLock = this.cm.isLocked(0);
49967
49968         var tbh = this.headerPanel.getHeight();
49969         var bbh = this.footerPanel.getHeight();
49970
49971         if(auto){
49972             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49973             var newHeight = ch + c.getBorderWidth("tb");
49974             if(g.maxHeight){
49975                 newHeight = Math.min(g.maxHeight, newHeight);
49976             }
49977             c.setHeight(newHeight);
49978         }
49979
49980         if(g.autoWidth){
49981             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49982         }
49983
49984         var s = this.scroller;
49985
49986         var csize = c.getSize(true);
49987
49988         this.el.setSize(csize.width, csize.height);
49989
49990         this.headerPanel.setWidth(csize.width);
49991         this.footerPanel.setWidth(csize.width);
49992
49993         var hdHeight = this.mainHd.getHeight();
49994         var vw = csize.width;
49995         var vh = csize.height - (tbh + bbh);
49996
49997         s.setSize(vw, vh);
49998
49999         var bt = this.getBodyTable();
50000         var ltWidth = hasLock ?
50001                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50002
50003         var scrollHeight = bt.offsetHeight;
50004         var scrollWidth = ltWidth + bt.offsetWidth;
50005         var vscroll = false, hscroll = false;
50006
50007         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50008
50009         var lw = this.lockedWrap, mw = this.mainWrap;
50010         var lb = this.lockedBody, mb = this.mainBody;
50011
50012         setTimeout(function(){
50013             var t = s.dom.offsetTop;
50014             var w = s.dom.clientWidth,
50015                 h = s.dom.clientHeight;
50016
50017             lw.setTop(t);
50018             lw.setSize(ltWidth, h);
50019
50020             mw.setLeftTop(ltWidth, t);
50021             mw.setSize(w-ltWidth, h);
50022
50023             lb.setHeight(h-hdHeight);
50024             mb.setHeight(h-hdHeight);
50025
50026             if(is2ndPass !== true && !gv.userResized && expandCol){
50027                 // high speed resize without full column calculation
50028                 
50029                 var ci = cm.getIndexById(expandCol);
50030                 if (ci < 0) {
50031                     ci = cm.findColumnIndex(expandCol);
50032                 }
50033                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50034                 var expandId = cm.getColumnId(ci);
50035                 var  tw = cm.getTotalWidth(false);
50036                 var currentWidth = cm.getColumnWidth(ci);
50037                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50038                 if(currentWidth != cw){
50039                     cm.setColumnWidth(ci, cw, true);
50040                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50041                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50042                     gv.updateSplitters();
50043                     gv.layout(false, true);
50044                 }
50045             }
50046
50047             if(initialRender){
50048                 lw.show();
50049                 mw.show();
50050             }
50051             //c.endMeasure();
50052         }, 10);
50053     },
50054
50055     onWindowResize : function(){
50056         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50057             return;
50058         }
50059         this.layout();
50060     },
50061
50062     appendFooter : function(parentEl){
50063         return null;
50064     },
50065
50066     sortAscText : "Sort Ascending",
50067     sortDescText : "Sort Descending",
50068     lockText : "Lock Column",
50069     unlockText : "Unlock Column",
50070     columnsText : "Columns"
50071 });
50072
50073
50074 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50075     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50076     this.proxy.el.addClass('x-grid3-col-dd');
50077 };
50078
50079 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50080     handleMouseDown : function(e){
50081
50082     },
50083
50084     callHandleMouseDown : function(e){
50085         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50086     }
50087 });
50088 /*
50089  * Based on:
50090  * Ext JS Library 1.1.1
50091  * Copyright(c) 2006-2007, Ext JS, LLC.
50092  *
50093  * Originally Released Under LGPL - original licence link has changed is not relivant.
50094  *
50095  * Fork - LGPL
50096  * <script type="text/javascript">
50097  */
50098  
50099 // private
50100 // This is a support class used internally by the Grid components
50101 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50102     this.grid = grid;
50103     this.view = grid.getView();
50104     this.proxy = this.view.resizeProxy;
50105     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50106         "gridSplitters" + this.grid.getGridEl().id, {
50107         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50108     });
50109     this.setHandleElId(Roo.id(hd));
50110     this.setOuterHandleElId(Roo.id(hd2));
50111     this.scroll = false;
50112 };
50113 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50114     fly: Roo.Element.fly,
50115
50116     b4StartDrag : function(x, y){
50117         this.view.headersDisabled = true;
50118         this.proxy.setHeight(this.view.mainWrap.getHeight());
50119         var w = this.cm.getColumnWidth(this.cellIndex);
50120         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50121         this.resetConstraints();
50122         this.setXConstraint(minw, 1000);
50123         this.setYConstraint(0, 0);
50124         this.minX = x - minw;
50125         this.maxX = x + 1000;
50126         this.startPos = x;
50127         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50128     },
50129
50130
50131     handleMouseDown : function(e){
50132         ev = Roo.EventObject.setEvent(e);
50133         var t = this.fly(ev.getTarget());
50134         if(t.hasClass("x-grid-split")){
50135             this.cellIndex = this.view.getCellIndex(t.dom);
50136             this.split = t.dom;
50137             this.cm = this.grid.colModel;
50138             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50139                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50140             }
50141         }
50142     },
50143
50144     endDrag : function(e){
50145         this.view.headersDisabled = false;
50146         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50147         var diff = endX - this.startPos;
50148         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50149     },
50150
50151     autoOffset : function(){
50152         this.setDelta(0,0);
50153     }
50154 });/*
50155  * Based on:
50156  * Ext JS Library 1.1.1
50157  * Copyright(c) 2006-2007, Ext JS, LLC.
50158  *
50159  * Originally Released Under LGPL - original licence link has changed is not relivant.
50160  *
50161  * Fork - LGPL
50162  * <script type="text/javascript">
50163  */
50164  
50165 // private
50166 // This is a support class used internally by the Grid components
50167 Roo.grid.GridDragZone = function(grid, config){
50168     this.view = grid.getView();
50169     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50170     if(this.view.lockedBody){
50171         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50172         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50173     }
50174     this.scroll = false;
50175     this.grid = grid;
50176     this.ddel = document.createElement('div');
50177     this.ddel.className = 'x-grid-dd-wrap';
50178 };
50179
50180 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50181     ddGroup : "GridDD",
50182
50183     getDragData : function(e){
50184         var t = Roo.lib.Event.getTarget(e);
50185         var rowIndex = this.view.findRowIndex(t);
50186         if(rowIndex !== false){
50187             var sm = this.grid.selModel;
50188             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50189               //  sm.mouseDown(e, t);
50190             //}
50191             if (e.hasModifier()){
50192                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50193             }
50194             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50195         }
50196         return false;
50197     },
50198
50199     onInitDrag : function(e){
50200         var data = this.dragData;
50201         this.ddel.innerHTML = this.grid.getDragDropText();
50202         this.proxy.update(this.ddel);
50203         // fire start drag?
50204     },
50205
50206     afterRepair : function(){
50207         this.dragging = false;
50208     },
50209
50210     getRepairXY : function(e, data){
50211         return false;
50212     },
50213
50214     onEndDrag : function(data, e){
50215         // fire end drag?
50216     },
50217
50218     onValidDrop : function(dd, e, id){
50219         // fire drag drop?
50220         this.hideProxy();
50221     },
50222
50223     beforeInvalidDrop : function(e, id){
50224
50225     }
50226 });/*
50227  * Based on:
50228  * Ext JS Library 1.1.1
50229  * Copyright(c) 2006-2007, Ext JS, LLC.
50230  *
50231  * Originally Released Under LGPL - original licence link has changed is not relivant.
50232  *
50233  * Fork - LGPL
50234  * <script type="text/javascript">
50235  */
50236  
50237
50238 /**
50239  * @class Roo.grid.ColumnModel
50240  * @extends Roo.util.Observable
50241  * This is the default implementation of a ColumnModel used by the Grid. It defines
50242  * the columns in the grid.
50243  * <br>Usage:<br>
50244  <pre><code>
50245  var colModel = new Roo.grid.ColumnModel([
50246         {header: "Ticker", width: 60, sortable: true, locked: true},
50247         {header: "Company Name", width: 150, sortable: true},
50248         {header: "Market Cap.", width: 100, sortable: true},
50249         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50250         {header: "Employees", width: 100, sortable: true, resizable: false}
50251  ]);
50252  </code></pre>
50253  * <p>
50254  
50255  * The config options listed for this class are options which may appear in each
50256  * individual column definition.
50257  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50258  * @constructor
50259  * @param {Object} config An Array of column config objects. See this class's
50260  * config objects for details.
50261 */
50262 Roo.grid.ColumnModel = function(config){
50263         /**
50264      * The config passed into the constructor
50265      */
50266     this.config = config;
50267     this.lookup = {};
50268
50269     // if no id, create one
50270     // if the column does not have a dataIndex mapping,
50271     // map it to the order it is in the config
50272     for(var i = 0, len = config.length; i < len; i++){
50273         var c = config[i];
50274         if(typeof c.dataIndex == "undefined"){
50275             c.dataIndex = i;
50276         }
50277         if(typeof c.renderer == "string"){
50278             c.renderer = Roo.util.Format[c.renderer];
50279         }
50280         if(typeof c.id == "undefined"){
50281             c.id = Roo.id();
50282         }
50283         if(c.editor && c.editor.xtype){
50284             c.editor  = Roo.factory(c.editor, Roo.grid);
50285         }
50286         if(c.editor && c.editor.isFormField){
50287             c.editor = new Roo.grid.GridEditor(c.editor);
50288         }
50289         this.lookup[c.id] = c;
50290     }
50291
50292     /**
50293      * The width of columns which have no width specified (defaults to 100)
50294      * @type Number
50295      */
50296     this.defaultWidth = 100;
50297
50298     /**
50299      * Default sortable of columns which have no sortable specified (defaults to false)
50300      * @type Boolean
50301      */
50302     this.defaultSortable = false;
50303
50304     this.addEvents({
50305         /**
50306              * @event widthchange
50307              * Fires when the width of a column changes.
50308              * @param {ColumnModel} this
50309              * @param {Number} columnIndex The column index
50310              * @param {Number} newWidth The new width
50311              */
50312             "widthchange": true,
50313         /**
50314              * @event headerchange
50315              * Fires when the text of a header changes.
50316              * @param {ColumnModel} this
50317              * @param {Number} columnIndex The column index
50318              * @param {Number} newText The new header text
50319              */
50320             "headerchange": true,
50321         /**
50322              * @event hiddenchange
50323              * Fires when a column is hidden or "unhidden".
50324              * @param {ColumnModel} this
50325              * @param {Number} columnIndex The column index
50326              * @param {Boolean} hidden true if hidden, false otherwise
50327              */
50328             "hiddenchange": true,
50329             /**
50330          * @event columnmoved
50331          * Fires when a column is moved.
50332          * @param {ColumnModel} this
50333          * @param {Number} oldIndex
50334          * @param {Number} newIndex
50335          */
50336         "columnmoved" : true,
50337         /**
50338          * @event columlockchange
50339          * Fires when a column's locked state is changed
50340          * @param {ColumnModel} this
50341          * @param {Number} colIndex
50342          * @param {Boolean} locked true if locked
50343          */
50344         "columnlockchange" : true
50345     });
50346     Roo.grid.ColumnModel.superclass.constructor.call(this);
50347 };
50348 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50349     /**
50350      * @cfg {String} header The header text to display in the Grid view.
50351      */
50352     /**
50353      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50354      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50355      * specified, the column's index is used as an index into the Record's data Array.
50356      */
50357     /**
50358      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50359      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50360      */
50361     /**
50362      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50363      * Defaults to the value of the {@link #defaultSortable} property.
50364      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50365      */
50366     /**
50367      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50368      */
50369     /**
50370      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50371      */
50372     /**
50373      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50374      */
50375     /**
50376      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50377      */
50378     /**
50379      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50380      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50381      * default renderer uses the raw data value.
50382      */
50383        /**
50384      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50385      */
50386     /**
50387      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50388      */
50389
50390     /**
50391      * Returns the id of the column at the specified index.
50392      * @param {Number} index The column index
50393      * @return {String} the id
50394      */
50395     getColumnId : function(index){
50396         return this.config[index].id;
50397     },
50398
50399     /**
50400      * Returns the column for a specified id.
50401      * @param {String} id The column id
50402      * @return {Object} the column
50403      */
50404     getColumnById : function(id){
50405         return this.lookup[id];
50406     },
50407
50408     
50409     /**
50410      * Returns the column for a specified dataIndex.
50411      * @param {String} dataIndex The column dataIndex
50412      * @return {Object|Boolean} the column or false if not found
50413      */
50414     getColumnByDataIndex: function(dataIndex){
50415         var index = this.findColumnIndex(dataIndex);
50416         return index > -1 ? this.config[index] : false;
50417     },
50418     
50419     /**
50420      * Returns the index for a specified column id.
50421      * @param {String} id The column id
50422      * @return {Number} the index, or -1 if not found
50423      */
50424     getIndexById : function(id){
50425         for(var i = 0, len = this.config.length; i < len; i++){
50426             if(this.config[i].id == id){
50427                 return i;
50428             }
50429         }
50430         return -1;
50431     },
50432     
50433     /**
50434      * Returns the index for a specified column dataIndex.
50435      * @param {String} dataIndex The column dataIndex
50436      * @return {Number} the index, or -1 if not found
50437      */
50438     
50439     findColumnIndex : function(dataIndex){
50440         for(var i = 0, len = this.config.length; i < len; i++){
50441             if(this.config[i].dataIndex == dataIndex){
50442                 return i;
50443             }
50444         }
50445         return -1;
50446     },
50447     
50448     
50449     moveColumn : function(oldIndex, newIndex){
50450         var c = this.config[oldIndex];
50451         this.config.splice(oldIndex, 1);
50452         this.config.splice(newIndex, 0, c);
50453         this.dataMap = null;
50454         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50455     },
50456
50457     isLocked : function(colIndex){
50458         return this.config[colIndex].locked === true;
50459     },
50460
50461     setLocked : function(colIndex, value, suppressEvent){
50462         if(this.isLocked(colIndex) == value){
50463             return;
50464         }
50465         this.config[colIndex].locked = value;
50466         if(!suppressEvent){
50467             this.fireEvent("columnlockchange", this, colIndex, value);
50468         }
50469     },
50470
50471     getTotalLockedWidth : function(){
50472         var totalWidth = 0;
50473         for(var i = 0; i < this.config.length; i++){
50474             if(this.isLocked(i) && !this.isHidden(i)){
50475                 this.totalWidth += this.getColumnWidth(i);
50476             }
50477         }
50478         return totalWidth;
50479     },
50480
50481     getLockedCount : function(){
50482         for(var i = 0, len = this.config.length; i < len; i++){
50483             if(!this.isLocked(i)){
50484                 return i;
50485             }
50486         }
50487     },
50488
50489     /**
50490      * Returns the number of columns.
50491      * @return {Number}
50492      */
50493     getColumnCount : function(visibleOnly){
50494         if(visibleOnly === true){
50495             var c = 0;
50496             for(var i = 0, len = this.config.length; i < len; i++){
50497                 if(!this.isHidden(i)){
50498                     c++;
50499                 }
50500             }
50501             return c;
50502         }
50503         return this.config.length;
50504     },
50505
50506     /**
50507      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50508      * @param {Function} fn
50509      * @param {Object} scope (optional)
50510      * @return {Array} result
50511      */
50512     getColumnsBy : function(fn, scope){
50513         var r = [];
50514         for(var i = 0, len = this.config.length; i < len; i++){
50515             var c = this.config[i];
50516             if(fn.call(scope||this, c, i) === true){
50517                 r[r.length] = c;
50518             }
50519         }
50520         return r;
50521     },
50522
50523     /**
50524      * Returns true if the specified column is sortable.
50525      * @param {Number} col The column index
50526      * @return {Boolean}
50527      */
50528     isSortable : function(col){
50529         if(typeof this.config[col].sortable == "undefined"){
50530             return this.defaultSortable;
50531         }
50532         return this.config[col].sortable;
50533     },
50534
50535     /**
50536      * Returns the rendering (formatting) function defined for the column.
50537      * @param {Number} col The column index.
50538      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50539      */
50540     getRenderer : function(col){
50541         if(!this.config[col].renderer){
50542             return Roo.grid.ColumnModel.defaultRenderer;
50543         }
50544         return this.config[col].renderer;
50545     },
50546
50547     /**
50548      * Sets the rendering (formatting) function for a column.
50549      * @param {Number} col The column index
50550      * @param {Function} fn The function to use to process the cell's raw data
50551      * to return HTML markup for the grid view. The render function is called with
50552      * the following parameters:<ul>
50553      * <li>Data value.</li>
50554      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50555      * <li>css A CSS style string to apply to the table cell.</li>
50556      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50557      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50558      * <li>Row index</li>
50559      * <li>Column index</li>
50560      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50561      */
50562     setRenderer : function(col, fn){
50563         this.config[col].renderer = fn;
50564     },
50565
50566     /**
50567      * Returns the width for the specified column.
50568      * @param {Number} col The column index
50569      * @return {Number}
50570      */
50571     getColumnWidth : function(col){
50572         return this.config[col].width * 1 || this.defaultWidth;
50573     },
50574
50575     /**
50576      * Sets the width for a column.
50577      * @param {Number} col The column index
50578      * @param {Number} width The new width
50579      */
50580     setColumnWidth : function(col, width, suppressEvent){
50581         this.config[col].width = width;
50582         this.totalWidth = null;
50583         if(!suppressEvent){
50584              this.fireEvent("widthchange", this, col, width);
50585         }
50586     },
50587
50588     /**
50589      * Returns the total width of all columns.
50590      * @param {Boolean} includeHidden True to include hidden column widths
50591      * @return {Number}
50592      */
50593     getTotalWidth : function(includeHidden){
50594         if(!this.totalWidth){
50595             this.totalWidth = 0;
50596             for(var i = 0, len = this.config.length; i < len; i++){
50597                 if(includeHidden || !this.isHidden(i)){
50598                     this.totalWidth += this.getColumnWidth(i);
50599                 }
50600             }
50601         }
50602         return this.totalWidth;
50603     },
50604
50605     /**
50606      * Returns the header for the specified column.
50607      * @param {Number} col The column index
50608      * @return {String}
50609      */
50610     getColumnHeader : function(col){
50611         return this.config[col].header;
50612     },
50613
50614     /**
50615      * Sets the header for a column.
50616      * @param {Number} col The column index
50617      * @param {String} header The new header
50618      */
50619     setColumnHeader : function(col, header){
50620         this.config[col].header = header;
50621         this.fireEvent("headerchange", this, col, header);
50622     },
50623
50624     /**
50625      * Returns the tooltip for the specified column.
50626      * @param {Number} col The column index
50627      * @return {String}
50628      */
50629     getColumnTooltip : function(col){
50630             return this.config[col].tooltip;
50631     },
50632     /**
50633      * Sets the tooltip for a column.
50634      * @param {Number} col The column index
50635      * @param {String} tooltip The new tooltip
50636      */
50637     setColumnTooltip : function(col, tooltip){
50638             this.config[col].tooltip = tooltip;
50639     },
50640
50641     /**
50642      * Returns the dataIndex for the specified column.
50643      * @param {Number} col The column index
50644      * @return {Number}
50645      */
50646     getDataIndex : function(col){
50647         return this.config[col].dataIndex;
50648     },
50649
50650     /**
50651      * Sets the dataIndex for a column.
50652      * @param {Number} col The column index
50653      * @param {Number} dataIndex The new dataIndex
50654      */
50655     setDataIndex : function(col, dataIndex){
50656         this.config[col].dataIndex = dataIndex;
50657     },
50658
50659     
50660     
50661     /**
50662      * Returns true if the cell is editable.
50663      * @param {Number} colIndex The column index
50664      * @param {Number} rowIndex The row index
50665      * @return {Boolean}
50666      */
50667     isCellEditable : function(colIndex, rowIndex){
50668         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50669     },
50670
50671     /**
50672      * Returns the editor defined for the cell/column.
50673      * return false or null to disable editing.
50674      * @param {Number} colIndex The column index
50675      * @param {Number} rowIndex The row index
50676      * @return {Object}
50677      */
50678     getCellEditor : function(colIndex, rowIndex){
50679         return this.config[colIndex].editor;
50680     },
50681
50682     /**
50683      * Sets if a column is editable.
50684      * @param {Number} col The column index
50685      * @param {Boolean} editable True if the column is editable
50686      */
50687     setEditable : function(col, editable){
50688         this.config[col].editable = editable;
50689     },
50690
50691
50692     /**
50693      * Returns true if the column is hidden.
50694      * @param {Number} colIndex The column index
50695      * @return {Boolean}
50696      */
50697     isHidden : function(colIndex){
50698         return this.config[colIndex].hidden;
50699     },
50700
50701
50702     /**
50703      * Returns true if the column width cannot be changed
50704      */
50705     isFixed : function(colIndex){
50706         return this.config[colIndex].fixed;
50707     },
50708
50709     /**
50710      * Returns true if the column can be resized
50711      * @return {Boolean}
50712      */
50713     isResizable : function(colIndex){
50714         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50715     },
50716     /**
50717      * Sets if a column is hidden.
50718      * @param {Number} colIndex The column index
50719      * @param {Boolean} hidden True if the column is hidden
50720      */
50721     setHidden : function(colIndex, hidden){
50722         this.config[colIndex].hidden = hidden;
50723         this.totalWidth = null;
50724         this.fireEvent("hiddenchange", this, colIndex, hidden);
50725     },
50726
50727     /**
50728      * Sets the editor for a column.
50729      * @param {Number} col The column index
50730      * @param {Object} editor The editor object
50731      */
50732     setEditor : function(col, editor){
50733         this.config[col].editor = editor;
50734     }
50735 });
50736
50737 Roo.grid.ColumnModel.defaultRenderer = function(value){
50738         if(typeof value == "string" && value.length < 1){
50739             return "&#160;";
50740         }
50741         return value;
50742 };
50743
50744 // Alias for backwards compatibility
50745 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50746 /*
50747  * Based on:
50748  * Ext JS Library 1.1.1
50749  * Copyright(c) 2006-2007, Ext JS, LLC.
50750  *
50751  * Originally Released Under LGPL - original licence link has changed is not relivant.
50752  *
50753  * Fork - LGPL
50754  * <script type="text/javascript">
50755  */
50756
50757 /**
50758  * @class Roo.grid.AbstractSelectionModel
50759  * @extends Roo.util.Observable
50760  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50761  * implemented by descendant classes.  This class should not be directly instantiated.
50762  * @constructor
50763  */
50764 Roo.grid.AbstractSelectionModel = function(){
50765     this.locked = false;
50766     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50767 };
50768
50769 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50770     /** @ignore Called by the grid automatically. Do not call directly. */
50771     init : function(grid){
50772         this.grid = grid;
50773         this.initEvents();
50774     },
50775
50776     /**
50777      * Locks the selections.
50778      */
50779     lock : function(){
50780         this.locked = true;
50781     },
50782
50783     /**
50784      * Unlocks the selections.
50785      */
50786     unlock : function(){
50787         this.locked = false;
50788     },
50789
50790     /**
50791      * Returns true if the selections are locked.
50792      * @return {Boolean}
50793      */
50794     isLocked : function(){
50795         return this.locked;
50796     }
50797 });/*
50798  * Based on:
50799  * Ext JS Library 1.1.1
50800  * Copyright(c) 2006-2007, Ext JS, LLC.
50801  *
50802  * Originally Released Under LGPL - original licence link has changed is not relivant.
50803  *
50804  * Fork - LGPL
50805  * <script type="text/javascript">
50806  */
50807 /**
50808  * @extends Roo.grid.AbstractSelectionModel
50809  * @class Roo.grid.RowSelectionModel
50810  * The default SelectionModel used by {@link Roo.grid.Grid}.
50811  * It supports multiple selections and keyboard selection/navigation. 
50812  * @constructor
50813  * @param {Object} config
50814  */
50815 Roo.grid.RowSelectionModel = function(config){
50816     Roo.apply(this, config);
50817     this.selections = new Roo.util.MixedCollection(false, function(o){
50818         return o.id;
50819     });
50820
50821     this.last = false;
50822     this.lastActive = false;
50823
50824     this.addEvents({
50825         /**
50826              * @event selectionchange
50827              * Fires when the selection changes
50828              * @param {SelectionModel} this
50829              */
50830             "selectionchange" : true,
50831         /**
50832              * @event afterselectionchange
50833              * Fires after the selection changes (eg. by key press or clicking)
50834              * @param {SelectionModel} this
50835              */
50836             "afterselectionchange" : true,
50837         /**
50838              * @event beforerowselect
50839              * Fires when a row is selected being selected, return false to cancel.
50840              * @param {SelectionModel} this
50841              * @param {Number} rowIndex The selected index
50842              * @param {Boolean} keepExisting False if other selections will be cleared
50843              */
50844             "beforerowselect" : true,
50845         /**
50846              * @event rowselect
50847              * Fires when a row is selected.
50848              * @param {SelectionModel} this
50849              * @param {Number} rowIndex The selected index
50850              * @param {Roo.data.Record} r The record
50851              */
50852             "rowselect" : true,
50853         /**
50854              * @event rowdeselect
50855              * Fires when a row is deselected.
50856              * @param {SelectionModel} this
50857              * @param {Number} rowIndex The selected index
50858              */
50859         "rowdeselect" : true
50860     });
50861     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50862     this.locked = false;
50863 };
50864
50865 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50866     /**
50867      * @cfg {Boolean} singleSelect
50868      * True to allow selection of only one row at a time (defaults to false)
50869      */
50870     singleSelect : false,
50871
50872     // private
50873     initEvents : function(){
50874
50875         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50876             this.grid.on("mousedown", this.handleMouseDown, this);
50877         }else{ // allow click to work like normal
50878             this.grid.on("rowclick", this.handleDragableRowClick, this);
50879         }
50880
50881         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50882             "up" : function(e){
50883                 if(!e.shiftKey){
50884                     this.selectPrevious(e.shiftKey);
50885                 }else if(this.last !== false && this.lastActive !== false){
50886                     var last = this.last;
50887                     this.selectRange(this.last,  this.lastActive-1);
50888                     this.grid.getView().focusRow(this.lastActive);
50889                     if(last !== false){
50890                         this.last = last;
50891                     }
50892                 }else{
50893                     this.selectFirstRow();
50894                 }
50895                 this.fireEvent("afterselectionchange", this);
50896             },
50897             "down" : function(e){
50898                 if(!e.shiftKey){
50899                     this.selectNext(e.shiftKey);
50900                 }else if(this.last !== false && this.lastActive !== false){
50901                     var last = this.last;
50902                     this.selectRange(this.last,  this.lastActive+1);
50903                     this.grid.getView().focusRow(this.lastActive);
50904                     if(last !== false){
50905                         this.last = last;
50906                     }
50907                 }else{
50908                     this.selectFirstRow();
50909                 }
50910                 this.fireEvent("afterselectionchange", this);
50911             },
50912             scope: this
50913         });
50914
50915         var view = this.grid.view;
50916         view.on("refresh", this.onRefresh, this);
50917         view.on("rowupdated", this.onRowUpdated, this);
50918         view.on("rowremoved", this.onRemove, this);
50919     },
50920
50921     // private
50922     onRefresh : function(){
50923         var ds = this.grid.dataSource, i, v = this.grid.view;
50924         var s = this.selections;
50925         s.each(function(r){
50926             if((i = ds.indexOfId(r.id)) != -1){
50927                 v.onRowSelect(i);
50928             }else{
50929                 s.remove(r);
50930             }
50931         });
50932     },
50933
50934     // private
50935     onRemove : function(v, index, r){
50936         this.selections.remove(r);
50937     },
50938
50939     // private
50940     onRowUpdated : function(v, index, r){
50941         if(this.isSelected(r)){
50942             v.onRowSelect(index);
50943         }
50944     },
50945
50946     /**
50947      * Select records.
50948      * @param {Array} records The records to select
50949      * @param {Boolean} keepExisting (optional) True to keep existing selections
50950      */
50951     selectRecords : function(records, keepExisting){
50952         if(!keepExisting){
50953             this.clearSelections();
50954         }
50955         var ds = this.grid.dataSource;
50956         for(var i = 0, len = records.length; i < len; i++){
50957             this.selectRow(ds.indexOf(records[i]), true);
50958         }
50959     },
50960
50961     /**
50962      * Gets the number of selected rows.
50963      * @return {Number}
50964      */
50965     getCount : function(){
50966         return this.selections.length;
50967     },
50968
50969     /**
50970      * Selects the first row in the grid.
50971      */
50972     selectFirstRow : function(){
50973         this.selectRow(0);
50974     },
50975
50976     /**
50977      * Select the last row.
50978      * @param {Boolean} keepExisting (optional) True to keep existing selections
50979      */
50980     selectLastRow : function(keepExisting){
50981         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50982     },
50983
50984     /**
50985      * Selects the row immediately following the last selected row.
50986      * @param {Boolean} keepExisting (optional) True to keep existing selections
50987      */
50988     selectNext : function(keepExisting){
50989         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50990             this.selectRow(this.last+1, keepExisting);
50991             this.grid.getView().focusRow(this.last);
50992         }
50993     },
50994
50995     /**
50996      * Selects the row that precedes the last selected row.
50997      * @param {Boolean} keepExisting (optional) True to keep existing selections
50998      */
50999     selectPrevious : function(keepExisting){
51000         if(this.last){
51001             this.selectRow(this.last-1, keepExisting);
51002             this.grid.getView().focusRow(this.last);
51003         }
51004     },
51005
51006     /**
51007      * Returns the selected records
51008      * @return {Array} Array of selected records
51009      */
51010     getSelections : function(){
51011         return [].concat(this.selections.items);
51012     },
51013
51014     /**
51015      * Returns the first selected record.
51016      * @return {Record}
51017      */
51018     getSelected : function(){
51019         return this.selections.itemAt(0);
51020     },
51021
51022
51023     /**
51024      * Clears all selections.
51025      */
51026     clearSelections : function(fast){
51027         if(this.locked) return;
51028         if(fast !== true){
51029             var ds = this.grid.dataSource;
51030             var s = this.selections;
51031             s.each(function(r){
51032                 this.deselectRow(ds.indexOfId(r.id));
51033             }, this);
51034             s.clear();
51035         }else{
51036             this.selections.clear();
51037         }
51038         this.last = false;
51039     },
51040
51041
51042     /**
51043      * Selects all rows.
51044      */
51045     selectAll : function(){
51046         if(this.locked) return;
51047         this.selections.clear();
51048         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51049             this.selectRow(i, true);
51050         }
51051     },
51052
51053     /**
51054      * Returns True if there is a selection.
51055      * @return {Boolean}
51056      */
51057     hasSelection : function(){
51058         return this.selections.length > 0;
51059     },
51060
51061     /**
51062      * Returns True if the specified row is selected.
51063      * @param {Number/Record} record The record or index of the record to check
51064      * @return {Boolean}
51065      */
51066     isSelected : function(index){
51067         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51068         return (r && this.selections.key(r.id) ? true : false);
51069     },
51070
51071     /**
51072      * Returns True if the specified record id is selected.
51073      * @param {String} id The id of record to check
51074      * @return {Boolean}
51075      */
51076     isIdSelected : function(id){
51077         return (this.selections.key(id) ? true : false);
51078     },
51079
51080     // private
51081     handleMouseDown : function(e, t){
51082         var view = this.grid.getView(), rowIndex;
51083         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51084             return;
51085         };
51086         if(e.shiftKey && this.last !== false){
51087             var last = this.last;
51088             this.selectRange(last, rowIndex, e.ctrlKey);
51089             this.last = last; // reset the last
51090             view.focusRow(rowIndex);
51091         }else{
51092             var isSelected = this.isSelected(rowIndex);
51093             if(e.button !== 0 && isSelected){
51094                 view.focusRow(rowIndex);
51095             }else if(e.ctrlKey && isSelected){
51096                 this.deselectRow(rowIndex);
51097             }else if(!isSelected){
51098                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51099                 view.focusRow(rowIndex);
51100             }
51101         }
51102         this.fireEvent("afterselectionchange", this);
51103     },
51104     // private
51105     handleDragableRowClick :  function(grid, rowIndex, e) 
51106     {
51107         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51108             this.selectRow(rowIndex, false);
51109             grid.view.focusRow(rowIndex);
51110              this.fireEvent("afterselectionchange", this);
51111         }
51112     },
51113     
51114     /**
51115      * Selects multiple rows.
51116      * @param {Array} rows Array of the indexes of the row to select
51117      * @param {Boolean} keepExisting (optional) True to keep existing selections
51118      */
51119     selectRows : function(rows, keepExisting){
51120         if(!keepExisting){
51121             this.clearSelections();
51122         }
51123         for(var i = 0, len = rows.length; i < len; i++){
51124             this.selectRow(rows[i], true);
51125         }
51126     },
51127
51128     /**
51129      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51130      * @param {Number} startRow The index of the first row in the range
51131      * @param {Number} endRow The index of the last row in the range
51132      * @param {Boolean} keepExisting (optional) True to retain existing selections
51133      */
51134     selectRange : function(startRow, endRow, keepExisting){
51135         if(this.locked) return;
51136         if(!keepExisting){
51137             this.clearSelections();
51138         }
51139         if(startRow <= endRow){
51140             for(var i = startRow; i <= endRow; i++){
51141                 this.selectRow(i, true);
51142             }
51143         }else{
51144             for(var i = startRow; i >= endRow; i--){
51145                 this.selectRow(i, true);
51146             }
51147         }
51148     },
51149
51150     /**
51151      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51152      * @param {Number} startRow The index of the first row in the range
51153      * @param {Number} endRow The index of the last row in the range
51154      */
51155     deselectRange : function(startRow, endRow, preventViewNotify){
51156         if(this.locked) return;
51157         for(var i = startRow; i <= endRow; i++){
51158             this.deselectRow(i, preventViewNotify);
51159         }
51160     },
51161
51162     /**
51163      * Selects a row.
51164      * @param {Number} row The index of the row to select
51165      * @param {Boolean} keepExisting (optional) True to keep existing selections
51166      */
51167     selectRow : function(index, keepExisting, preventViewNotify){
51168         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51169         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51170             if(!keepExisting || this.singleSelect){
51171                 this.clearSelections();
51172             }
51173             var r = this.grid.dataSource.getAt(index);
51174             this.selections.add(r);
51175             this.last = this.lastActive = index;
51176             if(!preventViewNotify){
51177                 this.grid.getView().onRowSelect(index);
51178             }
51179             this.fireEvent("rowselect", this, index, r);
51180             this.fireEvent("selectionchange", this);
51181         }
51182     },
51183
51184     /**
51185      * Deselects a row.
51186      * @param {Number} row The index of the row to deselect
51187      */
51188     deselectRow : function(index, preventViewNotify){
51189         if(this.locked) return;
51190         if(this.last == index){
51191             this.last = false;
51192         }
51193         if(this.lastActive == index){
51194             this.lastActive = false;
51195         }
51196         var r = this.grid.dataSource.getAt(index);
51197         this.selections.remove(r);
51198         if(!preventViewNotify){
51199             this.grid.getView().onRowDeselect(index);
51200         }
51201         this.fireEvent("rowdeselect", this, index);
51202         this.fireEvent("selectionchange", this);
51203     },
51204
51205     // private
51206     restoreLast : function(){
51207         if(this._last){
51208             this.last = this._last;
51209         }
51210     },
51211
51212     // private
51213     acceptsNav : function(row, col, cm){
51214         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51215     },
51216
51217     // private
51218     onEditorKey : function(field, e){
51219         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51220         if(k == e.TAB){
51221             e.stopEvent();
51222             ed.completeEdit();
51223             if(e.shiftKey){
51224                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51225             }else{
51226                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51227             }
51228         }else if(k == e.ENTER && !e.ctrlKey){
51229             e.stopEvent();
51230             ed.completeEdit();
51231             if(e.shiftKey){
51232                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51233             }else{
51234                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51235             }
51236         }else if(k == e.ESC){
51237             ed.cancelEdit();
51238         }
51239         if(newCell){
51240             g.startEditing(newCell[0], newCell[1]);
51241         }
51242     }
51243 });/*
51244  * Based on:
51245  * Ext JS Library 1.1.1
51246  * Copyright(c) 2006-2007, Ext JS, LLC.
51247  *
51248  * Originally Released Under LGPL - original licence link has changed is not relivant.
51249  *
51250  * Fork - LGPL
51251  * <script type="text/javascript">
51252  */
51253 /**
51254  * @class Roo.grid.CellSelectionModel
51255  * @extends Roo.grid.AbstractSelectionModel
51256  * This class provides the basic implementation for cell selection in a grid.
51257  * @constructor
51258  * @param {Object} config The object containing the configuration of this model.
51259  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51260  */
51261 Roo.grid.CellSelectionModel = function(config){
51262     Roo.apply(this, config);
51263
51264     this.selection = null;
51265
51266     this.addEvents({
51267         /**
51268              * @event beforerowselect
51269              * Fires before a cell is selected.
51270              * @param {SelectionModel} this
51271              * @param {Number} rowIndex The selected row index
51272              * @param {Number} colIndex The selected cell index
51273              */
51274             "beforecellselect" : true,
51275         /**
51276              * @event cellselect
51277              * Fires when a cell is selected.
51278              * @param {SelectionModel} this
51279              * @param {Number} rowIndex The selected row index
51280              * @param {Number} colIndex The selected cell index
51281              */
51282             "cellselect" : true,
51283         /**
51284              * @event selectionchange
51285              * Fires when the active selection changes.
51286              * @param {SelectionModel} this
51287              * @param {Object} selection null for no selection or an object (o) with two properties
51288                 <ul>
51289                 <li>o.record: the record object for the row the selection is in</li>
51290                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51291                 </ul>
51292              */
51293             "selectionchange" : true,
51294         /**
51295              * @event tabend
51296              * Fires when the tab (or enter) was pressed on the last editable cell
51297              * You can use this to trigger add new row.
51298              * @param {SelectionModel} this
51299              */
51300             "tabend" : true
51301     });
51302     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51303 };
51304
51305 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51306     
51307     enter_is_tab: false,
51308
51309     /** @ignore */
51310     initEvents : function(){
51311         this.grid.on("mousedown", this.handleMouseDown, this);
51312         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51313         var view = this.grid.view;
51314         view.on("refresh", this.onViewChange, this);
51315         view.on("rowupdated", this.onRowUpdated, this);
51316         view.on("beforerowremoved", this.clearSelections, this);
51317         view.on("beforerowsinserted", this.clearSelections, this);
51318         if(this.grid.isEditor){
51319             this.grid.on("beforeedit", this.beforeEdit,  this);
51320         }
51321     },
51322
51323         //private
51324     beforeEdit : function(e){
51325         this.select(e.row, e.column, false, true, e.record);
51326     },
51327
51328         //private
51329     onRowUpdated : function(v, index, r){
51330         if(this.selection && this.selection.record == r){
51331             v.onCellSelect(index, this.selection.cell[1]);
51332         }
51333     },
51334
51335         //private
51336     onViewChange : function(){
51337         this.clearSelections(true);
51338     },
51339
51340         /**
51341          * Returns the currently selected cell,.
51342          * @return {Array} The selected cell (row, column) or null if none selected.
51343          */
51344     getSelectedCell : function(){
51345         return this.selection ? this.selection.cell : null;
51346     },
51347
51348     /**
51349      * Clears all selections.
51350      * @param {Boolean} true to prevent the gridview from being notified about the change.
51351      */
51352     clearSelections : function(preventNotify){
51353         var s = this.selection;
51354         if(s){
51355             if(preventNotify !== true){
51356                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51357             }
51358             this.selection = null;
51359             this.fireEvent("selectionchange", this, null);
51360         }
51361     },
51362
51363     /**
51364      * Returns true if there is a selection.
51365      * @return {Boolean}
51366      */
51367     hasSelection : function(){
51368         return this.selection ? true : false;
51369     },
51370
51371     /** @ignore */
51372     handleMouseDown : function(e, t){
51373         var v = this.grid.getView();
51374         if(this.isLocked()){
51375             return;
51376         };
51377         var row = v.findRowIndex(t);
51378         var cell = v.findCellIndex(t);
51379         if(row !== false && cell !== false){
51380             this.select(row, cell);
51381         }
51382     },
51383
51384     /**
51385      * Selects a cell.
51386      * @param {Number} rowIndex
51387      * @param {Number} collIndex
51388      */
51389     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51390         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51391             this.clearSelections();
51392             r = r || this.grid.dataSource.getAt(rowIndex);
51393             this.selection = {
51394                 record : r,
51395                 cell : [rowIndex, colIndex]
51396             };
51397             if(!preventViewNotify){
51398                 var v = this.grid.getView();
51399                 v.onCellSelect(rowIndex, colIndex);
51400                 if(preventFocus !== true){
51401                     v.focusCell(rowIndex, colIndex);
51402                 }
51403             }
51404             this.fireEvent("cellselect", this, rowIndex, colIndex);
51405             this.fireEvent("selectionchange", this, this.selection);
51406         }
51407     },
51408
51409         //private
51410     isSelectable : function(rowIndex, colIndex, cm){
51411         return !cm.isHidden(colIndex);
51412     },
51413
51414     /** @ignore */
51415     handleKeyDown : function(e){
51416         //Roo.log('Cell Sel Model handleKeyDown');
51417         if(!e.isNavKeyPress()){
51418             return;
51419         }
51420         var g = this.grid, s = this.selection;
51421         if(!s){
51422             e.stopEvent();
51423             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51424             if(cell){
51425                 this.select(cell[0], cell[1]);
51426             }
51427             return;
51428         }
51429         var sm = this;
51430         var walk = function(row, col, step){
51431             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51432         };
51433         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51434         var newCell;
51435
51436       
51437
51438         switch(k){
51439             case e.TAB:
51440                 // handled by onEditorKey
51441                 if (g.isEditor && g.editing) {
51442                     return;
51443                 }
51444                 if(e.shiftKey) {
51445                     newCell = walk(r, c-1, -1);
51446                 } else {
51447                     newCell = walk(r, c+1, 1);
51448                 }
51449                 break;
51450             
51451             case e.DOWN:
51452                newCell = walk(r+1, c, 1);
51453                 break;
51454             
51455             case e.UP:
51456                 newCell = walk(r-1, c, -1);
51457                 break;
51458             
51459             case e.RIGHT:
51460                 newCell = walk(r, c+1, 1);
51461                 break;
51462             
51463             case e.LEFT:
51464                 newCell = walk(r, c-1, -1);
51465                 break;
51466             
51467             case e.ENTER:
51468                 
51469                 if(g.isEditor && !g.editing){
51470                    g.startEditing(r, c);
51471                    e.stopEvent();
51472                    return;
51473                 }
51474                 
51475                 
51476              break;
51477         };
51478         if(newCell){
51479             this.select(newCell[0], newCell[1]);
51480             e.stopEvent();
51481             
51482         }
51483     },
51484
51485     acceptsNav : function(row, col, cm){
51486         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51487     },
51488     /**
51489      * Selects a cell.
51490      * @param {Number} field (not used) - as it's normally used as a listener
51491      * @param {Number} e - event - fake it by using
51492      *
51493      * var e = Roo.EventObjectImpl.prototype;
51494      * e.keyCode = e.TAB
51495      *
51496      * 
51497      */
51498     onEditorKey : function(field, e){
51499         
51500         var k = e.getKey(),
51501             newCell,
51502             g = this.grid,
51503             ed = g.activeEditor,
51504             forward = false;
51505         ///Roo.log('onEditorKey' + k);
51506         
51507         
51508         if (this.enter_is_tab && k == e.ENTER) {
51509             k = e.TAB;
51510         }
51511         
51512         if(k == e.TAB){
51513             if(e.shiftKey){
51514                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51515             }else{
51516                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51517                 forward = true;
51518             }
51519             
51520             e.stopEvent();
51521             
51522         }else if(k == e.ENTER &&  !e.ctrlKey){
51523             ed.completeEdit();
51524             e.stopEvent();
51525             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51526         }else if(k == e.ESC){
51527             ed.cancelEdit();
51528         }
51529         
51530         
51531         if(newCell){
51532             //Roo.log('next cell after edit');
51533             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51534         } else if (forward) {
51535             // tabbed past last
51536             this.fireEvent.defer(100, this, ['tabend',this]);
51537         }
51538     }
51539 });/*
51540  * Based on:
51541  * Ext JS Library 1.1.1
51542  * Copyright(c) 2006-2007, Ext JS, LLC.
51543  *
51544  * Originally Released Under LGPL - original licence link has changed is not relivant.
51545  *
51546  * Fork - LGPL
51547  * <script type="text/javascript">
51548  */
51549  
51550 /**
51551  * @class Roo.grid.EditorGrid
51552  * @extends Roo.grid.Grid
51553  * Class for creating and editable grid.
51554  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51555  * The container MUST have some type of size defined for the grid to fill. The container will be 
51556  * automatically set to position relative if it isn't already.
51557  * @param {Object} dataSource The data model to bind to
51558  * @param {Object} colModel The column model with info about this grid's columns
51559  */
51560 Roo.grid.EditorGrid = function(container, config){
51561     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51562     this.getGridEl().addClass("xedit-grid");
51563
51564     if(!this.selModel){
51565         this.selModel = new Roo.grid.CellSelectionModel();
51566     }
51567
51568     this.activeEditor = null;
51569
51570         this.addEvents({
51571             /**
51572              * @event beforeedit
51573              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51574              * <ul style="padding:5px;padding-left:16px;">
51575              * <li>grid - This grid</li>
51576              * <li>record - The record being edited</li>
51577              * <li>field - The field name being edited</li>
51578              * <li>value - The value for the field being edited.</li>
51579              * <li>row - The grid row index</li>
51580              * <li>column - The grid column index</li>
51581              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51582              * </ul>
51583              * @param {Object} e An edit event (see above for description)
51584              */
51585             "beforeedit" : true,
51586             /**
51587              * @event afteredit
51588              * Fires after a cell is edited. <br />
51589              * <ul style="padding:5px;padding-left:16px;">
51590              * <li>grid - This grid</li>
51591              * <li>record - The record being edited</li>
51592              * <li>field - The field name being edited</li>
51593              * <li>value - The value being set</li>
51594              * <li>originalValue - The original value for the field, before the edit.</li>
51595              * <li>row - The grid row index</li>
51596              * <li>column - The grid column index</li>
51597              * </ul>
51598              * @param {Object} e An edit event (see above for description)
51599              */
51600             "afteredit" : true,
51601             /**
51602              * @event validateedit
51603              * Fires after a cell is edited, but before the value is set in the record. 
51604          * You can use this to modify the value being set in the field, Return false
51605              * to cancel the change. The edit event object has the following properties <br />
51606              * <ul style="padding:5px;padding-left:16px;">
51607          * <li>editor - This editor</li>
51608              * <li>grid - This grid</li>
51609              * <li>record - The record being edited</li>
51610              * <li>field - The field name being edited</li>
51611              * <li>value - The value being set</li>
51612              * <li>originalValue - The original value for the field, before the edit.</li>
51613              * <li>row - The grid row index</li>
51614              * <li>column - The grid column index</li>
51615              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51616              * </ul>
51617              * @param {Object} e An edit event (see above for description)
51618              */
51619             "validateedit" : true
51620         });
51621     this.on("bodyscroll", this.stopEditing,  this);
51622     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51623 };
51624
51625 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51626     /**
51627      * @cfg {Number} clicksToEdit
51628      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51629      */
51630     clicksToEdit: 2,
51631
51632     // private
51633     isEditor : true,
51634     // private
51635     trackMouseOver: false, // causes very odd FF errors
51636
51637     onCellDblClick : function(g, row, col){
51638         this.startEditing(row, col);
51639     },
51640
51641     onEditComplete : function(ed, value, startValue){
51642         this.editing = false;
51643         this.activeEditor = null;
51644         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51645         var r = ed.record;
51646         var field = this.colModel.getDataIndex(ed.col);
51647         var e = {
51648             grid: this,
51649             record: r,
51650             field: field,
51651             originalValue: startValue,
51652             value: value,
51653             row: ed.row,
51654             column: ed.col,
51655             cancel:false,
51656             editor: ed
51657         };
51658         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51659         cell.show();
51660           
51661         if(String(value) !== String(startValue)){
51662             
51663             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51664                 r.set(field, e.value);
51665                 // if we are dealing with a combo box..
51666                 // then we also set the 'name' colum to be the displayField
51667                 if (ed.field.displayField && ed.field.name) {
51668                     r.set(ed.field.name, ed.field.el.dom.value);
51669                 }
51670                 
51671                 delete e.cancel; //?? why!!!
51672                 this.fireEvent("afteredit", e);
51673             }
51674         } else {
51675             this.fireEvent("afteredit", e); // always fire it!
51676         }
51677         this.view.focusCell(ed.row, ed.col);
51678     },
51679
51680     /**
51681      * Starts editing the specified for the specified row/column
51682      * @param {Number} rowIndex
51683      * @param {Number} colIndex
51684      */
51685     startEditing : function(row, col){
51686         this.stopEditing();
51687         if(this.colModel.isCellEditable(col, row)){
51688             this.view.ensureVisible(row, col, true);
51689           
51690             var r = this.dataSource.getAt(row);
51691             var field = this.colModel.getDataIndex(col);
51692             var cell = Roo.get(this.view.getCell(row,col));
51693             var e = {
51694                 grid: this,
51695                 record: r,
51696                 field: field,
51697                 value: r.data[field],
51698                 row: row,
51699                 column: col,
51700                 cancel:false 
51701             };
51702             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51703                 this.editing = true;
51704                 var ed = this.colModel.getCellEditor(col, row);
51705                 
51706                 if (!ed) {
51707                     return;
51708                 }
51709                 if(!ed.rendered){
51710                     ed.render(ed.parentEl || document.body);
51711                 }
51712                 ed.field.reset();
51713                
51714                 cell.hide();
51715                 
51716                 (function(){ // complex but required for focus issues in safari, ie and opera
51717                     ed.row = row;
51718                     ed.col = col;
51719                     ed.record = r;
51720                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51721                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51722                     this.activeEditor = ed;
51723                     var v = r.data[field];
51724                     ed.startEdit(this.view.getCell(row, col), v);
51725                     // combo's with 'displayField and name set
51726                     if (ed.field.displayField && ed.field.name) {
51727                         ed.field.el.dom.value = r.data[ed.field.name];
51728                     }
51729                     
51730                     
51731                 }).defer(50, this);
51732             }
51733         }
51734     },
51735         
51736     /**
51737      * Stops any active editing
51738      */
51739     stopEditing : function(){
51740         if(this.activeEditor){
51741             this.activeEditor.completeEdit();
51742         }
51743         this.activeEditor = null;
51744     }
51745 });/*
51746  * Based on:
51747  * Ext JS Library 1.1.1
51748  * Copyright(c) 2006-2007, Ext JS, LLC.
51749  *
51750  * Originally Released Under LGPL - original licence link has changed is not relivant.
51751  *
51752  * Fork - LGPL
51753  * <script type="text/javascript">
51754  */
51755
51756 // private - not really -- you end up using it !
51757 // This is a support class used internally by the Grid components
51758
51759 /**
51760  * @class Roo.grid.GridEditor
51761  * @extends Roo.Editor
51762  * Class for creating and editable grid elements.
51763  * @param {Object} config any settings (must include field)
51764  */
51765 Roo.grid.GridEditor = function(field, config){
51766     if (!config && field.field) {
51767         config = field;
51768         field = Roo.factory(config.field, Roo.form);
51769     }
51770     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51771     field.monitorTab = false;
51772 };
51773
51774 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51775     
51776     /**
51777      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51778      */
51779     
51780     alignment: "tl-tl",
51781     autoSize: "width",
51782     hideEl : false,
51783     cls: "x-small-editor x-grid-editor",
51784     shim:false,
51785     shadow:"frame"
51786 });/*
51787  * Based on:
51788  * Ext JS Library 1.1.1
51789  * Copyright(c) 2006-2007, Ext JS, LLC.
51790  *
51791  * Originally Released Under LGPL - original licence link has changed is not relivant.
51792  *
51793  * Fork - LGPL
51794  * <script type="text/javascript">
51795  */
51796   
51797
51798   
51799 Roo.grid.PropertyRecord = Roo.data.Record.create([
51800     {name:'name',type:'string'},  'value'
51801 ]);
51802
51803
51804 Roo.grid.PropertyStore = function(grid, source){
51805     this.grid = grid;
51806     this.store = new Roo.data.Store({
51807         recordType : Roo.grid.PropertyRecord
51808     });
51809     this.store.on('update', this.onUpdate,  this);
51810     if(source){
51811         this.setSource(source);
51812     }
51813     Roo.grid.PropertyStore.superclass.constructor.call(this);
51814 };
51815
51816
51817
51818 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51819     setSource : function(o){
51820         this.source = o;
51821         this.store.removeAll();
51822         var data = [];
51823         for(var k in o){
51824             if(this.isEditableValue(o[k])){
51825                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51826             }
51827         }
51828         this.store.loadRecords({records: data}, {}, true);
51829     },
51830
51831     onUpdate : function(ds, record, type){
51832         if(type == Roo.data.Record.EDIT){
51833             var v = record.data['value'];
51834             var oldValue = record.modified['value'];
51835             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51836                 this.source[record.id] = v;
51837                 record.commit();
51838                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51839             }else{
51840                 record.reject();
51841             }
51842         }
51843     },
51844
51845     getProperty : function(row){
51846        return this.store.getAt(row);
51847     },
51848
51849     isEditableValue: function(val){
51850         if(val && val instanceof Date){
51851             return true;
51852         }else if(typeof val == 'object' || typeof val == 'function'){
51853             return false;
51854         }
51855         return true;
51856     },
51857
51858     setValue : function(prop, value){
51859         this.source[prop] = value;
51860         this.store.getById(prop).set('value', value);
51861     },
51862
51863     getSource : function(){
51864         return this.source;
51865     }
51866 });
51867
51868 Roo.grid.PropertyColumnModel = function(grid, store){
51869     this.grid = grid;
51870     var g = Roo.grid;
51871     g.PropertyColumnModel.superclass.constructor.call(this, [
51872         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51873         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51874     ]);
51875     this.store = store;
51876     this.bselect = Roo.DomHelper.append(document.body, {
51877         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51878             {tag: 'option', value: 'true', html: 'true'},
51879             {tag: 'option', value: 'false', html: 'false'}
51880         ]
51881     });
51882     Roo.id(this.bselect);
51883     var f = Roo.form;
51884     this.editors = {
51885         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51886         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51887         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51888         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51889         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51890     };
51891     this.renderCellDelegate = this.renderCell.createDelegate(this);
51892     this.renderPropDelegate = this.renderProp.createDelegate(this);
51893 };
51894
51895 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51896     
51897     
51898     nameText : 'Name',
51899     valueText : 'Value',
51900     
51901     dateFormat : 'm/j/Y',
51902     
51903     
51904     renderDate : function(dateVal){
51905         return dateVal.dateFormat(this.dateFormat);
51906     },
51907
51908     renderBool : function(bVal){
51909         return bVal ? 'true' : 'false';
51910     },
51911
51912     isCellEditable : function(colIndex, rowIndex){
51913         return colIndex == 1;
51914     },
51915
51916     getRenderer : function(col){
51917         return col == 1 ?
51918             this.renderCellDelegate : this.renderPropDelegate;
51919     },
51920
51921     renderProp : function(v){
51922         return this.getPropertyName(v);
51923     },
51924
51925     renderCell : function(val){
51926         var rv = val;
51927         if(val instanceof Date){
51928             rv = this.renderDate(val);
51929         }else if(typeof val == 'boolean'){
51930             rv = this.renderBool(val);
51931         }
51932         return Roo.util.Format.htmlEncode(rv);
51933     },
51934
51935     getPropertyName : function(name){
51936         var pn = this.grid.propertyNames;
51937         return pn && pn[name] ? pn[name] : name;
51938     },
51939
51940     getCellEditor : function(colIndex, rowIndex){
51941         var p = this.store.getProperty(rowIndex);
51942         var n = p.data['name'], val = p.data['value'];
51943         
51944         if(typeof(this.grid.customEditors[n]) == 'string'){
51945             return this.editors[this.grid.customEditors[n]];
51946         }
51947         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51948             return this.grid.customEditors[n];
51949         }
51950         if(val instanceof Date){
51951             return this.editors['date'];
51952         }else if(typeof val == 'number'){
51953             return this.editors['number'];
51954         }else if(typeof val == 'boolean'){
51955             return this.editors['boolean'];
51956         }else{
51957             return this.editors['string'];
51958         }
51959     }
51960 });
51961
51962 /**
51963  * @class Roo.grid.PropertyGrid
51964  * @extends Roo.grid.EditorGrid
51965  * This class represents the  interface of a component based property grid control.
51966  * <br><br>Usage:<pre><code>
51967  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51968       
51969  });
51970  // set any options
51971  grid.render();
51972  * </code></pre>
51973   
51974  * @constructor
51975  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51976  * The container MUST have some type of size defined for the grid to fill. The container will be
51977  * automatically set to position relative if it isn't already.
51978  * @param {Object} config A config object that sets properties on this grid.
51979  */
51980 Roo.grid.PropertyGrid = function(container, config){
51981     config = config || {};
51982     var store = new Roo.grid.PropertyStore(this);
51983     this.store = store;
51984     var cm = new Roo.grid.PropertyColumnModel(this, store);
51985     store.store.sort('name', 'ASC');
51986     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51987         ds: store.store,
51988         cm: cm,
51989         enableColLock:false,
51990         enableColumnMove:false,
51991         stripeRows:false,
51992         trackMouseOver: false,
51993         clicksToEdit:1
51994     }, config));
51995     this.getGridEl().addClass('x-props-grid');
51996     this.lastEditRow = null;
51997     this.on('columnresize', this.onColumnResize, this);
51998     this.addEvents({
51999          /**
52000              * @event beforepropertychange
52001              * Fires before a property changes (return false to stop?)
52002              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52003              * @param {String} id Record Id
52004              * @param {String} newval New Value
52005          * @param {String} oldval Old Value
52006              */
52007         "beforepropertychange": true,
52008         /**
52009              * @event propertychange
52010              * Fires after a property changes
52011              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52012              * @param {String} id Record Id
52013              * @param {String} newval New Value
52014          * @param {String} oldval Old Value
52015              */
52016         "propertychange": true
52017     });
52018     this.customEditors = this.customEditors || {};
52019 };
52020 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52021     
52022      /**
52023      * @cfg {Object} customEditors map of colnames=> custom editors.
52024      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52025      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52026      * false disables editing of the field.
52027          */
52028     
52029       /**
52030      * @cfg {Object} propertyNames map of property Names to their displayed value
52031          */
52032     
52033     render : function(){
52034         Roo.grid.PropertyGrid.superclass.render.call(this);
52035         this.autoSize.defer(100, this);
52036     },
52037
52038     autoSize : function(){
52039         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52040         if(this.view){
52041             this.view.fitColumns();
52042         }
52043     },
52044
52045     onColumnResize : function(){
52046         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52047         this.autoSize();
52048     },
52049     /**
52050      * Sets the data for the Grid
52051      * accepts a Key => Value object of all the elements avaiable.
52052      * @param {Object} data  to appear in grid.
52053      */
52054     setSource : function(source){
52055         this.store.setSource(source);
52056         //this.autoSize();
52057     },
52058     /**
52059      * Gets all the data from the grid.
52060      * @return {Object} data  data stored in grid
52061      */
52062     getSource : function(){
52063         return this.store.getSource();
52064     }
52065 });/*
52066  * Based on:
52067  * Ext JS Library 1.1.1
52068  * Copyright(c) 2006-2007, Ext JS, LLC.
52069  *
52070  * Originally Released Under LGPL - original licence link has changed is not relivant.
52071  *
52072  * Fork - LGPL
52073  * <script type="text/javascript">
52074  */
52075  
52076 /**
52077  * @class Roo.LoadMask
52078  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52079  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52080  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52081  * element's UpdateManager load indicator and will be destroyed after the initial load.
52082  * @constructor
52083  * Create a new LoadMask
52084  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52085  * @param {Object} config The config object
52086  */
52087 Roo.LoadMask = function(el, config){
52088     this.el = Roo.get(el);
52089     Roo.apply(this, config);
52090     if(this.store){
52091         this.store.on('beforeload', this.onBeforeLoad, this);
52092         this.store.on('load', this.onLoad, this);
52093         this.store.on('loadexception', this.onLoadException, this);
52094         this.removeMask = false;
52095     }else{
52096         var um = this.el.getUpdateManager();
52097         um.showLoadIndicator = false; // disable the default indicator
52098         um.on('beforeupdate', this.onBeforeLoad, this);
52099         um.on('update', this.onLoad, this);
52100         um.on('failure', this.onLoad, this);
52101         this.removeMask = true;
52102     }
52103 };
52104
52105 Roo.LoadMask.prototype = {
52106     /**
52107      * @cfg {Boolean} removeMask
52108      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52109      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52110      */
52111     /**
52112      * @cfg {String} msg
52113      * The text to display in a centered loading message box (defaults to 'Loading...')
52114      */
52115     msg : 'Loading...',
52116     /**
52117      * @cfg {String} msgCls
52118      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52119      */
52120     msgCls : 'x-mask-loading',
52121
52122     /**
52123      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52124      * @type Boolean
52125      */
52126     disabled: false,
52127
52128     /**
52129      * Disables the mask to prevent it from being displayed
52130      */
52131     disable : function(){
52132        this.disabled = true;
52133     },
52134
52135     /**
52136      * Enables the mask so that it can be displayed
52137      */
52138     enable : function(){
52139         this.disabled = false;
52140     },
52141     
52142     onLoadException : function()
52143     {
52144         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52145             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52146         }
52147         this.el.unmask(this.removeMask);
52148     },
52149     // private
52150     onLoad : function()
52151     {
52152         this.el.unmask(this.removeMask);
52153     },
52154
52155     // private
52156     onBeforeLoad : function(){
52157         if(!this.disabled){
52158             this.el.mask(this.msg, this.msgCls);
52159         }
52160     },
52161
52162     // private
52163     destroy : function(){
52164         if(this.store){
52165             this.store.un('beforeload', this.onBeforeLoad, this);
52166             this.store.un('load', this.onLoad, this);
52167             this.store.un('loadexception', this.onLoadException, this);
52168         }else{
52169             var um = this.el.getUpdateManager();
52170             um.un('beforeupdate', this.onBeforeLoad, this);
52171             um.un('update', this.onLoad, this);
52172             um.un('failure', this.onLoad, this);
52173         }
52174     }
52175 };/*
52176  * Based on:
52177  * Ext JS Library 1.1.1
52178  * Copyright(c) 2006-2007, Ext JS, LLC.
52179  *
52180  * Originally Released Under LGPL - original licence link has changed is not relivant.
52181  *
52182  * Fork - LGPL
52183  * <script type="text/javascript">
52184  */
52185 Roo.XTemplate = function(){
52186     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52187     var s = this.html;
52188
52189     s = ['<tpl>', s, '</tpl>'].join('');
52190
52191     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52192
52193     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52194     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52195     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52196     var m, id = 0;
52197     var tpls = [];
52198
52199     while(m = s.match(re)){
52200        var m2 = m[0].match(nameRe);
52201        var m3 = m[0].match(ifRe);
52202        var m4 = m[0].match(execRe);
52203        var exp = null, fn = null, exec = null;
52204        var name = m2 && m2[1] ? m2[1] : '';
52205        if(m3){
52206            exp = m3 && m3[1] ? m3[1] : null;
52207            if(exp){
52208                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52209            }
52210        }
52211        if(m4){
52212            exp = m4 && m4[1] ? m4[1] : null;
52213            if(exp){
52214                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52215            }
52216        }
52217        if(name){
52218            switch(name){
52219                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52220                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52221                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52222            }
52223        }
52224        tpls.push({
52225             id: id,
52226             target: name,
52227             exec: exec,
52228             test: fn,
52229             body: m[1]||''
52230         });
52231        s = s.replace(m[0], '{xtpl'+ id + '}');
52232        ++id;
52233     }
52234     for(var i = tpls.length-1; i >= 0; --i){
52235         this.compileTpl(tpls[i]);
52236     }
52237     this.master = tpls[tpls.length-1];
52238     this.tpls = tpls;
52239 };
52240 Roo.extend(Roo.XTemplate, Roo.Template, {
52241
52242     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52243
52244     applySubTemplate : function(id, values, parent){
52245         var t = this.tpls[id];
52246         if(t.test && !t.test.call(this, values, parent)){
52247             return '';
52248         }
52249         if(t.exec && t.exec.call(this, values, parent)){
52250             return '';
52251         }
52252         var vs = t.target ? t.target.call(this, values, parent) : values;
52253         parent = t.target ? values : parent;
52254         if(t.target && vs instanceof Array){
52255             var buf = [];
52256             for(var i = 0, len = vs.length; i < len; i++){
52257                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52258             }
52259             return buf.join('');
52260         }
52261         return t.compiled.call(this, vs, parent);
52262     },
52263
52264     compileTpl : function(tpl){
52265         var fm = Roo.util.Format;
52266         var useF = this.disableFormats !== true;
52267         var sep = Roo.isGecko ? "+" : ",";
52268         var fn = function(m, name, format, args){
52269             if(name.substr(0, 4) == 'xtpl'){
52270                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52271             }
52272             var v;
52273             if(name.indexOf('.') != -1){
52274                 v = name;
52275             }else{
52276                 v = "values['" + name + "']";
52277             }
52278             if(format && useF){
52279                 args = args ? ',' + args : "";
52280                 if(format.substr(0, 5) != "this."){
52281                     format = "fm." + format + '(';
52282                 }else{
52283                     format = 'this.call("'+ format.substr(5) + '", ';
52284                     args = ", values";
52285                 }
52286             }else{
52287                 args= ''; format = "("+v+" === undefined ? '' : ";
52288             }
52289             return "'"+ sep + format + v + args + ")"+sep+"'";
52290         };
52291         var body;
52292         // branched to use + in gecko and [].join() in others
52293         if(Roo.isGecko){
52294             body = "tpl.compiled = function(values, parent){ return '" +
52295                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52296                     "';};";
52297         }else{
52298             body = ["tpl.compiled = function(values, parent){ return ['"];
52299             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52300             body.push("'].join('');};");
52301             body = body.join('');
52302         }
52303         /** eval:var:zzzzzzz */
52304         eval(body);
52305         return this;
52306     },
52307
52308     applyTemplate : function(values){
52309         return this.master.compiled.call(this, values, {});
52310         var s = this.subs;
52311     },
52312
52313     apply : function(){
52314         return this.applyTemplate.apply(this, arguments);
52315     },
52316
52317     compile : function(){return this;}
52318 });
52319
52320 Roo.XTemplate.from = function(el){
52321     el = Roo.getDom(el);
52322     return new Roo.XTemplate(el.value || el.innerHTML);
52323 };/*
52324  * Original code for Roojs - LGPL
52325  * <script type="text/javascript">
52326  */
52327  
52328 /**
52329  * @class Roo.XComponent
52330  * A delayed Element creator...
52331  * Or a way to group chunks of interface together.
52332  * 
52333  * Mypart.xyx = new Roo.XComponent({
52334
52335     parent : 'Mypart.xyz', // empty == document.element.!!
52336     order : '001',
52337     name : 'xxxx'
52338     region : 'xxxx'
52339     disabled : function() {} 
52340      
52341     tree : function() { // return an tree of xtype declared components
52342         var MODULE = this;
52343         return 
52344         {
52345             xtype : 'NestedLayoutPanel',
52346             // technicall
52347         }
52348      ]
52349  *})
52350  *
52351  *
52352  * It can be used to build a big heiracy, with parent etc.
52353  * or you can just use this to render a single compoent to a dom element
52354  * MYPART.render(Roo.Element | String(id) | dom_element )
52355  * 
52356  * @extends Roo.util.Observable
52357  * @constructor
52358  * @param cfg {Object} configuration of component
52359  * 
52360  */
52361 Roo.XComponent = function(cfg) {
52362     Roo.apply(this, cfg);
52363     this.addEvents({ 
52364         /**
52365              * @event built
52366              * Fires when this the componnt is built
52367              * @param {Roo.XComponent} c the component
52368              */
52369         'built' : true,
52370         /**
52371              * @event buildcomplete
52372              * Fires on the top level element when all elements have been built
52373              * @param {Roo.XComponent} c the top level component.
52374          */
52375         'buildcomplete' : true
52376         
52377     });
52378     this.region = this.region || 'center'; // default..
52379     Roo.XComponent.register(this);
52380     this.modules = false;
52381     this.el = false; // where the layout goes..
52382     
52383     
52384 }
52385 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52386     /**
52387      * @property el
52388      * The created element (with Roo.factory())
52389      * @type {Roo.Layout}
52390      */
52391     el  : false,
52392     
52393     /**
52394      * @property el
52395      * for BC  - use el in new code
52396      * @type {Roo.Layout}
52397      */
52398     panel : false,
52399     
52400     /**
52401      * @property layout
52402      * for BC  - use el in new code
52403      * @type {Roo.Layout}
52404      */
52405     layout : false,
52406     
52407      /**
52408      * @cfg {Function|boolean} disabled
52409      * If this module is disabled by some rule, return true from the funtion
52410      */
52411     disabled : false,
52412     
52413     /**
52414      * @cfg {String} parent 
52415      * Name of parent element which it get xtype added to..
52416      */
52417     parent: false,
52418     
52419     /**
52420      * @cfg {String} order
52421      * Used to set the order in which elements are created (usefull for multiple tabs)
52422      */
52423     
52424     order : false,
52425     /**
52426      * @cfg {String} name
52427      * String to display while loading.
52428      */
52429     name : false,
52430     /**
52431      * @cfg {String} region
52432      * Region to render component to (defaults to center)
52433      */
52434     region : 'center',
52435     
52436     /**
52437      * @cfg {Array} items
52438      * A single item array - the first element is the root of the tree..
52439      * It's done this way to stay compatible with the Xtype system...
52440      */
52441     items : false,
52442     
52443     
52444      /**
52445      * render
52446      * render element to dom or tree
52447      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52448      */
52449     
52450     render : function(el)
52451     {
52452         
52453         el = el || false;
52454         var hp = this.parent ? 1 : 0;
52455         
52456         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52457             // if parent is a '#.....' string, then let's use that..
52458             var ename = this.parent.substr(1)
52459             this.parent = false;
52460             el = Roo.get(ename);
52461             if (!el) {
52462                 Roo.log("Warning - element can not be found :#" + ename );
52463                 return;
52464             }
52465         }
52466         
52467         
52468         if (!this.parent) {
52469             
52470             el = el ? Roo.get(el) : false;
52471             
52472             // it's a top level one..
52473             this.parent =  {
52474                 el : new Roo.BorderLayout(el || document.body, {
52475                 
52476                      center: {
52477                          titlebar: false,
52478                          autoScroll:false,
52479                          closeOnTab: true,
52480                          tabPosition: 'top',
52481                           //resizeTabs: true,
52482                          alwaysShowTabs: el && hp? false :  true,
52483                          hideTabs: el || !hp ? true :  false,
52484                          minTabWidth: 140
52485                      }
52486                  })
52487             }
52488         }
52489         
52490         
52491             
52492         var tree = this.tree();
52493         tree.region = tree.region || this.region;
52494         this.el = this.parent.el.addxtype(tree);
52495         this.fireEvent('built', this);
52496         
52497         this.panel = this.el;
52498         this.layout = this.panel.layout;    
52499          
52500     }
52501     
52502 });
52503
52504 Roo.apply(Roo.XComponent, {
52505     
52506     /**
52507      * @property  buildCompleted
52508      * True when the builder has completed building the interface.
52509      * @type Boolean
52510      */
52511     buildCompleted : false,
52512      
52513     /**
52514      * @property  topModule
52515      * the upper most module - uses document.element as it's constructor.
52516      * @type Object
52517      */
52518      
52519     topModule  : false,
52520       
52521     /**
52522      * @property  modules
52523      * array of modules to be created by registration system.
52524      * @type {Array} of Roo.XComponent
52525      */
52526     
52527     modules : [],
52528     /**
52529      * @property  elmodules
52530      * array of modules to be created by which use #ID 
52531      * @type {Array} of Roo.XComponent
52532      */
52533      
52534     elmodules : [],
52535
52536     
52537     /**
52538      * Register components to be built later.
52539      *
52540      * This solves the following issues
52541      * - Building is not done on page load, but after an authentication process has occured.
52542      * - Interface elements are registered on page load
52543      * - Parent Interface elements may not be loaded before child, so this handles that..
52544      * 
52545      *
52546      * example:
52547      * 
52548      * MyApp.register({
52549           order : '000001',
52550           module : 'Pman.Tab.projectMgr',
52551           region : 'center',
52552           parent : 'Pman.layout',
52553           disabled : false,  // or use a function..
52554         })
52555      
52556      * * @param {Object} details about module
52557      */
52558     register : function(obj) {
52559         this.modules.push(obj);
52560          
52561     },
52562     /**
52563      * convert a string to an object..
52564      * eg. 'AAA.BBB' -> finds AAA.BBB
52565
52566      */
52567     
52568     toObject : function(str)
52569     {
52570         if (!str || typeof(str) == 'object') {
52571             return str;
52572         }
52573         if (str.substring(0,1) == '#') {
52574             return str;
52575         }
52576
52577         var ar = str.split('.');
52578         var rt, o;
52579         rt = ar.shift();
52580             /** eval:var:o */
52581         try {
52582             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52583         } catch (e) {
52584             throw "Module not found : " + str;
52585         }
52586         
52587         if (o === false) {
52588             throw "Module not found : " + str;
52589         }
52590         Roo.each(ar, function(e) {
52591             if (typeof(o[e]) == 'undefined') {
52592                 throw "Module not found : " + str;
52593             }
52594             o = o[e];
52595         });
52596         
52597         return o;
52598         
52599     },
52600     
52601     
52602     /**
52603      * move modules into their correct place in the tree..
52604      * 
52605      */
52606     preBuild : function ()
52607     {
52608         var _t = this;
52609         Roo.each(this.modules , function (obj)
52610         {
52611             var opar = obj.parent;
52612             try { 
52613                 obj.parent = this.toObject(opar);
52614             } catch(e) {
52615                 Roo.log(e.toString());
52616                 return;
52617             }
52618             
52619             if (!obj.parent) {
52620                 this.topModule = obj;
52621                 return;
52622             }
52623             if (typeof(obj.parent) == 'string') {
52624                 this.elmodules.push(obj);
52625                 return;
52626             }
52627             if (obj.parent.constructor != Roo.XComponent) {
52628                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52629             }
52630             if (!obj.parent.modules) {
52631                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52632                     function(o) { return o.order + '' }
52633                 );
52634             }
52635             
52636             obj.parent.modules.add(obj);
52637         }, this);
52638     },
52639     
52640      /**
52641      * make a list of modules to build.
52642      * @return {Array} list of modules. 
52643      */ 
52644     
52645     buildOrder : function()
52646     {
52647         var _this = this;
52648         var cmp = function(a,b) {   
52649             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52650         };
52651         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52652             throw "No top level modules to build";
52653         }
52654         
52655         // make a flat list in order of modules to build.
52656         var mods = this.topModule ? [ this.topModule ] : [];
52657         Roo.each(this.elmodules,function(e) { mods.push(e) });
52658
52659         
52660         // add modules to their parents..
52661         var addMod = function(m) {
52662            // Roo.debug && Roo.log(m.modKey);
52663             
52664             mods.push(m);
52665             if (m.modules) {
52666                 m.modules.keySort('ASC',  cmp );
52667                 m.modules.each(addMod);
52668             }
52669             // not sure if this is used any more..
52670             if (m.finalize) {
52671                 m.finalize.name = m.name + " (clean up) ";
52672                 mods.push(m.finalize);
52673             }
52674             
52675         }
52676         if (this.topModule) { 
52677             this.topModule.modules.keySort('ASC',  cmp );
52678             this.topModule.modules.each(addMod);
52679         }
52680         return mods;
52681     },
52682     
52683      /**
52684      * Build the registered modules.
52685      * @param {Object} parent element.
52686      * @param {Function} optional method to call after module has been added.
52687      * 
52688      */ 
52689    
52690     build : function() 
52691     {
52692         
52693         this.preBuild();
52694         var mods = this.buildOrder();
52695       
52696         //this.allmods = mods;
52697         //Roo.debug && Roo.log(mods);
52698         //return;
52699         if (!mods.length) { // should not happen
52700             throw "NO modules!!!";
52701         }
52702         
52703         
52704         
52705         // flash it up as modal - so we store the mask!?
52706         Roo.MessageBox.show({ title: 'loading' });
52707         Roo.MessageBox.show({
52708            title: "Please wait...",
52709            msg: "Building Interface...",
52710            width:450,
52711            progress:true,
52712            closable:false,
52713            modal: false
52714           
52715         });
52716         var total = mods.length;
52717         
52718         var _this = this;
52719         var progressRun = function() {
52720             if (!mods.length) {
52721                 Roo.debug && Roo.log('hide?');
52722                 Roo.MessageBox.hide();
52723                 if (_this.topModule) { 
52724                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52725                 }
52726                 // THE END...
52727                 return false;   
52728             }
52729             
52730             var m = mods.shift();
52731             
52732             
52733             Roo.debug && Roo.log(m);
52734             // not sure if this is supported any more.. - modules that are are just function
52735             if (typeof(m) == 'function') { 
52736                 m.call(this);
52737                 return progressRun.defer(10, _this);
52738             } 
52739             
52740             
52741             
52742             Roo.MessageBox.updateProgress(
52743                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52744                     " of " + total + 
52745                     (m.name ? (' - ' + m.name) : '')
52746                     );
52747             
52748          
52749             // is the module disabled?
52750             var disabled = (typeof(m.disabled) == 'function') ?
52751                 m.disabled.call(m.module.disabled) : m.disabled;    
52752             
52753             
52754             if (disabled) {
52755                 return progressRun(); // we do not update the display!
52756             }
52757             
52758             // now build 
52759             
52760             m.render();
52761             // it's 10 on top level, and 1 on others??? why...
52762             return progressRun.defer(10, _this);
52763              
52764         }
52765         progressRun.defer(1, _this);
52766      
52767         
52768         
52769     }
52770     
52771      
52772    
52773     
52774     
52775 });
52776  //<script type="text/javascript">
52777
52778
52779 /**
52780  * @class Roo.Login
52781  * @extends Roo.LayoutDialog
52782  * A generic Login Dialog..... - only one needed in theory!?!?
52783  *
52784  * Fires XComponent builder on success...
52785  * 
52786  * Sends 
52787  *    username,password, lang = for login actions.
52788  *    check = 1 for periodic checking that sesion is valid.
52789  *    passwordRequest = email request password
52790  *    logout = 1 = to logout
52791  * 
52792  * Affects: (this id="????" elements)
52793  *   loading  (removed) (used to indicate application is loading)
52794  *   loading-mask (hides) (used to hide application when it's building loading)
52795  *   
52796  * 
52797  * Usage: 
52798  *    
52799  * 
52800  * Myapp.login = Roo.Login({
52801      url: xxxx,
52802    
52803      realm : 'Myapp', 
52804      
52805      
52806      method : 'POST',
52807      
52808      
52809      * 
52810  })
52811  * 
52812  * 
52813  * 
52814  **/
52815  
52816 Roo.Login = function(cfg)
52817 {
52818     this.addEvents({
52819         'refreshed' : true
52820     });
52821     
52822     Roo.apply(this,cfg);
52823     
52824     Roo.onReady(function() {
52825         this.onLoad();
52826     }, this);
52827     // call parent..
52828     
52829    
52830     Roo.Login.superclass.constructor.call(this, this);
52831     //this.addxtype(this.items[0]);
52832     
52833     
52834 }
52835
52836
52837 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52838     
52839     /**
52840      * @cfg {String} method
52841      * Method used to query for login details.
52842      */
52843     
52844     method : 'POST',
52845     /**
52846      * @cfg {String} url
52847      * URL to query login data. - eg. baseURL + '/Login.php'
52848      */
52849     url : '',
52850     
52851     /**
52852      * @property user
52853      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52854      * @type {Object} 
52855      */
52856     user : false,
52857     /**
52858      * @property checkFails
52859      * Number of times we have attempted to get authentication check, and failed.
52860      * @type {Number} 
52861      */
52862     checkFails : 0,
52863       /**
52864      * @property intervalID
52865      * The window interval that does the constant login checking.
52866      * @type {Number} 
52867      */
52868     intervalID : 0,
52869     
52870     
52871     onLoad : function() // called on page load...
52872     {
52873         // load 
52874          
52875         if (Roo.get('loading')) { // clear any loading indicator..
52876             Roo.get('loading').remove();
52877         }
52878         
52879         //this.switchLang('en'); // set the language to english..
52880        
52881         this.check({
52882             success:  function(response, opts)  {  // check successfull...
52883             
52884                 var res = this.processResponse(response);
52885                 this.checkFails =0;
52886                 if (!res.success) { // error!
52887                     this.checkFails = 5;
52888                     //console.log('call failure');
52889                     return this.failure(response,opts);
52890                 }
52891                 
52892                 if (!res.data.id) { // id=0 == login failure.
52893                     return this.show();
52894                 }
52895                 
52896                               
52897                         //console.log(success);
52898                 this.fillAuth(res.data);   
52899                 this.checkFails =0;
52900                 Roo.XComponent.build();
52901             },
52902             failure : this.show
52903         });
52904         
52905     }, 
52906     
52907     
52908     check: function(cfg) // called every so often to refresh cookie etc..
52909     {
52910         if (cfg.again) { // could be undefined..
52911             this.checkFails++;
52912         } else {
52913             this.checkFails = 0;
52914         }
52915         var _this = this;
52916         if (this.sending) {
52917             if ( this.checkFails > 4) {
52918                 Roo.MessageBox.alert("Error",  
52919                     "Error getting authentication status. - try reloading, or wait a while", function() {
52920                         _this.sending = false;
52921                     }); 
52922                 return;
52923             }
52924             cfg.again = true;
52925             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52926             return;
52927         }
52928         this.sending = true;
52929         
52930         Roo.Ajax.request({  
52931             url: this.url,
52932             params: {
52933                 getAuthUser: true
52934             },  
52935             method: this.method,
52936             success:  cfg.success || this.success,
52937             failure : cfg.failure || this.failure,
52938             scope : this,
52939             callCfg : cfg
52940               
52941         });  
52942     }, 
52943     
52944     
52945     logout: function()
52946     {
52947         window.onbeforeunload = function() { }; // false does not work for IE..
52948         this.user = false;
52949         var _this = this;
52950         
52951         Roo.Ajax.request({  
52952             url: this.url,
52953             params: {
52954                 logout: 1
52955             },  
52956             method: 'GET',
52957             failure : function() {
52958                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52959                     document.location = document.location.toString() + '?ts=' + Math.random();
52960                 });
52961                 
52962             },
52963             success : function() {
52964                 _this.user = false;
52965                 this.checkFails =0;
52966                 // fixme..
52967                 document.location = document.location.toString() + '?ts=' + Math.random();
52968             }
52969               
52970               
52971         }); 
52972     },
52973     
52974     processResponse : function (response)
52975     {
52976         var res = '';
52977         try {
52978             res = Roo.decode(response.responseText);
52979             // oops...
52980             if (typeof(res) != 'object') {
52981                 res = { success : false, errorMsg : res, errors : true };
52982             }
52983             if (typeof(res.success) == 'undefined') {
52984                 res.success = false;
52985             }
52986             
52987         } catch(e) {
52988             res = { success : false,  errorMsg : response.responseText, errors : true };
52989         }
52990         return res;
52991     },
52992     
52993     success : function(response, opts)  // check successfull...
52994     {  
52995         this.sending = false;
52996         var res = this.processResponse(response);
52997         if (!res.success) {
52998             return this.failure(response, opts);
52999         }
53000         if (!res.data || !res.data.id) {
53001             return this.failure(response,opts);
53002         }
53003         //console.log(res);
53004         this.fillAuth(res.data);
53005         
53006         this.checkFails =0;
53007         
53008     },
53009     
53010     
53011     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53012     {
53013         this.authUser = -1;
53014         this.sending = false;
53015         var res = this.processResponse(response);
53016         //console.log(res);
53017         if ( this.checkFails > 2) {
53018         
53019             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53020                 "Error getting authentication status. - try reloading"); 
53021             return;
53022         }
53023         opts.callCfg.again = true;
53024         this.check.defer(1000, this, [ opts.callCfg ]);
53025         return;  
53026     },
53027     
53028     
53029     
53030     fillAuth: function(au) {
53031         this.startAuthCheck();
53032         this.authUserId = au.id;
53033         this.authUser = au;
53034         this.lastChecked = new Date();
53035         this.fireEvent('refreshed', au);
53036         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53037         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53038         au.lang = au.lang || 'en';
53039         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53040         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53041         this.switchLang(au.lang );
53042         
53043      
53044         // open system... - -on setyp..
53045         if (this.authUserId  < 0) {
53046             Roo.MessageBox.alert("Warning", 
53047                 "This is an open system - please set up a admin user with a password.");  
53048         }
53049          
53050         //Pman.onload(); // which should do nothing if it's a re-auth result...
53051         
53052              
53053     },
53054     
53055     startAuthCheck : function() // starter for timeout checking..
53056     {
53057         if (this.intervalID) { // timer already in place...
53058             return false;
53059         }
53060         var _this = this;
53061         this.intervalID =  window.setInterval(function() {
53062               _this.check(false);
53063             }, 120000); // every 120 secs = 2mins..
53064         
53065         
53066     },
53067          
53068     
53069     switchLang : function (lang) 
53070     {
53071         _T = typeof(_T) == 'undefined' ? false : _T;
53072           if (!_T || !lang.length) {
53073             return;
53074         }
53075         
53076         if (!_T && lang != 'en') {
53077             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53078             return;
53079         }
53080         
53081         if (typeof(_T.en) == 'undefined') {
53082             _T.en = {};
53083             Roo.apply(_T.en, _T);
53084         }
53085         
53086         if (typeof(_T[lang]) == 'undefined') {
53087             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53088             return;
53089         }
53090         
53091         
53092         Roo.apply(_T, _T[lang]);
53093         // just need to set the text values for everything...
53094         var _this = this;
53095         /* this will not work ...
53096         if (this.form) { 
53097             
53098                
53099             function formLabel(name, val) {
53100                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53101             }
53102             
53103             formLabel('password', "Password"+':');
53104             formLabel('username', "Email Address"+':');
53105             formLabel('lang', "Language"+':');
53106             this.dialog.setTitle("Login");
53107             this.dialog.buttons[0].setText("Forgot Password");
53108             this.dialog.buttons[1].setText("Login");
53109         }
53110         */
53111         
53112         
53113     },
53114     
53115     
53116     title: "Login",
53117     modal: true,
53118     width:  350,
53119     //height: 230,
53120     height: 180,
53121     shadow: true,
53122     minWidth:200,
53123     minHeight:180,
53124     //proxyDrag: true,
53125     closable: false,
53126     draggable: false,
53127     collapsible: false,
53128     resizable: false,
53129     center: {  // needed??
53130         autoScroll:false,
53131         titlebar: false,
53132        // tabPosition: 'top',
53133         hideTabs: true,
53134         closeOnTab: true,
53135         alwaysShowTabs: false
53136     } ,
53137     listeners : {
53138         
53139         show  : function(dlg)
53140         {
53141             //console.log(this);
53142             this.form = this.layout.getRegion('center').activePanel.form;
53143             this.form.dialog = dlg;
53144             this.buttons[0].form = this.form;
53145             this.buttons[0].dialog = dlg;
53146             this.buttons[1].form = this.form;
53147             this.buttons[1].dialog = dlg;
53148            
53149            //this.resizeToLogo.defer(1000,this);
53150             // this is all related to resizing for logos..
53151             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53152            //// if (!sz) {
53153              //   this.resizeToLogo.defer(1000,this);
53154              //   return;
53155            // }
53156             //var w = Ext.lib.Dom.getViewWidth() - 100;
53157             //var h = Ext.lib.Dom.getViewHeight() - 100;
53158             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53159             //this.center();
53160             if (this.disabled) {
53161                 this.hide();
53162                 return;
53163             }
53164             
53165             if (this.user.id < 0) { // used for inital setup situations.
53166                 return;
53167             }
53168             
53169             if (this.intervalID) {
53170                 // remove the timer
53171                 window.clearInterval(this.intervalID);
53172                 this.intervalID = false;
53173             }
53174             
53175             
53176             if (Roo.get('loading')) {
53177                 Roo.get('loading').remove();
53178             }
53179             if (Roo.get('loading-mask')) {
53180                 Roo.get('loading-mask').hide();
53181             }
53182             
53183             //incomming._node = tnode;
53184             this.form.reset();
53185             //this.dialog.modal = !modal;
53186             //this.dialog.show();
53187             this.el.unmask(); 
53188             
53189             
53190             this.form.setValues({
53191                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53192                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53193             });
53194             
53195             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53196             if (this.form.findField('username').getValue().length > 0 ){
53197                 this.form.findField('password').focus();
53198             } else {
53199                this.form.findField('username').focus();
53200             }
53201     
53202         }
53203     },
53204     items : [
53205          {
53206        
53207             xtype : 'ContentPanel',
53208             xns : Roo,
53209             region: 'center',
53210             fitToFrame : true,
53211             
53212             items : [
53213     
53214                 {
53215                
53216                     xtype : 'Form',
53217                     xns : Roo.form,
53218                     labelWidth: 100,
53219                     style : 'margin: 10px;',
53220                     
53221                     listeners : {
53222                         actionfailed : function(f, act) {
53223                             // form can return { errors: .... }
53224                                 
53225                             //act.result.errors // invalid form element list...
53226                             //act.result.errorMsg// invalid form element list...
53227                             
53228                             this.dialog.el.unmask();
53229                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53230                                         "Login failed - communication error - try again.");
53231                                       
53232                         },
53233                         actioncomplete: function(re, act) {
53234                              
53235                             Roo.state.Manager.set(
53236                                 this.dialog.realm + '.username',  
53237                                     this.findField('username').getValue()
53238                             );
53239                             Roo.state.Manager.set(
53240                                 this.dialog.realm + '.lang',  
53241                                 this.findField('lang').getValue() 
53242                             );
53243                             
53244                             this.dialog.fillAuth(act.result.data);
53245                               
53246                             this.dialog.hide();
53247                             
53248                             if (Roo.get('loading-mask')) {
53249                                 Roo.get('loading-mask').show();
53250                             }
53251                             Roo.XComponent.build();
53252                             
53253                              
53254                             
53255                         }
53256                     },
53257                     items : [
53258                         {
53259                             xtype : 'TextField',
53260                             xns : Roo.form,
53261                             fieldLabel: "Email Address",
53262                             name: 'username',
53263                             width:200,
53264                             autoCreate : {tag: "input", type: "text", size: "20"}
53265                         },
53266                         {
53267                             xtype : 'TextField',
53268                             xns : Roo.form,
53269                             fieldLabel: "Password",
53270                             inputType: 'password',
53271                             name: 'password',
53272                             width:200,
53273                             autoCreate : {tag: "input", type: "text", size: "20"},
53274                             listeners : {
53275                                 specialkey : function(e,ev) {
53276                                     if (ev.keyCode == 13) {
53277                                         this.form.dialog.el.mask("Logging in");
53278                                         this.form.doAction('submit', {
53279                                             url: this.form.dialog.url,
53280                                             method: this.form.dialog.method
53281                                         });
53282                                     }
53283                                 }
53284                             }  
53285                         },
53286                         {
53287                             xtype : 'ComboBox',
53288                             xns : Roo.form,
53289                             fieldLabel: "Language",
53290                             name : 'langdisp',
53291                             store: {
53292                                 xtype : 'SimpleStore',
53293                                 fields: ['lang', 'ldisp'],
53294                                 data : [
53295                                     [ 'en', 'English' ],
53296                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53297                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53298                                 ]
53299                             },
53300                             
53301                             valueField : 'lang',
53302                             hiddenName:  'lang',
53303                             width: 200,
53304                             displayField:'ldisp',
53305                             typeAhead: false,
53306                             editable: false,
53307                             mode: 'local',
53308                             triggerAction: 'all',
53309                             emptyText:'Select a Language...',
53310                             selectOnFocus:true,
53311                             listeners : {
53312                                 select :  function(cb, rec, ix) {
53313                                     this.form.switchLang(rec.data.lang);
53314                                 }
53315                             }
53316                         
53317                         }
53318                     ]
53319                 }
53320                   
53321                 
53322             ]
53323         }
53324     ],
53325     buttons : [
53326         {
53327             xtype : 'Button',
53328             xns : 'Roo',
53329             text : "Forgot Password",
53330             listeners : {
53331                 click : function() {
53332                     //console.log(this);
53333                     var n = this.form.findField('username').getValue();
53334                     if (!n.length) {
53335                         Roo.MessageBox.alert("Error", "Fill in your email address");
53336                         return;
53337                     }
53338                     Roo.Ajax.request({
53339                         url: this.dialog.url,
53340                         params: {
53341                             passwordRequest: n
53342                         },
53343                         method: this.dialog.method,
53344                         success:  function(response, opts)  {  // check successfull...
53345                         
53346                             var res = this.dialog.processResponse(response);
53347                             if (!res.success) { // error!
53348                                Roo.MessageBox.alert("Error" ,
53349                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53350                                return;
53351                             }
53352                             Roo.MessageBox.alert("Notice" ,
53353                                 "Please check you email for the Password Reset message");
53354                         },
53355                         failure : function() {
53356                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53357                         }
53358                         
53359                     });
53360                 }
53361             }
53362         },
53363         {
53364             xtype : 'Button',
53365             xns : 'Roo',
53366             text : "Login",
53367             listeners : {
53368                 
53369                 click : function () {
53370                         
53371                     this.dialog.el.mask("Logging in");
53372                     this.form.doAction('submit', {
53373                             url: this.dialog.url,
53374                             method: this.dialog.method
53375                     });
53376                 }
53377             }
53378         }
53379     ]
53380   
53381   
53382 })
53383  
53384
53385
53386