roojs-core.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             var p = this.getPositioning();
8874             if(typeof msg == 'string'){
8875                 if(!this._maskMsg){
8876                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8877                 }
8878                 var mm = this._maskMsg;
8879                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8880                 mm.dom.firstChild.innerHTML = msg;
8881                 mm.setDisplayed(true);
8882                 mm.center(this);
8883                 mm.setStyle('z-index', parseInt(p['z-index']) + 102);
8884             }
8885             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8886                 this._mask.setHeight(this.getHeight());
8887             }
8888             this._mask.setStyle('z-index', parseInt(p['z-index']) + 100);
8889             
8890             return this._mask;
8891         },
8892
8893         /**
8894          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8895          * it is cached for reuse.
8896          */
8897         unmask : function(removeEl){
8898             if(this._mask){
8899                 if(removeEl === true){
8900                     this._mask.remove();
8901                     delete this._mask;
8902                     if(this._maskMsg){
8903                         this._maskMsg.remove();
8904                         delete this._maskMsg;
8905                     }
8906                 }else{
8907                     this._mask.setDisplayed(false);
8908                     if(this._maskMsg){
8909                         this._maskMsg.setDisplayed(false);
8910                     }
8911                 }
8912             }
8913             this.removeClass("x-masked");
8914         },
8915
8916         /**
8917          * Returns true if this element is masked
8918          * @return {Boolean}
8919          */
8920         isMasked : function(){
8921             return this._mask && this._mask.isVisible();
8922         },
8923
8924         /**
8925          * Creates an iframe shim for this element to keep selects and other windowed objects from
8926          * showing through.
8927          * @return {Roo.Element} The new shim element
8928          */
8929         createShim : function(){
8930             var el = document.createElement('iframe');
8931             el.frameBorder = 'no';
8932             el.className = 'roo-shim';
8933             if(Roo.isIE && Roo.isSecure){
8934                 el.src = Roo.SSL_SECURE_URL;
8935             }
8936             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8937             shim.autoBoxAdjust = false;
8938             return shim;
8939         },
8940
8941         /**
8942          * Removes this element from the DOM and deletes it from the cache
8943          */
8944         remove : function(){
8945             if(this.dom.parentNode){
8946                 this.dom.parentNode.removeChild(this.dom);
8947             }
8948             delete El.cache[this.dom.id];
8949         },
8950
8951         /**
8952          * Sets up event handlers to add and remove a css class when the mouse is over this element
8953          * @param {String} className
8954          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8955          * mouseout events for children elements
8956          * @return {Roo.Element} this
8957          */
8958         addClassOnOver : function(className, preventFlicker){
8959             this.on("mouseover", function(){
8960                 Roo.fly(this, '_internal').addClass(className);
8961             }, this.dom);
8962             var removeFn = function(e){
8963                 if(preventFlicker !== true || !e.within(this, true)){
8964                     Roo.fly(this, '_internal').removeClass(className);
8965                 }
8966             };
8967             this.on("mouseout", removeFn, this.dom);
8968             return this;
8969         },
8970
8971         /**
8972          * Sets up event handlers to add and remove a css class when this element has the focus
8973          * @param {String} className
8974          * @return {Roo.Element} this
8975          */
8976         addClassOnFocus : function(className){
8977             this.on("focus", function(){
8978                 Roo.fly(this, '_internal').addClass(className);
8979             }, this.dom);
8980             this.on("blur", function(){
8981                 Roo.fly(this, '_internal').removeClass(className);
8982             }, this.dom);
8983             return this;
8984         },
8985         /**
8986          * 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)
8987          * @param {String} className
8988          * @return {Roo.Element} this
8989          */
8990         addClassOnClick : function(className){
8991             var dom = this.dom;
8992             this.on("mousedown", function(){
8993                 Roo.fly(dom, '_internal').addClass(className);
8994                 var d = Roo.get(document);
8995                 var fn = function(){
8996                     Roo.fly(dom, '_internal').removeClass(className);
8997                     d.removeListener("mouseup", fn);
8998                 };
8999                 d.on("mouseup", fn);
9000             });
9001             return this;
9002         },
9003
9004         /**
9005          * Stops the specified event from bubbling and optionally prevents the default action
9006          * @param {String} eventName
9007          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9008          * @return {Roo.Element} this
9009          */
9010         swallowEvent : function(eventName, preventDefault){
9011             var fn = function(e){
9012                 e.stopPropagation();
9013                 if(preventDefault){
9014                     e.preventDefault();
9015                 }
9016             };
9017             if(eventName instanceof Array){
9018                 for(var i = 0, len = eventName.length; i < len; i++){
9019                      this.on(eventName[i], fn);
9020                 }
9021                 return this;
9022             }
9023             this.on(eventName, fn);
9024             return this;
9025         },
9026
9027         /**
9028          * @private
9029          */
9030       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9031
9032         /**
9033          * Sizes this element to its parent element's dimensions performing
9034          * neccessary box adjustments.
9035          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9036          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9037          * @return {Roo.Element} this
9038          */
9039         fitToParent : function(monitorResize, targetParent) {
9040           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9041           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9042           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9043             return;
9044           }
9045           var p = Roo.get(targetParent || this.dom.parentNode);
9046           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9047           if (monitorResize === true) {
9048             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9049             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9050           }
9051           return this;
9052         },
9053
9054         /**
9055          * Gets the next sibling, skipping text nodes
9056          * @return {HTMLElement} The next sibling or null
9057          */
9058         getNextSibling : function(){
9059             var n = this.dom.nextSibling;
9060             while(n && n.nodeType != 1){
9061                 n = n.nextSibling;
9062             }
9063             return n;
9064         },
9065
9066         /**
9067          * Gets the previous sibling, skipping text nodes
9068          * @return {HTMLElement} The previous sibling or null
9069          */
9070         getPrevSibling : function(){
9071             var n = this.dom.previousSibling;
9072             while(n && n.nodeType != 1){
9073                 n = n.previousSibling;
9074             }
9075             return n;
9076         },
9077
9078
9079         /**
9080          * Appends the passed element(s) to this element
9081          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9082          * @return {Roo.Element} this
9083          */
9084         appendChild: function(el){
9085             el = Roo.get(el);
9086             el.appendTo(this);
9087             return this;
9088         },
9089
9090         /**
9091          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9092          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9093          * automatically generated with the specified attributes.
9094          * @param {HTMLElement} insertBefore (optional) a child element of this element
9095          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9096          * @return {Roo.Element} The new child element
9097          */
9098         createChild: function(config, insertBefore, returnDom){
9099             config = config || {tag:'div'};
9100             if(insertBefore){
9101                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9102             }
9103             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9104         },
9105
9106         /**
9107          * Appends this element to the passed element
9108          * @param {String/HTMLElement/Element} el The new parent element
9109          * @return {Roo.Element} this
9110          */
9111         appendTo: function(el){
9112             el = Roo.getDom(el);
9113             el.appendChild(this.dom);
9114             return this;
9115         },
9116
9117         /**
9118          * Inserts this element before the passed element in the DOM
9119          * @param {String/HTMLElement/Element} el The element to insert before
9120          * @return {Roo.Element} this
9121          */
9122         insertBefore: function(el){
9123             el = Roo.getDom(el);
9124             el.parentNode.insertBefore(this.dom, el);
9125             return this;
9126         },
9127
9128         /**
9129          * Inserts this element after the passed element in the DOM
9130          * @param {String/HTMLElement/Element} el The element to insert after
9131          * @return {Roo.Element} this
9132          */
9133         insertAfter: function(el){
9134             el = Roo.getDom(el);
9135             el.parentNode.insertBefore(this.dom, el.nextSibling);
9136             return this;
9137         },
9138
9139         /**
9140          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9141          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9142          * @return {Roo.Element} The new child
9143          */
9144         insertFirst: function(el, returnDom){
9145             el = el || {};
9146             if(typeof el == 'object' && !el.nodeType){ // dh config
9147                 return this.createChild(el, this.dom.firstChild, returnDom);
9148             }else{
9149                 el = Roo.getDom(el);
9150                 this.dom.insertBefore(el, this.dom.firstChild);
9151                 return !returnDom ? Roo.get(el) : el;
9152             }
9153         },
9154
9155         /**
9156          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9157          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9158          * @param {String} where (optional) 'before' or 'after' defaults to before
9159          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9160          * @return {Roo.Element} the inserted Element
9161          */
9162         insertSibling: function(el, where, returnDom){
9163             where = where ? where.toLowerCase() : 'before';
9164             el = el || {};
9165             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9166
9167             if(typeof el == 'object' && !el.nodeType){ // dh config
9168                 if(where == 'after' && !this.dom.nextSibling){
9169                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9170                 }else{
9171                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9172                 }
9173
9174             }else{
9175                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9176                             where == 'before' ? this.dom : this.dom.nextSibling);
9177                 if(!returnDom){
9178                     rt = Roo.get(rt);
9179                 }
9180             }
9181             return rt;
9182         },
9183
9184         /**
9185          * Creates and wraps this element with another element
9186          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9187          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9188          * @return {HTMLElement/Element} The newly created wrapper element
9189          */
9190         wrap: function(config, returnDom){
9191             if(!config){
9192                 config = {tag: "div"};
9193             }
9194             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9195             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9196             return newEl;
9197         },
9198
9199         /**
9200          * Replaces the passed element with this element
9201          * @param {String/HTMLElement/Element} el The element to replace
9202          * @return {Roo.Element} this
9203          */
9204         replace: function(el){
9205             el = Roo.get(el);
9206             this.insertBefore(el);
9207             el.remove();
9208             return this;
9209         },
9210
9211         /**
9212          * Inserts an html fragment into this element
9213          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9214          * @param {String} html The HTML fragment
9215          * @param {Boolean} returnEl True to return an Roo.Element
9216          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9217          */
9218         insertHtml : function(where, html, returnEl){
9219             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9220             return returnEl ? Roo.get(el) : el;
9221         },
9222
9223         /**
9224          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9225          * @param {Object} o The object with the attributes
9226          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9227          * @return {Roo.Element} this
9228          */
9229         set : function(o, useSet){
9230             var el = this.dom;
9231             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9232             for(var attr in o){
9233                 if(attr == "style" || typeof o[attr] == "function") continue;
9234                 if(attr=="cls"){
9235                     el.className = o["cls"];
9236                 }else{
9237                     if(useSet) el.setAttribute(attr, o[attr]);
9238                     else el[attr] = o[attr];
9239                 }
9240             }
9241             if(o.style){
9242                 Roo.DomHelper.applyStyles(el, o.style);
9243             }
9244             return this;
9245         },
9246
9247         /**
9248          * Convenience method for constructing a KeyMap
9249          * @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:
9250          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9251          * @param {Function} fn The function to call
9252          * @param {Object} scope (optional) The scope of the function
9253          * @return {Roo.KeyMap} The KeyMap created
9254          */
9255         addKeyListener : function(key, fn, scope){
9256             var config;
9257             if(typeof key != "object" || key instanceof Array){
9258                 config = {
9259                     key: key,
9260                     fn: fn,
9261                     scope: scope
9262                 };
9263             }else{
9264                 config = {
9265                     key : key.key,
9266                     shift : key.shift,
9267                     ctrl : key.ctrl,
9268                     alt : key.alt,
9269                     fn: fn,
9270                     scope: scope
9271                 };
9272             }
9273             return new Roo.KeyMap(this, config);
9274         },
9275
9276         /**
9277          * Creates a KeyMap for this element
9278          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9279          * @return {Roo.KeyMap} The KeyMap created
9280          */
9281         addKeyMap : function(config){
9282             return new Roo.KeyMap(this, config);
9283         },
9284
9285         /**
9286          * Returns true if this element is scrollable.
9287          * @return {Boolean}
9288          */
9289          isScrollable : function(){
9290             var dom = this.dom;
9291             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9292         },
9293
9294         /**
9295          * 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().
9296          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9297          * @param {Number} value The new scroll value
9298          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9299          * @return {Element} this
9300          */
9301
9302         scrollTo : function(side, value, animate){
9303             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9304             if(!animate || !A){
9305                 this.dom[prop] = value;
9306             }else{
9307                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9308                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9309             }
9310             return this;
9311         },
9312
9313         /**
9314          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9315          * within this element's scrollable range.
9316          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9317          * @param {Number} distance How far to scroll the element in pixels
9318          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9319          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9320          * was scrolled as far as it could go.
9321          */
9322          scroll : function(direction, distance, animate){
9323              if(!this.isScrollable()){
9324                  return;
9325              }
9326              var el = this.dom;
9327              var l = el.scrollLeft, t = el.scrollTop;
9328              var w = el.scrollWidth, h = el.scrollHeight;
9329              var cw = el.clientWidth, ch = el.clientHeight;
9330              direction = direction.toLowerCase();
9331              var scrolled = false;
9332              var a = this.preanim(arguments, 2);
9333              switch(direction){
9334                  case "l":
9335                  case "left":
9336                      if(w - l > cw){
9337                          var v = Math.min(l + distance, w-cw);
9338                          this.scrollTo("left", v, a);
9339                          scrolled = true;
9340                      }
9341                      break;
9342                 case "r":
9343                 case "right":
9344                      if(l > 0){
9345                          var v = Math.max(l - distance, 0);
9346                          this.scrollTo("left", v, a);
9347                          scrolled = true;
9348                      }
9349                      break;
9350                 case "t":
9351                 case "top":
9352                 case "up":
9353                      if(t > 0){
9354                          var v = Math.max(t - distance, 0);
9355                          this.scrollTo("top", v, a);
9356                          scrolled = true;
9357                      }
9358                      break;
9359                 case "b":
9360                 case "bottom":
9361                 case "down":
9362                      if(h - t > ch){
9363                          var v = Math.min(t + distance, h-ch);
9364                          this.scrollTo("top", v, a);
9365                          scrolled = true;
9366                      }
9367                      break;
9368              }
9369              return scrolled;
9370         },
9371
9372         /**
9373          * Translates the passed page coordinates into left/top css values for this element
9374          * @param {Number/Array} x The page x or an array containing [x, y]
9375          * @param {Number} y The page y
9376          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9377          */
9378         translatePoints : function(x, y){
9379             if(typeof x == 'object' || x instanceof Array){
9380                 y = x[1]; x = x[0];
9381             }
9382             var p = this.getStyle('position');
9383             var o = this.getXY();
9384
9385             var l = parseInt(this.getStyle('left'), 10);
9386             var t = parseInt(this.getStyle('top'), 10);
9387
9388             if(isNaN(l)){
9389                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9390             }
9391             if(isNaN(t)){
9392                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9393             }
9394
9395             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9396         },
9397
9398         /**
9399          * Returns the current scroll position of the element.
9400          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9401          */
9402         getScroll : function(){
9403             var d = this.dom, doc = document;
9404             if(d == doc || d == doc.body){
9405                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9406                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9407                 return {left: l, top: t};
9408             }else{
9409                 return {left: d.scrollLeft, top: d.scrollTop};
9410             }
9411         },
9412
9413         /**
9414          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9415          * are convert to standard 6 digit hex color.
9416          * @param {String} attr The css attribute
9417          * @param {String} defaultValue The default value to use when a valid color isn't found
9418          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9419          * YUI color anims.
9420          */
9421         getColor : function(attr, defaultValue, prefix){
9422             var v = this.getStyle(attr);
9423             if(!v || v == "transparent" || v == "inherit") {
9424                 return defaultValue;
9425             }
9426             var color = typeof prefix == "undefined" ? "#" : prefix;
9427             if(v.substr(0, 4) == "rgb("){
9428                 var rvs = v.slice(4, v.length -1).split(",");
9429                 for(var i = 0; i < 3; i++){
9430                     var h = parseInt(rvs[i]).toString(16);
9431                     if(h < 16){
9432                         h = "0" + h;
9433                     }
9434                     color += h;
9435                 }
9436             } else {
9437                 if(v.substr(0, 1) == "#"){
9438                     if(v.length == 4) {
9439                         for(var i = 1; i < 4; i++){
9440                             var c = v.charAt(i);
9441                             color +=  c + c;
9442                         }
9443                     }else if(v.length == 7){
9444                         color += v.substr(1);
9445                     }
9446                 }
9447             }
9448             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9449         },
9450
9451         /**
9452          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9453          * gradient background, rounded corners and a 4-way shadow.
9454          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9455          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9456          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9457          * @return {Roo.Element} this
9458          */
9459         boxWrap : function(cls){
9460             cls = cls || 'x-box';
9461             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9462             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9463             return el;
9464         },
9465
9466         /**
9467          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9468          * @param {String} namespace The namespace in which to look for the attribute
9469          * @param {String} name The attribute name
9470          * @return {String} The attribute value
9471          */
9472         getAttributeNS : Roo.isIE ? function(ns, name){
9473             var d = this.dom;
9474             var type = typeof d[ns+":"+name];
9475             if(type != 'undefined' && type != 'unknown'){
9476                 return d[ns+":"+name];
9477             }
9478             return d[name];
9479         } : function(ns, name){
9480             var d = this.dom;
9481             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9482         }
9483     };
9484
9485     var ep = El.prototype;
9486
9487     /**
9488      * Appends an event handler (Shorthand for addListener)
9489      * @param {String}   eventName     The type of event to append
9490      * @param {Function} fn        The method the event invokes
9491      * @param {Object} scope       (optional) The scope (this object) of the fn
9492      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9493      * @method
9494      */
9495     ep.on = ep.addListener;
9496         // backwards compat
9497     ep.mon = ep.addListener;
9498
9499     /**
9500      * Removes an event handler from this element (shorthand for removeListener)
9501      * @param {String} eventName the type of event to remove
9502      * @param {Function} fn the method the event invokes
9503      * @return {Roo.Element} this
9504      * @method
9505      */
9506     ep.un = ep.removeListener;
9507
9508     /**
9509      * true to automatically adjust width and height settings for box-model issues (default to true)
9510      */
9511     ep.autoBoxAdjust = true;
9512
9513     // private
9514     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9515
9516     // private
9517     El.addUnits = function(v, defaultUnit){
9518         if(v === "" || v == "auto"){
9519             return v;
9520         }
9521         if(v === undefined){
9522             return '';
9523         }
9524         if(typeof v == "number" || !El.unitPattern.test(v)){
9525             return v + (defaultUnit || 'px');
9526         }
9527         return v;
9528     };
9529
9530     // special markup used throughout Roo when box wrapping elements
9531     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>';
9532     /**
9533      * Visibility mode constant - Use visibility to hide element
9534      * @static
9535      * @type Number
9536      */
9537     El.VISIBILITY = 1;
9538     /**
9539      * Visibility mode constant - Use display to hide element
9540      * @static
9541      * @type Number
9542      */
9543     El.DISPLAY = 2;
9544
9545     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9546     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9547     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9548
9549
9550
9551     /**
9552      * @private
9553      */
9554     El.cache = {};
9555
9556     var docEl;
9557
9558     /**
9559      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9560      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9561      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9562      * @return {Element} The Element object
9563      * @static
9564      */
9565     El.get = function(el){
9566         var ex, elm, id;
9567         if(!el){ return null; }
9568         if(typeof el == "string"){ // element id
9569             if(!(elm = document.getElementById(el))){
9570                 return null;
9571             }
9572             if(ex = El.cache[el]){
9573                 ex.dom = elm;
9574             }else{
9575                 ex = El.cache[el] = new El(elm);
9576             }
9577             return ex;
9578         }else if(el.tagName){ // dom element
9579             if(!(id = el.id)){
9580                 id = Roo.id(el);
9581             }
9582             if(ex = El.cache[id]){
9583                 ex.dom = el;
9584             }else{
9585                 ex = El.cache[id] = new El(el);
9586             }
9587             return ex;
9588         }else if(el instanceof El){
9589             if(el != docEl){
9590                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9591                                                               // catch case where it hasn't been appended
9592                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9593             }
9594             return el;
9595         }else if(el.isComposite){
9596             return el;
9597         }else if(el instanceof Array){
9598             return El.select(el);
9599         }else if(el == document){
9600             // create a bogus element object representing the document object
9601             if(!docEl){
9602                 var f = function(){};
9603                 f.prototype = El.prototype;
9604                 docEl = new f();
9605                 docEl.dom = document;
9606             }
9607             return docEl;
9608         }
9609         return null;
9610     };
9611
9612     // private
9613     El.uncache = function(el){
9614         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9615             if(a[i]){
9616                 delete El.cache[a[i].id || a[i]];
9617             }
9618         }
9619     };
9620
9621     // private
9622     // Garbage collection - uncache elements/purge listeners on orphaned elements
9623     // so we don't hold a reference and cause the browser to retain them
9624     El.garbageCollect = function(){
9625         if(!Roo.enableGarbageCollector){
9626             clearInterval(El.collectorThread);
9627             return;
9628         }
9629         for(var eid in El.cache){
9630             var el = El.cache[eid], d = el.dom;
9631             // -------------------------------------------------------
9632             // Determining what is garbage:
9633             // -------------------------------------------------------
9634             // !d
9635             // dom node is null, definitely garbage
9636             // -------------------------------------------------------
9637             // !d.parentNode
9638             // no parentNode == direct orphan, definitely garbage
9639             // -------------------------------------------------------
9640             // !d.offsetParent && !document.getElementById(eid)
9641             // display none elements have no offsetParent so we will
9642             // also try to look it up by it's id. However, check
9643             // offsetParent first so we don't do unneeded lookups.
9644             // This enables collection of elements that are not orphans
9645             // directly, but somewhere up the line they have an orphan
9646             // parent.
9647             // -------------------------------------------------------
9648             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9649                 delete El.cache[eid];
9650                 if(d && Roo.enableListenerCollection){
9651                     E.purgeElement(d);
9652                 }
9653             }
9654         }
9655     }
9656     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9657
9658
9659     // dom is optional
9660     El.Flyweight = function(dom){
9661         this.dom = dom;
9662     };
9663     El.Flyweight.prototype = El.prototype;
9664
9665     El._flyweights = {};
9666     /**
9667      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9668      * the dom node can be overwritten by other code.
9669      * @param {String/HTMLElement} el The dom node or id
9670      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9671      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9672      * @static
9673      * @return {Element} The shared Element object
9674      */
9675     El.fly = function(el, named){
9676         named = named || '_global';
9677         el = Roo.getDom(el);
9678         if(!el){
9679             return null;
9680         }
9681         if(!El._flyweights[named]){
9682             El._flyweights[named] = new El.Flyweight();
9683         }
9684         El._flyweights[named].dom = el;
9685         return El._flyweights[named];
9686     };
9687
9688     /**
9689      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9690      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9691      * Shorthand of {@link Roo.Element#get}
9692      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9693      * @return {Element} The Element object
9694      * @member Roo
9695      * @method get
9696      */
9697     Roo.get = El.get;
9698     /**
9699      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9700      * the dom node can be overwritten by other code.
9701      * Shorthand of {@link Roo.Element#fly}
9702      * @param {String/HTMLElement} el The dom node or id
9703      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9704      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9705      * @static
9706      * @return {Element} The shared Element object
9707      * @member Roo
9708      * @method fly
9709      */
9710     Roo.fly = El.fly;
9711
9712     // speedy lookup for elements never to box adjust
9713     var noBoxAdjust = Roo.isStrict ? {
9714         select:1
9715     } : {
9716         input:1, select:1, textarea:1
9717     };
9718     if(Roo.isIE || Roo.isGecko){
9719         noBoxAdjust['button'] = 1;
9720     }
9721
9722
9723     Roo.EventManager.on(window, 'unload', function(){
9724         delete El.cache;
9725         delete El._flyweights;
9726     });
9727 })();
9728
9729
9730
9731
9732 if(Roo.DomQuery){
9733     Roo.Element.selectorFunction = Roo.DomQuery.select;
9734 }
9735
9736 Roo.Element.select = function(selector, unique, root){
9737     var els;
9738     if(typeof selector == "string"){
9739         els = Roo.Element.selectorFunction(selector, root);
9740     }else if(selector.length !== undefined){
9741         els = selector;
9742     }else{
9743         throw "Invalid selector";
9744     }
9745     if(unique === true){
9746         return new Roo.CompositeElement(els);
9747     }else{
9748         return new Roo.CompositeElementLite(els);
9749     }
9750 };
9751 /**
9752  * Selects elements based on the passed CSS selector to enable working on them as 1.
9753  * @param {String/Array} selector The CSS selector or an array of elements
9754  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9755  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9756  * @return {CompositeElementLite/CompositeElement}
9757  * @member Roo
9758  * @method select
9759  */
9760 Roo.select = Roo.Element.select;
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774
9775 /*
9776  * Based on:
9777  * Ext JS Library 1.1.1
9778  * Copyright(c) 2006-2007, Ext JS, LLC.
9779  *
9780  * Originally Released Under LGPL - original licence link has changed is not relivant.
9781  *
9782  * Fork - LGPL
9783  * <script type="text/javascript">
9784  */
9785
9786
9787
9788 //Notifies Element that fx methods are available
9789 Roo.enableFx = true;
9790
9791 /**
9792  * @class Roo.Fx
9793  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9794  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9795  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9796  * Element effects to work.</p><br/>
9797  *
9798  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9799  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9800  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9801  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9802  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9803  * expected results and should be done with care.</p><br/>
9804  *
9805  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9806  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9807 <pre>
9808 Value  Description
9809 -----  -----------------------------
9810 tl     The top left corner
9811 t      The center of the top edge
9812 tr     The top right corner
9813 l      The center of the left edge
9814 r      The center of the right edge
9815 bl     The bottom left corner
9816 b      The center of the bottom edge
9817 br     The bottom right corner
9818 </pre>
9819  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9820  * below are common options that can be passed to any Fx method.</b>
9821  * @cfg {Function} callback A function called when the effect is finished
9822  * @cfg {Object} scope The scope of the effect function
9823  * @cfg {String} easing A valid Easing value for the effect
9824  * @cfg {String} afterCls A css class to apply after the effect
9825  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9826  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9827  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9828  * effects that end with the element being visually hidden, ignored otherwise)
9829  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9830  * a function which returns such a specification that will be applied to the Element after the effect finishes
9831  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9832  * @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
9833  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9834  */
9835 Roo.Fx = {
9836         /**
9837          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9838          * origin for the slide effect.  This function automatically handles wrapping the element with
9839          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9840          * Usage:
9841          *<pre><code>
9842 // default: slide the element in from the top
9843 el.slideIn();
9844
9845 // custom: slide the element in from the right with a 2-second duration
9846 el.slideIn('r', { duration: 2 });
9847
9848 // common config options shown with default values
9849 el.slideIn('t', {
9850     easing: 'easeOut',
9851     duration: .5
9852 });
9853 </code></pre>
9854          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9855          * @param {Object} options (optional) Object literal with any of the Fx config options
9856          * @return {Roo.Element} The Element
9857          */
9858     slideIn : function(anchor, o){
9859         var el = this.getFxEl();
9860         o = o || {};
9861
9862         el.queueFx(o, function(){
9863
9864             anchor = anchor || "t";
9865
9866             // fix display to visibility
9867             this.fixDisplay();
9868
9869             // restore values after effect
9870             var r = this.getFxRestore();
9871             var b = this.getBox();
9872             // fixed size for slide
9873             this.setSize(b);
9874
9875             // wrap if needed
9876             var wrap = this.fxWrap(r.pos, o, "hidden");
9877
9878             var st = this.dom.style;
9879             st.visibility = "visible";
9880             st.position = "absolute";
9881
9882             // clear out temp styles after slide and unwrap
9883             var after = function(){
9884                 el.fxUnwrap(wrap, r.pos, o);
9885                 st.width = r.width;
9886                 st.height = r.height;
9887                 el.afterFx(o);
9888             };
9889             // time to calc the positions
9890             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9891
9892             switch(anchor.toLowerCase()){
9893                 case "t":
9894                     wrap.setSize(b.width, 0);
9895                     st.left = st.bottom = "0";
9896                     a = {height: bh};
9897                 break;
9898                 case "l":
9899                     wrap.setSize(0, b.height);
9900                     st.right = st.top = "0";
9901                     a = {width: bw};
9902                 break;
9903                 case "r":
9904                     wrap.setSize(0, b.height);
9905                     wrap.setX(b.right);
9906                     st.left = st.top = "0";
9907                     a = {width: bw, points: pt};
9908                 break;
9909                 case "b":
9910                     wrap.setSize(b.width, 0);
9911                     wrap.setY(b.bottom);
9912                     st.left = st.top = "0";
9913                     a = {height: bh, points: pt};
9914                 break;
9915                 case "tl":
9916                     wrap.setSize(0, 0);
9917                     st.right = st.bottom = "0";
9918                     a = {width: bw, height: bh};
9919                 break;
9920                 case "bl":
9921                     wrap.setSize(0, 0);
9922                     wrap.setY(b.y+b.height);
9923                     st.right = st.top = "0";
9924                     a = {width: bw, height: bh, points: pt};
9925                 break;
9926                 case "br":
9927                     wrap.setSize(0, 0);
9928                     wrap.setXY([b.right, b.bottom]);
9929                     st.left = st.top = "0";
9930                     a = {width: bw, height: bh, points: pt};
9931                 break;
9932                 case "tr":
9933                     wrap.setSize(0, 0);
9934                     wrap.setX(b.x+b.width);
9935                     st.left = st.bottom = "0";
9936                     a = {width: bw, height: bh, points: pt};
9937                 break;
9938             }
9939             this.dom.style.visibility = "visible";
9940             wrap.show();
9941
9942             arguments.callee.anim = wrap.fxanim(a,
9943                 o,
9944                 'motion',
9945                 .5,
9946                 'easeOut', after);
9947         });
9948         return this;
9949     },
9950     
9951         /**
9952          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9953          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9954          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9955          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9956          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9957          * Usage:
9958          *<pre><code>
9959 // default: slide the element out to the top
9960 el.slideOut();
9961
9962 // custom: slide the element out to the right with a 2-second duration
9963 el.slideOut('r', { duration: 2 });
9964
9965 // common config options shown with default values
9966 el.slideOut('t', {
9967     easing: 'easeOut',
9968     duration: .5,
9969     remove: false,
9970     useDisplay: false
9971 });
9972 </code></pre>
9973          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9974          * @param {Object} options (optional) Object literal with any of the Fx config options
9975          * @return {Roo.Element} The Element
9976          */
9977     slideOut : function(anchor, o){
9978         var el = this.getFxEl();
9979         o = o || {};
9980
9981         el.queueFx(o, function(){
9982
9983             anchor = anchor || "t";
9984
9985             // restore values after effect
9986             var r = this.getFxRestore();
9987             
9988             var b = this.getBox();
9989             // fixed size for slide
9990             this.setSize(b);
9991
9992             // wrap if needed
9993             var wrap = this.fxWrap(r.pos, o, "visible");
9994
9995             var st = this.dom.style;
9996             st.visibility = "visible";
9997             st.position = "absolute";
9998
9999             wrap.setSize(b);
10000
10001             var after = function(){
10002                 if(o.useDisplay){
10003                     el.setDisplayed(false);
10004                 }else{
10005                     el.hide();
10006                 }
10007
10008                 el.fxUnwrap(wrap, r.pos, o);
10009
10010                 st.width = r.width;
10011                 st.height = r.height;
10012
10013                 el.afterFx(o);
10014             };
10015
10016             var a, zero = {to: 0};
10017             switch(anchor.toLowerCase()){
10018                 case "t":
10019                     st.left = st.bottom = "0";
10020                     a = {height: zero};
10021                 break;
10022                 case "l":
10023                     st.right = st.top = "0";
10024                     a = {width: zero};
10025                 break;
10026                 case "r":
10027                     st.left = st.top = "0";
10028                     a = {width: zero, points: {to:[b.right, b.y]}};
10029                 break;
10030                 case "b":
10031                     st.left = st.top = "0";
10032                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10033                 break;
10034                 case "tl":
10035                     st.right = st.bottom = "0";
10036                     a = {width: zero, height: zero};
10037                 break;
10038                 case "bl":
10039                     st.right = st.top = "0";
10040                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10041                 break;
10042                 case "br":
10043                     st.left = st.top = "0";
10044                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10045                 break;
10046                 case "tr":
10047                     st.left = st.bottom = "0";
10048                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10049                 break;
10050             }
10051
10052             arguments.callee.anim = wrap.fxanim(a,
10053                 o,
10054                 'motion',
10055                 .5,
10056                 "easeOut", after);
10057         });
10058         return this;
10059     },
10060
10061         /**
10062          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10063          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10064          * The element must be removed from the DOM using the 'remove' config option if desired.
10065          * Usage:
10066          *<pre><code>
10067 // default
10068 el.puff();
10069
10070 // common config options shown with default values
10071 el.puff({
10072     easing: 'easeOut',
10073     duration: .5,
10074     remove: false,
10075     useDisplay: false
10076 });
10077 </code></pre>
10078          * @param {Object} options (optional) Object literal with any of the Fx config options
10079          * @return {Roo.Element} The Element
10080          */
10081     puff : function(o){
10082         var el = this.getFxEl();
10083         o = o || {};
10084
10085         el.queueFx(o, function(){
10086             this.clearOpacity();
10087             this.show();
10088
10089             // restore values after effect
10090             var r = this.getFxRestore();
10091             var st = this.dom.style;
10092
10093             var after = function(){
10094                 if(o.useDisplay){
10095                     el.setDisplayed(false);
10096                 }else{
10097                     el.hide();
10098                 }
10099
10100                 el.clearOpacity();
10101
10102                 el.setPositioning(r.pos);
10103                 st.width = r.width;
10104                 st.height = r.height;
10105                 st.fontSize = '';
10106                 el.afterFx(o);
10107             };
10108
10109             var width = this.getWidth();
10110             var height = this.getHeight();
10111
10112             arguments.callee.anim = this.fxanim({
10113                     width : {to: this.adjustWidth(width * 2)},
10114                     height : {to: this.adjustHeight(height * 2)},
10115                     points : {by: [-(width * .5), -(height * .5)]},
10116                     opacity : {to: 0},
10117                     fontSize: {to:200, unit: "%"}
10118                 },
10119                 o,
10120                 'motion',
10121                 .5,
10122                 "easeOut", after);
10123         });
10124         return this;
10125     },
10126
10127         /**
10128          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10129          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10130          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10131          * Usage:
10132          *<pre><code>
10133 // default
10134 el.switchOff();
10135
10136 // all config options shown with default values
10137 el.switchOff({
10138     easing: 'easeIn',
10139     duration: .3,
10140     remove: false,
10141     useDisplay: false
10142 });
10143 </code></pre>
10144          * @param {Object} options (optional) Object literal with any of the Fx config options
10145          * @return {Roo.Element} The Element
10146          */
10147     switchOff : function(o){
10148         var el = this.getFxEl();
10149         o = o || {};
10150
10151         el.queueFx(o, function(){
10152             this.clearOpacity();
10153             this.clip();
10154
10155             // restore values after effect
10156             var r = this.getFxRestore();
10157             var st = this.dom.style;
10158
10159             var after = function(){
10160                 if(o.useDisplay){
10161                     el.setDisplayed(false);
10162                 }else{
10163                     el.hide();
10164                 }
10165
10166                 el.clearOpacity();
10167                 el.setPositioning(r.pos);
10168                 st.width = r.width;
10169                 st.height = r.height;
10170
10171                 el.afterFx(o);
10172             };
10173
10174             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10175                 this.clearOpacity();
10176                 (function(){
10177                     this.fxanim({
10178                         height:{to:1},
10179                         points:{by:[0, this.getHeight() * .5]}
10180                     }, o, 'motion', 0.3, 'easeIn', after);
10181                 }).defer(100, this);
10182             });
10183         });
10184         return this;
10185     },
10186
10187     /**
10188      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10189      * changed using the "attr" config option) and then fading back to the original color. If no original
10190      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10191      * Usage:
10192 <pre><code>
10193 // default: highlight background to yellow
10194 el.highlight();
10195
10196 // custom: highlight foreground text to blue for 2 seconds
10197 el.highlight("0000ff", { attr: 'color', duration: 2 });
10198
10199 // common config options shown with default values
10200 el.highlight("ffff9c", {
10201     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10202     endColor: (current color) or "ffffff",
10203     easing: 'easeIn',
10204     duration: 1
10205 });
10206 </code></pre>
10207      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10208      * @param {Object} options (optional) Object literal with any of the Fx config options
10209      * @return {Roo.Element} The Element
10210      */ 
10211     highlight : function(color, o){
10212         var el = this.getFxEl();
10213         o = o || {};
10214
10215         el.queueFx(o, function(){
10216             color = color || "ffff9c";
10217             attr = o.attr || "backgroundColor";
10218
10219             this.clearOpacity();
10220             this.show();
10221
10222             var origColor = this.getColor(attr);
10223             var restoreColor = this.dom.style[attr];
10224             endColor = (o.endColor || origColor) || "ffffff";
10225
10226             var after = function(){
10227                 el.dom.style[attr] = restoreColor;
10228                 el.afterFx(o);
10229             };
10230
10231             var a = {};
10232             a[attr] = {from: color, to: endColor};
10233             arguments.callee.anim = this.fxanim(a,
10234                 o,
10235                 'color',
10236                 1,
10237                 'easeIn', after);
10238         });
10239         return this;
10240     },
10241
10242    /**
10243     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10244     * Usage:
10245 <pre><code>
10246 // default: a single light blue ripple
10247 el.frame();
10248
10249 // custom: 3 red ripples lasting 3 seconds total
10250 el.frame("ff0000", 3, { duration: 3 });
10251
10252 // common config options shown with default values
10253 el.frame("C3DAF9", 1, {
10254     duration: 1 //duration of entire animation (not each individual ripple)
10255     // Note: Easing is not configurable and will be ignored if included
10256 });
10257 </code></pre>
10258     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10259     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10260     * @param {Object} options (optional) Object literal with any of the Fx config options
10261     * @return {Roo.Element} The Element
10262     */
10263     frame : function(color, count, o){
10264         var el = this.getFxEl();
10265         o = o || {};
10266
10267         el.queueFx(o, function(){
10268             color = color || "#C3DAF9";
10269             if(color.length == 6){
10270                 color = "#" + color;
10271             }
10272             count = count || 1;
10273             duration = o.duration || 1;
10274             this.show();
10275
10276             var b = this.getBox();
10277             var animFn = function(){
10278                 var proxy = this.createProxy({
10279
10280                      style:{
10281                         visbility:"hidden",
10282                         position:"absolute",
10283                         "z-index":"35000", // yee haw
10284                         border:"0px solid " + color
10285                      }
10286                   });
10287                 var scale = Roo.isBorderBox ? 2 : 1;
10288                 proxy.animate({
10289                     top:{from:b.y, to:b.y - 20},
10290                     left:{from:b.x, to:b.x - 20},
10291                     borderWidth:{from:0, to:10},
10292                     opacity:{from:1, to:0},
10293                     height:{from:b.height, to:(b.height + (20*scale))},
10294                     width:{from:b.width, to:(b.width + (20*scale))}
10295                 }, duration, function(){
10296                     proxy.remove();
10297                 });
10298                 if(--count > 0){
10299                      animFn.defer((duration/2)*1000, this);
10300                 }else{
10301                     el.afterFx(o);
10302                 }
10303             };
10304             animFn.call(this);
10305         });
10306         return this;
10307     },
10308
10309    /**
10310     * Creates a pause before any subsequent queued effects begin.  If there are
10311     * no effects queued after the pause it will have no effect.
10312     * Usage:
10313 <pre><code>
10314 el.pause(1);
10315 </code></pre>
10316     * @param {Number} seconds The length of time to pause (in seconds)
10317     * @return {Roo.Element} The Element
10318     */
10319     pause : function(seconds){
10320         var el = this.getFxEl();
10321         var o = {};
10322
10323         el.queueFx(o, function(){
10324             setTimeout(function(){
10325                 el.afterFx(o);
10326             }, seconds * 1000);
10327         });
10328         return this;
10329     },
10330
10331    /**
10332     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10333     * using the "endOpacity" config option.
10334     * Usage:
10335 <pre><code>
10336 // default: fade in from opacity 0 to 100%
10337 el.fadeIn();
10338
10339 // custom: fade in from opacity 0 to 75% over 2 seconds
10340 el.fadeIn({ endOpacity: .75, duration: 2});
10341
10342 // common config options shown with default values
10343 el.fadeIn({
10344     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10345     easing: 'easeOut',
10346     duration: .5
10347 });
10348 </code></pre>
10349     * @param {Object} options (optional) Object literal with any of the Fx config options
10350     * @return {Roo.Element} The Element
10351     */
10352     fadeIn : function(o){
10353         var el = this.getFxEl();
10354         o = o || {};
10355         el.queueFx(o, function(){
10356             this.setOpacity(0);
10357             this.fixDisplay();
10358             this.dom.style.visibility = 'visible';
10359             var to = o.endOpacity || 1;
10360             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10361                 o, null, .5, "easeOut", function(){
10362                 if(to == 1){
10363                     this.clearOpacity();
10364                 }
10365                 el.afterFx(o);
10366             });
10367         });
10368         return this;
10369     },
10370
10371    /**
10372     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10373     * using the "endOpacity" config option.
10374     * Usage:
10375 <pre><code>
10376 // default: fade out from the element's current opacity to 0
10377 el.fadeOut();
10378
10379 // custom: fade out from the element's current opacity to 25% over 2 seconds
10380 el.fadeOut({ endOpacity: .25, duration: 2});
10381
10382 // common config options shown with default values
10383 el.fadeOut({
10384     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10385     easing: 'easeOut',
10386     duration: .5
10387     remove: false,
10388     useDisplay: false
10389 });
10390 </code></pre>
10391     * @param {Object} options (optional) Object literal with any of the Fx config options
10392     * @return {Roo.Element} The Element
10393     */
10394     fadeOut : function(o){
10395         var el = this.getFxEl();
10396         o = o || {};
10397         el.queueFx(o, function(){
10398             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10399                 o, null, .5, "easeOut", function(){
10400                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10401                      this.dom.style.display = "none";
10402                 }else{
10403                      this.dom.style.visibility = "hidden";
10404                 }
10405                 this.clearOpacity();
10406                 el.afterFx(o);
10407             });
10408         });
10409         return this;
10410     },
10411
10412    /**
10413     * Animates the transition of an element's dimensions from a starting height/width
10414     * to an ending height/width.
10415     * Usage:
10416 <pre><code>
10417 // change height and width to 100x100 pixels
10418 el.scale(100, 100);
10419
10420 // common config options shown with default values.  The height and width will default to
10421 // the element's existing values if passed as null.
10422 el.scale(
10423     [element's width],
10424     [element's height], {
10425     easing: 'easeOut',
10426     duration: .35
10427 });
10428 </code></pre>
10429     * @param {Number} width  The new width (pass undefined to keep the original width)
10430     * @param {Number} height  The new height (pass undefined to keep the original height)
10431     * @param {Object} options (optional) Object literal with any of the Fx config options
10432     * @return {Roo.Element} The Element
10433     */
10434     scale : function(w, h, o){
10435         this.shift(Roo.apply({}, o, {
10436             width: w,
10437             height: h
10438         }));
10439         return this;
10440     },
10441
10442    /**
10443     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10444     * Any of these properties not specified in the config object will not be changed.  This effect 
10445     * requires that at least one new dimension, position or opacity setting must be passed in on
10446     * the config object in order for the function to have any effect.
10447     * Usage:
10448 <pre><code>
10449 // slide the element horizontally to x position 200 while changing the height and opacity
10450 el.shift({ x: 200, height: 50, opacity: .8 });
10451
10452 // common config options shown with default values.
10453 el.shift({
10454     width: [element's width],
10455     height: [element's height],
10456     x: [element's x position],
10457     y: [element's y position],
10458     opacity: [element's opacity],
10459     easing: 'easeOut',
10460     duration: .35
10461 });
10462 </code></pre>
10463     * @param {Object} options  Object literal with any of the Fx config options
10464     * @return {Roo.Element} The Element
10465     */
10466     shift : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469         el.queueFx(o, function(){
10470             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10471             if(w !== undefined){
10472                 a.width = {to: this.adjustWidth(w)};
10473             }
10474             if(h !== undefined){
10475                 a.height = {to: this.adjustHeight(h)};
10476             }
10477             if(x !== undefined || y !== undefined){
10478                 a.points = {to: [
10479                     x !== undefined ? x : this.getX(),
10480                     y !== undefined ? y : this.getY()
10481                 ]};
10482             }
10483             if(op !== undefined){
10484                 a.opacity = {to: op};
10485             }
10486             if(o.xy !== undefined){
10487                 a.points = {to: o.xy};
10488             }
10489             arguments.callee.anim = this.fxanim(a,
10490                 o, 'motion', .35, "easeOut", function(){
10491                 el.afterFx(o);
10492             });
10493         });
10494         return this;
10495     },
10496
10497         /**
10498          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10499          * ending point of the effect.
10500          * Usage:
10501          *<pre><code>
10502 // default: slide the element downward while fading out
10503 el.ghost();
10504
10505 // custom: slide the element out to the right with a 2-second duration
10506 el.ghost('r', { duration: 2 });
10507
10508 // common config options shown with default values
10509 el.ghost('b', {
10510     easing: 'easeOut',
10511     duration: .5
10512     remove: false,
10513     useDisplay: false
10514 });
10515 </code></pre>
10516          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10517          * @param {Object} options (optional) Object literal with any of the Fx config options
10518          * @return {Roo.Element} The Element
10519          */
10520     ghost : function(anchor, o){
10521         var el = this.getFxEl();
10522         o = o || {};
10523
10524         el.queueFx(o, function(){
10525             anchor = anchor || "b";
10526
10527             // restore values after effect
10528             var r = this.getFxRestore();
10529             var w = this.getWidth(),
10530                 h = this.getHeight();
10531
10532             var st = this.dom.style;
10533
10534             var after = function(){
10535                 if(o.useDisplay){
10536                     el.setDisplayed(false);
10537                 }else{
10538                     el.hide();
10539                 }
10540
10541                 el.clearOpacity();
10542                 el.setPositioning(r.pos);
10543                 st.width = r.width;
10544                 st.height = r.height;
10545
10546                 el.afterFx(o);
10547             };
10548
10549             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10550             switch(anchor.toLowerCase()){
10551                 case "t":
10552                     pt.by = [0, -h];
10553                 break;
10554                 case "l":
10555                     pt.by = [-w, 0];
10556                 break;
10557                 case "r":
10558                     pt.by = [w, 0];
10559                 break;
10560                 case "b":
10561                     pt.by = [0, h];
10562                 break;
10563                 case "tl":
10564                     pt.by = [-w, -h];
10565                 break;
10566                 case "bl":
10567                     pt.by = [-w, h];
10568                 break;
10569                 case "br":
10570                     pt.by = [w, h];
10571                 break;
10572                 case "tr":
10573                     pt.by = [w, -h];
10574                 break;
10575             }
10576
10577             arguments.callee.anim = this.fxanim(a,
10578                 o,
10579                 'motion',
10580                 .5,
10581                 "easeOut", after);
10582         });
10583         return this;
10584     },
10585
10586         /**
10587          * Ensures that all effects queued after syncFx is called on the element are
10588          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10589          * @return {Roo.Element} The Element
10590          */
10591     syncFx : function(){
10592         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10593             block : false,
10594             concurrent : true,
10595             stopFx : false
10596         });
10597         return this;
10598     },
10599
10600         /**
10601          * Ensures that all effects queued after sequenceFx is called on the element are
10602          * run in sequence.  This is the opposite of {@link #syncFx}.
10603          * @return {Roo.Element} The Element
10604          */
10605     sequenceFx : function(){
10606         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10607             block : false,
10608             concurrent : false,
10609             stopFx : false
10610         });
10611         return this;
10612     },
10613
10614         /* @private */
10615     nextFx : function(){
10616         var ef = this.fxQueue[0];
10617         if(ef){
10618             ef.call(this);
10619         }
10620     },
10621
10622         /**
10623          * Returns true if the element has any effects actively running or queued, else returns false.
10624          * @return {Boolean} True if element has active effects, else false
10625          */
10626     hasActiveFx : function(){
10627         return this.fxQueue && this.fxQueue[0];
10628     },
10629
10630         /**
10631          * Stops any running effects and clears the element's internal effects queue if it contains
10632          * any additional effects that haven't started yet.
10633          * @return {Roo.Element} The Element
10634          */
10635     stopFx : function(){
10636         if(this.hasActiveFx()){
10637             var cur = this.fxQueue[0];
10638             if(cur && cur.anim && cur.anim.isAnimated()){
10639                 this.fxQueue = [cur]; // clear out others
10640                 cur.anim.stop(true);
10641             }
10642         }
10643         return this;
10644     },
10645
10646         /* @private */
10647     beforeFx : function(o){
10648         if(this.hasActiveFx() && !o.concurrent){
10649            if(o.stopFx){
10650                this.stopFx();
10651                return true;
10652            }
10653            return false;
10654         }
10655         return true;
10656     },
10657
10658         /**
10659          * Returns true if the element is currently blocking so that no other effect can be queued
10660          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10661          * used to ensure that an effect initiated by a user action runs to completion prior to the
10662          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10663          * @return {Boolean} True if blocking, else false
10664          */
10665     hasFxBlock : function(){
10666         var q = this.fxQueue;
10667         return q && q[0] && q[0].block;
10668     },
10669
10670         /* @private */
10671     queueFx : function(o, fn){
10672         if(!this.fxQueue){
10673             this.fxQueue = [];
10674         }
10675         if(!this.hasFxBlock()){
10676             Roo.applyIf(o, this.fxDefaults);
10677             if(!o.concurrent){
10678                 var run = this.beforeFx(o);
10679                 fn.block = o.block;
10680                 this.fxQueue.push(fn);
10681                 if(run){
10682                     this.nextFx();
10683                 }
10684             }else{
10685                 fn.call(this);
10686             }
10687         }
10688         return this;
10689     },
10690
10691         /* @private */
10692     fxWrap : function(pos, o, vis){
10693         var wrap;
10694         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10695             var wrapXY;
10696             if(o.fixPosition){
10697                 wrapXY = this.getXY();
10698             }
10699             var div = document.createElement("div");
10700             div.style.visibility = vis;
10701             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10702             wrap.setPositioning(pos);
10703             if(wrap.getStyle("position") == "static"){
10704                 wrap.position("relative");
10705             }
10706             this.clearPositioning('auto');
10707             wrap.clip();
10708             wrap.dom.appendChild(this.dom);
10709             if(wrapXY){
10710                 wrap.setXY(wrapXY);
10711             }
10712         }
10713         return wrap;
10714     },
10715
10716         /* @private */
10717     fxUnwrap : function(wrap, pos, o){
10718         this.clearPositioning();
10719         this.setPositioning(pos);
10720         if(!o.wrap){
10721             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10722             wrap.remove();
10723         }
10724     },
10725
10726         /* @private */
10727     getFxRestore : function(){
10728         var st = this.dom.style;
10729         return {pos: this.getPositioning(), width: st.width, height : st.height};
10730     },
10731
10732         /* @private */
10733     afterFx : function(o){
10734         if(o.afterStyle){
10735             this.applyStyles(o.afterStyle);
10736         }
10737         if(o.afterCls){
10738             this.addClass(o.afterCls);
10739         }
10740         if(o.remove === true){
10741             this.remove();
10742         }
10743         Roo.callback(o.callback, o.scope, [this]);
10744         if(!o.concurrent){
10745             this.fxQueue.shift();
10746             this.nextFx();
10747         }
10748     },
10749
10750         /* @private */
10751     getFxEl : function(){ // support for composite element fx
10752         return Roo.get(this.dom);
10753     },
10754
10755         /* @private */
10756     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10757         animType = animType || 'run';
10758         opt = opt || {};
10759         var anim = Roo.lib.Anim[animType](
10760             this.dom, args,
10761             (opt.duration || defaultDur) || .35,
10762             (opt.easing || defaultEase) || 'easeOut',
10763             function(){
10764                 Roo.callback(cb, this);
10765             },
10766             this
10767         );
10768         opt.anim = anim;
10769         return anim;
10770     }
10771 };
10772
10773 // backwords compat
10774 Roo.Fx.resize = Roo.Fx.scale;
10775
10776 //When included, Roo.Fx is automatically applied to Element so that all basic
10777 //effects are available directly via the Element API
10778 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10779  * Based on:
10780  * Ext JS Library 1.1.1
10781  * Copyright(c) 2006-2007, Ext JS, LLC.
10782  *
10783  * Originally Released Under LGPL - original licence link has changed is not relivant.
10784  *
10785  * Fork - LGPL
10786  * <script type="text/javascript">
10787  */
10788
10789
10790 /**
10791  * @class Roo.CompositeElement
10792  * Standard composite class. Creates a Roo.Element for every element in the collection.
10793  * <br><br>
10794  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10795  * actions will be performed on all the elements in this collection.</b>
10796  * <br><br>
10797  * All methods return <i>this</i> and can be chained.
10798  <pre><code>
10799  var els = Roo.select("#some-el div.some-class", true);
10800  // or select directly from an existing element
10801  var el = Roo.get('some-el');
10802  el.select('div.some-class', true);
10803
10804  els.setWidth(100); // all elements become 100 width
10805  els.hide(true); // all elements fade out and hide
10806  // or
10807  els.setWidth(100).hide(true);
10808  </code></pre>
10809  */
10810 Roo.CompositeElement = function(els){
10811     this.elements = [];
10812     this.addElements(els);
10813 };
10814 Roo.CompositeElement.prototype = {
10815     isComposite: true,
10816     addElements : function(els){
10817         if(!els) return this;
10818         if(typeof els == "string"){
10819             els = Roo.Element.selectorFunction(els);
10820         }
10821         var yels = this.elements;
10822         var index = yels.length-1;
10823         for(var i = 0, len = els.length; i < len; i++) {
10824                 yels[++index] = Roo.get(els[i]);
10825         }
10826         return this;
10827     },
10828
10829     /**
10830     * Clears this composite and adds the elements returned by the passed selector.
10831     * @param {String/Array} els A string CSS selector, an array of elements or an element
10832     * @return {CompositeElement} this
10833     */
10834     fill : function(els){
10835         this.elements = [];
10836         this.add(els);
10837         return this;
10838     },
10839
10840     /**
10841     * Filters this composite to only elements that match the passed selector.
10842     * @param {String} selector A string CSS selector
10843     * @return {CompositeElement} this
10844     */
10845     filter : function(selector){
10846         var els = [];
10847         this.each(function(el){
10848             if(el.is(selector)){
10849                 els[els.length] = el.dom;
10850             }
10851         });
10852         this.fill(els);
10853         return this;
10854     },
10855
10856     invoke : function(fn, args){
10857         var els = this.elements;
10858         for(var i = 0, len = els.length; i < len; i++) {
10859                 Roo.Element.prototype[fn].apply(els[i], args);
10860         }
10861         return this;
10862     },
10863     /**
10864     * Adds elements to this composite.
10865     * @param {String/Array} els A string CSS selector, an array of elements or an element
10866     * @return {CompositeElement} this
10867     */
10868     add : function(els){
10869         if(typeof els == "string"){
10870             this.addElements(Roo.Element.selectorFunction(els));
10871         }else if(els.length !== undefined){
10872             this.addElements(els);
10873         }else{
10874             this.addElements([els]);
10875         }
10876         return this;
10877     },
10878     /**
10879     * Calls the passed function passing (el, this, index) for each element in this composite.
10880     * @param {Function} fn The function to call
10881     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10882     * @return {CompositeElement} this
10883     */
10884     each : function(fn, scope){
10885         var els = this.elements;
10886         for(var i = 0, len = els.length; i < len; i++){
10887             if(fn.call(scope || els[i], els[i], this, i) === false) {
10888                 break;
10889             }
10890         }
10891         return this;
10892     },
10893
10894     /**
10895      * Returns the Element object at the specified index
10896      * @param {Number} index
10897      * @return {Roo.Element}
10898      */
10899     item : function(index){
10900         return this.elements[index] || null;
10901     },
10902
10903     /**
10904      * Returns the first Element
10905      * @return {Roo.Element}
10906      */
10907     first : function(){
10908         return this.item(0);
10909     },
10910
10911     /**
10912      * Returns the last Element
10913      * @return {Roo.Element}
10914      */
10915     last : function(){
10916         return this.item(this.elements.length-1);
10917     },
10918
10919     /**
10920      * Returns the number of elements in this composite
10921      * @return Number
10922      */
10923     getCount : function(){
10924         return this.elements.length;
10925     },
10926
10927     /**
10928      * Returns true if this composite contains the passed element
10929      * @return Boolean
10930      */
10931     contains : function(el){
10932         return this.indexOf(el) !== -1;
10933     },
10934
10935     /**
10936      * Returns true if this composite contains the passed element
10937      * @return Boolean
10938      */
10939     indexOf : function(el){
10940         return this.elements.indexOf(Roo.get(el));
10941     },
10942
10943
10944     /**
10945     * Removes the specified element(s).
10946     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10947     * or an array of any of those.
10948     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10949     * @return {CompositeElement} this
10950     */
10951     removeElement : function(el, removeDom){
10952         if(el instanceof Array){
10953             for(var i = 0, len = el.length; i < len; i++){
10954                 this.removeElement(el[i]);
10955             }
10956             return this;
10957         }
10958         var index = typeof el == 'number' ? el : this.indexOf(el);
10959         if(index !== -1){
10960             if(removeDom){
10961                 var d = this.elements[index];
10962                 if(d.dom){
10963                     d.remove();
10964                 }else{
10965                     d.parentNode.removeChild(d);
10966                 }
10967             }
10968             this.elements.splice(index, 1);
10969         }
10970         return this;
10971     },
10972
10973     /**
10974     * Replaces the specified element with the passed element.
10975     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10976     * to replace.
10977     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10978     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10979     * @return {CompositeElement} this
10980     */
10981     replaceElement : function(el, replacement, domReplace){
10982         var index = typeof el == 'number' ? el : this.indexOf(el);
10983         if(index !== -1){
10984             if(domReplace){
10985                 this.elements[index].replaceWith(replacement);
10986             }else{
10987                 this.elements.splice(index, 1, Roo.get(replacement))
10988             }
10989         }
10990         return this;
10991     },
10992
10993     /**
10994      * Removes all elements.
10995      */
10996     clear : function(){
10997         this.elements = [];
10998     }
10999 };
11000 (function(){
11001     Roo.CompositeElement.createCall = function(proto, fnName){
11002         if(!proto[fnName]){
11003             proto[fnName] = function(){
11004                 return this.invoke(fnName, arguments);
11005             };
11006         }
11007     };
11008     for(var fnName in Roo.Element.prototype){
11009         if(typeof Roo.Element.prototype[fnName] == "function"){
11010             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11011         }
11012     };
11013 })();
11014 /*
11015  * Based on:
11016  * Ext JS Library 1.1.1
11017  * Copyright(c) 2006-2007, Ext JS, LLC.
11018  *
11019  * Originally Released Under LGPL - original licence link has changed is not relivant.
11020  *
11021  * Fork - LGPL
11022  * <script type="text/javascript">
11023  */
11024
11025 /**
11026  * @class Roo.CompositeElementLite
11027  * @extends Roo.CompositeElement
11028  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11029  <pre><code>
11030  var els = Roo.select("#some-el div.some-class");
11031  // or select directly from an existing element
11032  var el = Roo.get('some-el');
11033  el.select('div.some-class');
11034
11035  els.setWidth(100); // all elements become 100 width
11036  els.hide(true); // all elements fade out and hide
11037  // or
11038  els.setWidth(100).hide(true);
11039  </code></pre><br><br>
11040  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11041  * actions will be performed on all the elements in this collection.</b>
11042  */
11043 Roo.CompositeElementLite = function(els){
11044     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11045     this.el = new Roo.Element.Flyweight();
11046 };
11047 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11048     addElements : function(els){
11049         if(els){
11050             if(els instanceof Array){
11051                 this.elements = this.elements.concat(els);
11052             }else{
11053                 var yels = this.elements;
11054                 var index = yels.length-1;
11055                 for(var i = 0, len = els.length; i < len; i++) {
11056                     yels[++index] = els[i];
11057                 }
11058             }
11059         }
11060         return this;
11061     },
11062     invoke : function(fn, args){
11063         var els = this.elements;
11064         var el = this.el;
11065         for(var i = 0, len = els.length; i < len; i++) {
11066             el.dom = els[i];
11067                 Roo.Element.prototype[fn].apply(el, args);
11068         }
11069         return this;
11070     },
11071     /**
11072      * Returns a flyweight Element of the dom element object at the specified index
11073      * @param {Number} index
11074      * @return {Roo.Element}
11075      */
11076     item : function(index){
11077         if(!this.elements[index]){
11078             return null;
11079         }
11080         this.el.dom = this.elements[index];
11081         return this.el;
11082     },
11083
11084     // fixes scope with flyweight
11085     addListener : function(eventName, handler, scope, opt){
11086         var els = this.elements;
11087         for(var i = 0, len = els.length; i < len; i++) {
11088             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11089         }
11090         return this;
11091     },
11092
11093     /**
11094     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11095     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11096     * a reference to the dom node, use el.dom.</b>
11097     * @param {Function} fn The function to call
11098     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11099     * @return {CompositeElement} this
11100     */
11101     each : function(fn, scope){
11102         var els = this.elements;
11103         var el = this.el;
11104         for(var i = 0, len = els.length; i < len; i++){
11105             el.dom = els[i];
11106                 if(fn.call(scope || el, el, this, i) === false){
11107                 break;
11108             }
11109         }
11110         return this;
11111     },
11112
11113     indexOf : function(el){
11114         return this.elements.indexOf(Roo.getDom(el));
11115     },
11116
11117     replaceElement : function(el, replacement, domReplace){
11118         var index = typeof el == 'number' ? el : this.indexOf(el);
11119         if(index !== -1){
11120             replacement = Roo.getDom(replacement);
11121             if(domReplace){
11122                 var d = this.elements[index];
11123                 d.parentNode.insertBefore(replacement, d);
11124                 d.parentNode.removeChild(d);
11125             }
11126             this.elements.splice(index, 1, replacement);
11127         }
11128         return this;
11129     }
11130 });
11131 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11132
11133 /*
11134  * Based on:
11135  * Ext JS Library 1.1.1
11136  * Copyright(c) 2006-2007, Ext JS, LLC.
11137  *
11138  * Originally Released Under LGPL - original licence link has changed is not relivant.
11139  *
11140  * Fork - LGPL
11141  * <script type="text/javascript">
11142  */
11143
11144  
11145
11146 /**
11147  * @class Roo.data.Connection
11148  * @extends Roo.util.Observable
11149  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11150  * either to a configured URL, or to a URL specified at request time.<br><br>
11151  * <p>
11152  * Requests made by this class are asynchronous, and will return immediately. No data from
11153  * the server will be available to the statement immediately following the {@link #request} call.
11154  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11155  * <p>
11156  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11157  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11158  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11159  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11160  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11161  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11162  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11163  * standard DOM methods.
11164  * @constructor
11165  * @param {Object} config a configuration object.
11166  */
11167 Roo.data.Connection = function(config){
11168     Roo.apply(this, config);
11169     this.addEvents({
11170         /**
11171          * @event beforerequest
11172          * Fires before a network request is made to retrieve a data object.
11173          * @param {Connection} conn This Connection object.
11174          * @param {Object} options The options config object passed to the {@link #request} method.
11175          */
11176         "beforerequest" : true,
11177         /**
11178          * @event requestcomplete
11179          * Fires if the request was successfully completed.
11180          * @param {Connection} conn This Connection object.
11181          * @param {Object} response The XHR object containing the response data.
11182          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11183          * @param {Object} options The options config object passed to the {@link #request} method.
11184          */
11185         "requestcomplete" : true,
11186         /**
11187          * @event requestexception
11188          * Fires if an error HTTP status was returned from the server.
11189          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11190          * @param {Connection} conn This Connection object.
11191          * @param {Object} response The XHR object containing the response data.
11192          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11193          * @param {Object} options The options config object passed to the {@link #request} method.
11194          */
11195         "requestexception" : true
11196     });
11197     Roo.data.Connection.superclass.constructor.call(this);
11198 };
11199
11200 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11201     /**
11202      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11203      */
11204     /**
11205      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11206      * extra parameters to each request made by this object. (defaults to undefined)
11207      */
11208     /**
11209      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11210      *  to each request made by this object. (defaults to undefined)
11211      */
11212     /**
11213      * @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)
11214      */
11215     /**
11216      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11217      */
11218     timeout : 30000,
11219     /**
11220      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11221      * @type Boolean
11222      */
11223     autoAbort:false,
11224
11225     /**
11226      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11227      * @type Boolean
11228      */
11229     disableCaching: true,
11230
11231     /**
11232      * Sends an HTTP request to a remote server.
11233      * @param {Object} options An object which may contain the following properties:<ul>
11234      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11235      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11236      * request, a url encoded string or a function to call to get either.</li>
11237      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11238      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11239      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11240      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11241      * <li>options {Object} The parameter to the request call.</li>
11242      * <li>success {Boolean} True if the request succeeded.</li>
11243      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11244      * </ul></li>
11245      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11251      * The callback is passed the following parameters:<ul>
11252      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11253      * <li>options {Object} The parameter to the request call.</li>
11254      * </ul></li>
11255      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11256      * for the callback function. Defaults to the browser window.</li>
11257      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11258      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11259      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11260      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11261      * params for the post data. Any params will be appended to the URL.</li>
11262      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11263      * </ul>
11264      * @return {Number} transactionId
11265      */
11266     request : function(o){
11267         if(this.fireEvent("beforerequest", this, o) !== false){
11268             var p = o.params;
11269
11270             if(typeof p == "function"){
11271                 p = p.call(o.scope||window, o);
11272             }
11273             if(typeof p == "object"){
11274                 p = Roo.urlEncode(o.params);
11275             }
11276             if(this.extraParams){
11277                 var extras = Roo.urlEncode(this.extraParams);
11278                 p = p ? (p + '&' + extras) : extras;
11279             }
11280
11281             var url = o.url || this.url;
11282             if(typeof url == 'function'){
11283                 url = url.call(o.scope||window, o);
11284             }
11285
11286             if(o.form){
11287                 var form = Roo.getDom(o.form);
11288                 url = url || form.action;
11289
11290                 var enctype = form.getAttribute("enctype");
11291                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11292                     return this.doFormUpload(o, p, url);
11293                 }
11294                 var f = Roo.lib.Ajax.serializeForm(form);
11295                 p = p ? (p + '&' + f) : f;
11296             }
11297
11298             var hs = o.headers;
11299             if(this.defaultHeaders){
11300                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11301                 if(!o.headers){
11302                     o.headers = hs;
11303                 }
11304             }
11305
11306             var cb = {
11307                 success: this.handleResponse,
11308                 failure: this.handleFailure,
11309                 scope: this,
11310                 argument: {options: o},
11311                 timeout : this.timeout
11312             };
11313
11314             var method = o.method||this.method||(p ? "POST" : "GET");
11315
11316             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11317                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11318             }
11319
11320             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11321                 if(o.autoAbort){
11322                     this.abort();
11323                 }
11324             }else if(this.autoAbort !== false){
11325                 this.abort();
11326             }
11327
11328             if((method == 'GET' && p) || o.xmlData){
11329                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11330                 p = '';
11331             }
11332             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11333             return this.transId;
11334         }else{
11335             Roo.callback(o.callback, o.scope, [o, null, null]);
11336             return null;
11337         }
11338     },
11339
11340     /**
11341      * Determine whether this object has a request outstanding.
11342      * @param {Number} transactionId (Optional) defaults to the last transaction
11343      * @return {Boolean} True if there is an outstanding request.
11344      */
11345     isLoading : function(transId){
11346         if(transId){
11347             return Roo.lib.Ajax.isCallInProgress(transId);
11348         }else{
11349             return this.transId ? true : false;
11350         }
11351     },
11352
11353     /**
11354      * Aborts any outstanding request.
11355      * @param {Number} transactionId (Optional) defaults to the last transaction
11356      */
11357     abort : function(transId){
11358         if(transId || this.isLoading()){
11359             Roo.lib.Ajax.abort(transId || this.transId);
11360         }
11361     },
11362
11363     // private
11364     handleResponse : function(response){
11365         this.transId = false;
11366         var options = response.argument.options;
11367         response.argument = options ? options.argument : null;
11368         this.fireEvent("requestcomplete", this, response, options);
11369         Roo.callback(options.success, options.scope, [response, options]);
11370         Roo.callback(options.callback, options.scope, [options, true, response]);
11371     },
11372
11373     // private
11374     handleFailure : function(response, e){
11375         this.transId = false;
11376         var options = response.argument.options;
11377         response.argument = options ? options.argument : null;
11378         this.fireEvent("requestexception", this, response, options, e);
11379         Roo.callback(options.failure, options.scope, [response, options]);
11380         Roo.callback(options.callback, options.scope, [options, false, response]);
11381     },
11382
11383     // private
11384     doFormUpload : function(o, ps, url){
11385         var id = Roo.id();
11386         var frame = document.createElement('iframe');
11387         frame.id = id;
11388         frame.name = id;
11389         frame.className = 'x-hidden';
11390         if(Roo.isIE){
11391             frame.src = Roo.SSL_SECURE_URL;
11392         }
11393         document.body.appendChild(frame);
11394
11395         if(Roo.isIE){
11396            document.frames[id].name = id;
11397         }
11398
11399         var form = Roo.getDom(o.form);
11400         form.target = id;
11401         form.method = 'POST';
11402         form.enctype = form.encoding = 'multipart/form-data';
11403         if(url){
11404             form.action = url;
11405         }
11406
11407         var hiddens, hd;
11408         if(ps){ // add dynamic params
11409             hiddens = [];
11410             ps = Roo.urlDecode(ps, false);
11411             for(var k in ps){
11412                 if(ps.hasOwnProperty(k)){
11413                     hd = document.createElement('input');
11414                     hd.type = 'hidden';
11415                     hd.name = k;
11416                     hd.value = ps[k];
11417                     form.appendChild(hd);
11418                     hiddens.push(hd);
11419                 }
11420             }
11421         }
11422
11423         function cb(){
11424             var r = {  // bogus response object
11425                 responseText : '',
11426                 responseXML : null
11427             };
11428
11429             r.argument = o ? o.argument : null;
11430
11431             try { //
11432                 var doc;
11433                 if(Roo.isIE){
11434                     doc = frame.contentWindow.document;
11435                 }else {
11436                     doc = (frame.contentDocument || window.frames[id].document);
11437                 }
11438                 if(doc && doc.body){
11439                     r.responseText = doc.body.innerHTML;
11440                 }
11441                 if(doc && doc.XMLDocument){
11442                     r.responseXML = doc.XMLDocument;
11443                 }else {
11444                     r.responseXML = doc;
11445                 }
11446             }
11447             catch(e) {
11448                 // ignore
11449             }
11450
11451             Roo.EventManager.removeListener(frame, 'load', cb, this);
11452
11453             this.fireEvent("requestcomplete", this, r, o);
11454             Roo.callback(o.success, o.scope, [r, o]);
11455             Roo.callback(o.callback, o.scope, [o, true, r]);
11456
11457             setTimeout(function(){document.body.removeChild(frame);}, 100);
11458         }
11459
11460         Roo.EventManager.on(frame, 'load', cb, this);
11461         form.submit();
11462
11463         if(hiddens){ // remove dynamic params
11464             for(var i = 0, len = hiddens.length; i < len; i++){
11465                 form.removeChild(hiddens[i]);
11466             }
11467         }
11468     }
11469 });
11470
11471 /**
11472  * @class Roo.Ajax
11473  * @extends Roo.data.Connection
11474  * Global Ajax request class.
11475  *
11476  * @singleton
11477  */
11478 Roo.Ajax = new Roo.data.Connection({
11479     // fix up the docs
11480    /**
11481      * @cfg {String} url @hide
11482      */
11483     /**
11484      * @cfg {Object} extraParams @hide
11485      */
11486     /**
11487      * @cfg {Object} defaultHeaders @hide
11488      */
11489     /**
11490      * @cfg {String} method (Optional) @hide
11491      */
11492     /**
11493      * @cfg {Number} timeout (Optional) @hide
11494      */
11495     /**
11496      * @cfg {Boolean} autoAbort (Optional) @hide
11497      */
11498
11499     /**
11500      * @cfg {Boolean} disableCaching (Optional) @hide
11501      */
11502
11503     /**
11504      * @property  disableCaching
11505      * True to add a unique cache-buster param to GET requests. (defaults to true)
11506      * @type Boolean
11507      */
11508     /**
11509      * @property  url
11510      * The default URL to be used for requests to the server. (defaults to undefined)
11511      * @type String
11512      */
11513     /**
11514      * @property  extraParams
11515      * An object containing properties which are used as
11516      * extra parameters to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  defaultHeaders
11521      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11522      * @type Object
11523      */
11524     /**
11525      * @property  method
11526      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11527      * @type String
11528      */
11529     /**
11530      * @property  timeout
11531      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11532      * @type Number
11533      */
11534
11535     /**
11536      * @property  autoAbort
11537      * Whether a new request should abort any pending requests. (defaults to false)
11538      * @type Boolean
11539      */
11540     autoAbort : false,
11541
11542     /**
11543      * Serialize the passed form into a url encoded string
11544      * @param {String/HTMLElement} form
11545      * @return {String}
11546      */
11547     serializeForm : function(form){
11548         return Roo.lib.Ajax.serializeForm(form);
11549     }
11550 });/*
11551  * Based on:
11552  * Ext JS Library 1.1.1
11553  * Copyright(c) 2006-2007, Ext JS, LLC.
11554  *
11555  * Originally Released Under LGPL - original licence link has changed is not relivant.
11556  *
11557  * Fork - LGPL
11558  * <script type="text/javascript">
11559  */
11560  
11561 /**
11562  * Global Ajax request class.
11563  * 
11564  * @class Roo.Ajax
11565  * @extends Roo.data.Connection
11566  * @static
11567  * 
11568  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11569  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11570  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11571  * @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)
11572  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11573  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11574  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11575  */
11576 Roo.Ajax = new Roo.data.Connection({
11577     // fix up the docs
11578     /**
11579      * @scope Roo.Ajax
11580      * @type {Boolear} 
11581      */
11582     autoAbort : false,
11583
11584     /**
11585      * Serialize the passed form into a url encoded string
11586      * @scope Roo.Ajax
11587      * @param {String/HTMLElement} form
11588      * @return {String}
11589      */
11590     serializeForm : function(form){
11591         return Roo.lib.Ajax.serializeForm(form);
11592     }
11593 });/*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603
11604  
11605 /**
11606  * @class Roo.UpdateManager
11607  * @extends Roo.util.Observable
11608  * Provides AJAX-style update for Element object.<br><br>
11609  * Usage:<br>
11610  * <pre><code>
11611  * // Get it from a Roo.Element object
11612  * var el = Roo.get("foo");
11613  * var mgr = el.getUpdateManager();
11614  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11615  * ...
11616  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11617  * <br>
11618  * // or directly (returns the same UpdateManager instance)
11619  * var mgr = new Roo.UpdateManager("myElementId");
11620  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11621  * mgr.on("update", myFcnNeedsToKnow);
11622  * <br>
11623    // short handed call directly from the element object
11624    Roo.get("foo").load({
11625         url: "bar.php",
11626         scripts:true,
11627         params: "for=bar",
11628         text: "Loading Foo..."
11629    });
11630  * </code></pre>
11631  * @constructor
11632  * Create new UpdateManager directly.
11633  * @param {String/HTMLElement/Roo.Element} el The element to update
11634  * @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).
11635  */
11636 Roo.UpdateManager = function(el, forceNew){
11637     el = Roo.get(el);
11638     if(!forceNew && el.updateManager){
11639         return el.updateManager;
11640     }
11641     /**
11642      * The Element object
11643      * @type Roo.Element
11644      */
11645     this.el = el;
11646     /**
11647      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11648      * @type String
11649      */
11650     this.defaultUrl = null;
11651
11652     this.addEvents({
11653         /**
11654          * @event beforeupdate
11655          * Fired before an update is made, return false from your handler and the update is cancelled.
11656          * @param {Roo.Element} el
11657          * @param {String/Object/Function} url
11658          * @param {String/Object} params
11659          */
11660         "beforeupdate": true,
11661         /**
11662          * @event update
11663          * Fired after successful update is made.
11664          * @param {Roo.Element} el
11665          * @param {Object} oResponseObject The response Object
11666          */
11667         "update": true,
11668         /**
11669          * @event failure
11670          * Fired on update failure.
11671          * @param {Roo.Element} el
11672          * @param {Object} oResponseObject The response Object
11673          */
11674         "failure": true
11675     });
11676     var d = Roo.UpdateManager.defaults;
11677     /**
11678      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11679      * @type String
11680      */
11681     this.sslBlankUrl = d.sslBlankUrl;
11682     /**
11683      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11684      * @type Boolean
11685      */
11686     this.disableCaching = d.disableCaching;
11687     /**
11688      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11689      * @type String
11690      */
11691     this.indicatorText = d.indicatorText;
11692     /**
11693      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11694      * @type String
11695      */
11696     this.showLoadIndicator = d.showLoadIndicator;
11697     /**
11698      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11699      * @type Number
11700      */
11701     this.timeout = d.timeout;
11702
11703     /**
11704      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11705      * @type Boolean
11706      */
11707     this.loadScripts = d.loadScripts;
11708
11709     /**
11710      * Transaction object of current executing transaction
11711      */
11712     this.transaction = null;
11713
11714     /**
11715      * @private
11716      */
11717     this.autoRefreshProcId = null;
11718     /**
11719      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11720      * @type Function
11721      */
11722     this.refreshDelegate = this.refresh.createDelegate(this);
11723     /**
11724      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11725      * @type Function
11726      */
11727     this.updateDelegate = this.update.createDelegate(this);
11728     /**
11729      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11730      * @type Function
11731      */
11732     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11733     /**
11734      * @private
11735      */
11736     this.successDelegate = this.processSuccess.createDelegate(this);
11737     /**
11738      * @private
11739      */
11740     this.failureDelegate = this.processFailure.createDelegate(this);
11741
11742     if(!this.renderer){
11743      /**
11744       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11745       */
11746     this.renderer = new Roo.UpdateManager.BasicRenderer();
11747     }
11748     
11749     Roo.UpdateManager.superclass.constructor.call(this);
11750 };
11751
11752 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11753     /**
11754      * Get the Element this UpdateManager is bound to
11755      * @return {Roo.Element} The element
11756      */
11757     getEl : function(){
11758         return this.el;
11759     },
11760     /**
11761      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11762      * @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:
11763 <pre><code>
11764 um.update({<br/>
11765     url: "your-url.php",<br/>
11766     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11767     callback: yourFunction,<br/>
11768     scope: yourObject, //(optional scope)  <br/>
11769     discardUrl: false, <br/>
11770     nocache: false,<br/>
11771     text: "Loading...",<br/>
11772     timeout: 30,<br/>
11773     scripts: false<br/>
11774 });
11775 </code></pre>
11776      * The only required property is url. The optional properties nocache, text and scripts
11777      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11778      * @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}
11779      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11780      * @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.
11781      */
11782     update : function(url, params, callback, discardUrl){
11783         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11784             var method = this.method, cfg;
11785             if(typeof url == "object"){ // must be config object
11786                 cfg = url;
11787                 url = cfg.url;
11788                 params = params || cfg.params;
11789                 callback = callback || cfg.callback;
11790                 discardUrl = discardUrl || cfg.discardUrl;
11791                 if(callback && cfg.scope){
11792                     callback = callback.createDelegate(cfg.scope);
11793                 }
11794                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11795                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11796                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11797                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11798                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11799             }
11800             this.showLoading();
11801             if(!discardUrl){
11802                 this.defaultUrl = url;
11803             }
11804             if(typeof url == "function"){
11805                 url = url.call(this);
11806             }
11807
11808             method = method || (params ? "POST" : "GET");
11809             if(method == "GET"){
11810                 url = this.prepareUrl(url);
11811             }
11812
11813             var o = Roo.apply(cfg ||{}, {
11814                 url : url,
11815                 params: params,
11816                 success: this.successDelegate,
11817                 failure: this.failureDelegate,
11818                 callback: undefined,
11819                 timeout: (this.timeout*1000),
11820                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11821             });
11822
11823             this.transaction = Roo.Ajax.request(o);
11824         }
11825     },
11826
11827     /**
11828      * 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.
11829      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11830      * @param {String/HTMLElement} form The form Id or form element
11831      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11832      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11833      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11834      */
11835     formUpdate : function(form, url, reset, callback){
11836         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11837             if(typeof url == "function"){
11838                 url = url.call(this);
11839             }
11840             form = Roo.getDom(form);
11841             this.transaction = Roo.Ajax.request({
11842                 form: form,
11843                 url:url,
11844                 success: this.successDelegate,
11845                 failure: this.failureDelegate,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11848             });
11849             this.showLoading.defer(1, this);
11850         }
11851     },
11852
11853     /**
11854      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11856      */
11857     refresh : function(callback){
11858         if(this.defaultUrl == null){
11859             return;
11860         }
11861         this.update(this.defaultUrl, null, callback, true);
11862     },
11863
11864     /**
11865      * Set this element to auto refresh.
11866      * @param {Number} interval How often to update (in seconds).
11867      * @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)
11868      * @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}
11869      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11870      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11871      */
11872     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11873         if(refreshNow){
11874             this.update(url || this.defaultUrl, params, callback, true);
11875         }
11876         if(this.autoRefreshProcId){
11877             clearInterval(this.autoRefreshProcId);
11878         }
11879         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11880     },
11881
11882     /**
11883      * Stop auto refresh on this element.
11884      */
11885      stopAutoRefresh : function(){
11886         if(this.autoRefreshProcId){
11887             clearInterval(this.autoRefreshProcId);
11888             delete this.autoRefreshProcId;
11889         }
11890     },
11891
11892     isAutoRefreshing : function(){
11893        return this.autoRefreshProcId ? true : false;
11894     },
11895     /**
11896      * Called to update the element to "Loading" state. Override to perform custom action.
11897      */
11898     showLoading : function(){
11899         if(this.showLoadIndicator){
11900             this.el.update(this.indicatorText);
11901         }
11902     },
11903
11904     /**
11905      * Adds unique parameter to query string if disableCaching = true
11906      * @private
11907      */
11908     prepareUrl : function(url){
11909         if(this.disableCaching){
11910             var append = "_dc=" + (new Date().getTime());
11911             if(url.indexOf("?") !== -1){
11912                 url += "&" + append;
11913             }else{
11914                 url += "?" + append;
11915             }
11916         }
11917         return url;
11918     },
11919
11920     /**
11921      * @private
11922      */
11923     processSuccess : function(response){
11924         this.transaction = null;
11925         if(response.argument.form && response.argument.reset){
11926             try{ // put in try/catch since some older FF releases had problems with this
11927                 response.argument.form.reset();
11928             }catch(e){}
11929         }
11930         if(this.loadScripts){
11931             this.renderer.render(this.el, response, this,
11932                 this.updateComplete.createDelegate(this, [response]));
11933         }else{
11934             this.renderer.render(this.el, response, this);
11935             this.updateComplete(response);
11936         }
11937     },
11938
11939     updateComplete : function(response){
11940         this.fireEvent("update", this.el, response);
11941         if(typeof response.argument.callback == "function"){
11942             response.argument.callback(this.el, true, response);
11943         }
11944     },
11945
11946     /**
11947      * @private
11948      */
11949     processFailure : function(response){
11950         this.transaction = null;
11951         this.fireEvent("failure", this.el, response);
11952         if(typeof response.argument.callback == "function"){
11953             response.argument.callback(this.el, false, response);
11954         }
11955     },
11956
11957     /**
11958      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11959      * @param {Object} renderer The object implementing the render() method
11960      */
11961     setRenderer : function(renderer){
11962         this.renderer = renderer;
11963     },
11964
11965     getRenderer : function(){
11966        return this.renderer;
11967     },
11968
11969     /**
11970      * Set the defaultUrl used for updates
11971      * @param {String/Function} defaultUrl The url or a function to call to get the url
11972      */
11973     setDefaultUrl : function(defaultUrl){
11974         this.defaultUrl = defaultUrl;
11975     },
11976
11977     /**
11978      * Aborts the executing transaction
11979      */
11980     abort : function(){
11981         if(this.transaction){
11982             Roo.Ajax.abort(this.transaction);
11983         }
11984     },
11985
11986     /**
11987      * Returns true if an update is in progress
11988      * @return {Boolean}
11989      */
11990     isUpdating : function(){
11991         if(this.transaction){
11992             return Roo.Ajax.isLoading(this.transaction);
11993         }
11994         return false;
11995     }
11996 });
11997
11998 /**
11999  * @class Roo.UpdateManager.defaults
12000  * @static (not really - but it helps the doc tool)
12001  * The defaults collection enables customizing the default properties of UpdateManager
12002  */
12003    Roo.UpdateManager.defaults = {
12004        /**
12005          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12006          * @type Number
12007          */
12008          timeout : 30,
12009
12010          /**
12011          * True to process scripts by default (Defaults to false).
12012          * @type Boolean
12013          */
12014         loadScripts : false,
12015
12016         /**
12017         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12018         * @type String
12019         */
12020         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12021         /**
12022          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12023          * @type Boolean
12024          */
12025         disableCaching : false,
12026         /**
12027          * Whether to show indicatorText when loading (Defaults to true).
12028          * @type Boolean
12029          */
12030         showLoadIndicator : true,
12031         /**
12032          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12033          * @type String
12034          */
12035         indicatorText : '<div class="loading-indicator">Loading...</div>'
12036    };
12037
12038 /**
12039  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12040  *Usage:
12041  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12042  * @param {String/HTMLElement/Roo.Element} el The element to update
12043  * @param {String} url The url
12044  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12045  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12046  * @static
12047  * @deprecated
12048  * @member Roo.UpdateManager
12049  */
12050 Roo.UpdateManager.updateElement = function(el, url, params, options){
12051     var um = Roo.get(el, true).getUpdateManager();
12052     Roo.apply(um, options);
12053     um.update(url, params, options ? options.callback : null);
12054 };
12055 // alias for backwards compat
12056 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12057 /**
12058  * @class Roo.UpdateManager.BasicRenderer
12059  * Default Content renderer. Updates the elements innerHTML with the responseText.
12060  */
12061 Roo.UpdateManager.BasicRenderer = function(){};
12062
12063 Roo.UpdateManager.BasicRenderer.prototype = {
12064     /**
12065      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12066      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12067      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12068      * @param {Roo.Element} el The element being rendered
12069      * @param {Object} response The YUI Connect response object
12070      * @param {UpdateManager} updateManager The calling update manager
12071      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12072      */
12073      render : function(el, response, updateManager, callback){
12074         el.update(response.responseText, updateManager.loadScripts, callback);
12075     }
12076 };
12077 /*
12078  * Based on:
12079  * Ext JS Library 1.1.1
12080  * Copyright(c) 2006-2007, Ext JS, LLC.
12081  *
12082  * Originally Released Under LGPL - original licence link has changed is not relivant.
12083  *
12084  * Fork - LGPL
12085  * <script type="text/javascript">
12086  */
12087
12088 /**
12089  * @class Roo.util.DelayedTask
12090  * Provides a convenient method of performing setTimeout where a new
12091  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12092  * You can use this class to buffer
12093  * the keypress events for a certain number of milliseconds, and perform only if they stop
12094  * for that amount of time.
12095  * @constructor The parameters to this constructor serve as defaults and are not required.
12096  * @param {Function} fn (optional) The default function to timeout
12097  * @param {Object} scope (optional) The default scope of that timeout
12098  * @param {Array} args (optional) The default Array of arguments
12099  */
12100 Roo.util.DelayedTask = function(fn, scope, args){
12101     var id = null, d, t;
12102
12103     var call = function(){
12104         var now = new Date().getTime();
12105         if(now - t >= d){
12106             clearInterval(id);
12107             id = null;
12108             fn.apply(scope, args || []);
12109         }
12110     };
12111     /**
12112      * Cancels any pending timeout and queues a new one
12113      * @param {Number} delay The milliseconds to delay
12114      * @param {Function} newFn (optional) Overrides function passed to constructor
12115      * @param {Object} newScope (optional) Overrides scope passed to constructor
12116      * @param {Array} newArgs (optional) Overrides args passed to constructor
12117      */
12118     this.delay = function(delay, newFn, newScope, newArgs){
12119         if(id && delay != d){
12120             this.cancel();
12121         }
12122         d = delay;
12123         t = new Date().getTime();
12124         fn = newFn || fn;
12125         scope = newScope || scope;
12126         args = newArgs || args;
12127         if(!id){
12128             id = setInterval(call, d);
12129         }
12130     };
12131
12132     /**
12133      * Cancel the last queued timeout
12134      */
12135     this.cancel = function(){
12136         if(id){
12137             clearInterval(id);
12138             id = null;
12139         }
12140     };
12141 };/*
12142  * Based on:
12143  * Ext JS Library 1.1.1
12144  * Copyright(c) 2006-2007, Ext JS, LLC.
12145  *
12146  * Originally Released Under LGPL - original licence link has changed is not relivant.
12147  *
12148  * Fork - LGPL
12149  * <script type="text/javascript">
12150  */
12151  
12152  
12153 Roo.util.TaskRunner = function(interval){
12154     interval = interval || 10;
12155     var tasks = [], removeQueue = [];
12156     var id = 0;
12157     var running = false;
12158
12159     var stopThread = function(){
12160         running = false;
12161         clearInterval(id);
12162         id = 0;
12163     };
12164
12165     var startThread = function(){
12166         if(!running){
12167             running = true;
12168             id = setInterval(runTasks, interval);
12169         }
12170     };
12171
12172     var removeTask = function(task){
12173         removeQueue.push(task);
12174         if(task.onStop){
12175             task.onStop();
12176         }
12177     };
12178
12179     var runTasks = function(){
12180         if(removeQueue.length > 0){
12181             for(var i = 0, len = removeQueue.length; i < len; i++){
12182                 tasks.remove(removeQueue[i]);
12183             }
12184             removeQueue = [];
12185             if(tasks.length < 1){
12186                 stopThread();
12187                 return;
12188             }
12189         }
12190         var now = new Date().getTime();
12191         for(var i = 0, len = tasks.length; i < len; ++i){
12192             var t = tasks[i];
12193             var itime = now - t.taskRunTime;
12194             if(t.interval <= itime){
12195                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12196                 t.taskRunTime = now;
12197                 if(rt === false || t.taskRunCount === t.repeat){
12198                     removeTask(t);
12199                     return;
12200                 }
12201             }
12202             if(t.duration && t.duration <= (now - t.taskStartTime)){
12203                 removeTask(t);
12204             }
12205         }
12206     };
12207
12208     /**
12209      * Queues a new task.
12210      * @param {Object} task
12211      */
12212     this.start = function(task){
12213         tasks.push(task);
12214         task.taskStartTime = new Date().getTime();
12215         task.taskRunTime = 0;
12216         task.taskRunCount = 0;
12217         startThread();
12218         return task;
12219     };
12220
12221     this.stop = function(task){
12222         removeTask(task);
12223         return task;
12224     };
12225
12226     this.stopAll = function(){
12227         stopThread();
12228         for(var i = 0, len = tasks.length; i < len; i++){
12229             if(tasks[i].onStop){
12230                 tasks[i].onStop();
12231             }
12232         }
12233         tasks = [];
12234         removeQueue = [];
12235     };
12236 };
12237
12238 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12239  * Based on:
12240  * Ext JS Library 1.1.1
12241  * Copyright(c) 2006-2007, Ext JS, LLC.
12242  *
12243  * Originally Released Under LGPL - original licence link has changed is not relivant.
12244  *
12245  * Fork - LGPL
12246  * <script type="text/javascript">
12247  */
12248
12249  
12250 /**
12251  * @class Roo.util.MixedCollection
12252  * @extends Roo.util.Observable
12253  * A Collection class that maintains both numeric indexes and keys and exposes events.
12254  * @constructor
12255  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12256  * collection (defaults to false)
12257  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12258  * and return the key value for that item.  This is used when available to look up the key on items that
12259  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12260  * equivalent to providing an implementation for the {@link #getKey} method.
12261  */
12262 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12263     this.items = [];
12264     this.map = {};
12265     this.keys = [];
12266     this.length = 0;
12267     this.addEvents({
12268         /**
12269          * @event clear
12270          * Fires when the collection is cleared.
12271          */
12272         "clear" : true,
12273         /**
12274          * @event add
12275          * Fires when an item is added to the collection.
12276          * @param {Number} index The index at which the item was added.
12277          * @param {Object} o The item added.
12278          * @param {String} key The key associated with the added item.
12279          */
12280         "add" : true,
12281         /**
12282          * @event replace
12283          * Fires when an item is replaced in the collection.
12284          * @param {String} key he key associated with the new added.
12285          * @param {Object} old The item being replaced.
12286          * @param {Object} new The new item.
12287          */
12288         "replace" : true,
12289         /**
12290          * @event remove
12291          * Fires when an item is removed from the collection.
12292          * @param {Object} o The item being removed.
12293          * @param {String} key (optional) The key associated with the removed item.
12294          */
12295         "remove" : true,
12296         "sort" : true
12297     });
12298     this.allowFunctions = allowFunctions === true;
12299     if(keyFn){
12300         this.getKey = keyFn;
12301     }
12302     Roo.util.MixedCollection.superclass.constructor.call(this);
12303 };
12304
12305 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12306     allowFunctions : false,
12307     
12308 /**
12309  * Adds an item to the collection.
12310  * @param {String} key The key to associate with the item
12311  * @param {Object} o The item to add.
12312  * @return {Object} The item added.
12313  */
12314     add : function(key, o){
12315         if(arguments.length == 1){
12316             o = arguments[0];
12317             key = this.getKey(o);
12318         }
12319         if(typeof key == "undefined" || key === null){
12320             this.length++;
12321             this.items.push(o);
12322             this.keys.push(null);
12323         }else{
12324             var old = this.map[key];
12325             if(old){
12326                 return this.replace(key, o);
12327             }
12328             this.length++;
12329             this.items.push(o);
12330             this.map[key] = o;
12331             this.keys.push(key);
12332         }
12333         this.fireEvent("add", this.length-1, o, key);
12334         return o;
12335     },
12336        
12337 /**
12338   * MixedCollection has a generic way to fetch keys if you implement getKey.
12339 <pre><code>
12340 // normal way
12341 var mc = new Roo.util.MixedCollection();
12342 mc.add(someEl.dom.id, someEl);
12343 mc.add(otherEl.dom.id, otherEl);
12344 //and so on
12345
12346 // using getKey
12347 var mc = new Roo.util.MixedCollection();
12348 mc.getKey = function(el){
12349    return el.dom.id;
12350 };
12351 mc.add(someEl);
12352 mc.add(otherEl);
12353
12354 // or via the constructor
12355 var mc = new Roo.util.MixedCollection(false, function(el){
12356    return el.dom.id;
12357 });
12358 mc.add(someEl);
12359 mc.add(otherEl);
12360 </code></pre>
12361  * @param o {Object} The item for which to find the key.
12362  * @return {Object} The key for the passed item.
12363  */
12364     getKey : function(o){
12365          return o.id; 
12366     },
12367    
12368 /**
12369  * Replaces an item in the collection.
12370  * @param {String} key The key associated with the item to replace, or the item to replace.
12371  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12372  * @return {Object}  The new item.
12373  */
12374     replace : function(key, o){
12375         if(arguments.length == 1){
12376             o = arguments[0];
12377             key = this.getKey(o);
12378         }
12379         var old = this.item(key);
12380         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12381              return this.add(key, o);
12382         }
12383         var index = this.indexOfKey(key);
12384         this.items[index] = o;
12385         this.map[key] = o;
12386         this.fireEvent("replace", key, old, o);
12387         return o;
12388     },
12389    
12390 /**
12391  * Adds all elements of an Array or an Object to the collection.
12392  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12393  * an Array of values, each of which are added to the collection.
12394  */
12395     addAll : function(objs){
12396         if(arguments.length > 1 || objs instanceof Array){
12397             var args = arguments.length > 1 ? arguments : objs;
12398             for(var i = 0, len = args.length; i < len; i++){
12399                 this.add(args[i]);
12400             }
12401         }else{
12402             for(var key in objs){
12403                 if(this.allowFunctions || typeof objs[key] != "function"){
12404                     this.add(key, objs[key]);
12405                 }
12406             }
12407         }
12408     },
12409    
12410 /**
12411  * Executes the specified function once for every item in the collection, passing each
12412  * item as the first and only parameter. returning false from the function will stop the iteration.
12413  * @param {Function} fn The function to execute for each item.
12414  * @param {Object} scope (optional) The scope in which to execute the function.
12415  */
12416     each : function(fn, scope){
12417         var items = [].concat(this.items); // each safe for removal
12418         for(var i = 0, len = items.length; i < len; i++){
12419             if(fn.call(scope || items[i], items[i], i, len) === false){
12420                 break;
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every key in the collection, passing each
12427  * key, and its associated item as the first two parameters.
12428  * @param {Function} fn The function to execute for each item.
12429  * @param {Object} scope (optional) The scope in which to execute the function.
12430  */
12431     eachKey : function(fn, scope){
12432         for(var i = 0, len = this.keys.length; i < len; i++){
12433             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12434         }
12435     },
12436    
12437 /**
12438  * Returns the first item in the collection which elicits a true return value from the
12439  * passed selection function.
12440  * @param {Function} fn The selection function to execute for each item.
12441  * @param {Object} scope (optional) The scope in which to execute the function.
12442  * @return {Object} The first item in the collection which returned true from the selection function.
12443  */
12444     find : function(fn, scope){
12445         for(var i = 0, len = this.items.length; i < len; i++){
12446             if(fn.call(scope || window, this.items[i], this.keys[i])){
12447                 return this.items[i];
12448             }
12449         }
12450         return null;
12451     },
12452    
12453 /**
12454  * Inserts an item at the specified index in the collection.
12455  * @param {Number} index The index to insert the item at.
12456  * @param {String} key The key to associate with the new item, or the item itself.
12457  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12458  * @return {Object} The item inserted.
12459  */
12460     insert : function(index, key, o){
12461         if(arguments.length == 2){
12462             o = arguments[1];
12463             key = this.getKey(o);
12464         }
12465         if(index >= this.length){
12466             return this.add(key, o);
12467         }
12468         this.length++;
12469         this.items.splice(index, 0, o);
12470         if(typeof key != "undefined" && key != null){
12471             this.map[key] = o;
12472         }
12473         this.keys.splice(index, 0, key);
12474         this.fireEvent("add", index, o, key);
12475         return o;
12476     },
12477    
12478 /**
12479  * Removed an item from the collection.
12480  * @param {Object} o The item to remove.
12481  * @return {Object} The item removed.
12482  */
12483     remove : function(o){
12484         return this.removeAt(this.indexOf(o));
12485     },
12486    
12487 /**
12488  * Remove an item from a specified index in the collection.
12489  * @param {Number} index The index within the collection of the item to remove.
12490  */
12491     removeAt : function(index){
12492         if(index < this.length && index >= 0){
12493             this.length--;
12494             var o = this.items[index];
12495             this.items.splice(index, 1);
12496             var key = this.keys[index];
12497             if(typeof key != "undefined"){
12498                 delete this.map[key];
12499             }
12500             this.keys.splice(index, 1);
12501             this.fireEvent("remove", o, key);
12502         }
12503     },
12504    
12505 /**
12506  * Removed an item associated with the passed key fom the collection.
12507  * @param {String} key The key of the item to remove.
12508  */
12509     removeKey : function(key){
12510         return this.removeAt(this.indexOfKey(key));
12511     },
12512    
12513 /**
12514  * Returns the number of items in the collection.
12515  * @return {Number} the number of items in the collection.
12516  */
12517     getCount : function(){
12518         return this.length; 
12519     },
12520    
12521 /**
12522  * Returns index within the collection of the passed Object.
12523  * @param {Object} o The item to find the index of.
12524  * @return {Number} index of the item.
12525  */
12526     indexOf : function(o){
12527         if(!this.items.indexOf){
12528             for(var i = 0, len = this.items.length; i < len; i++){
12529                 if(this.items[i] == o) return i;
12530             }
12531             return -1;
12532         }else{
12533             return this.items.indexOf(o);
12534         }
12535     },
12536    
12537 /**
12538  * Returns index within the collection of the passed key.
12539  * @param {String} key The key to find the index of.
12540  * @return {Number} index of the key.
12541  */
12542     indexOfKey : function(key){
12543         if(!this.keys.indexOf){
12544             for(var i = 0, len = this.keys.length; i < len; i++){
12545                 if(this.keys[i] == key) return i;
12546             }
12547             return -1;
12548         }else{
12549             return this.keys.indexOf(key);
12550         }
12551     },
12552    
12553 /**
12554  * Returns the item associated with the passed key OR index. Key has priority over index.
12555  * @param {String/Number} key The key or index of the item.
12556  * @return {Object} The item associated with the passed key.
12557  */
12558     item : function(key){
12559         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12560         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12561     },
12562     
12563 /**
12564  * Returns the item at the specified index.
12565  * @param {Number} index The index of the item.
12566  * @return {Object}
12567  */
12568     itemAt : function(index){
12569         return this.items[index];
12570     },
12571     
12572 /**
12573  * Returns the item associated with the passed key.
12574  * @param {String/Number} key The key of the item.
12575  * @return {Object} The item associated with the passed key.
12576  */
12577     key : function(key){
12578         return this.map[key];
12579     },
12580    
12581 /**
12582  * Returns true if the collection contains the passed Object as an item.
12583  * @param {Object} o  The Object to look for in the collection.
12584  * @return {Boolean} True if the collection contains the Object as an item.
12585  */
12586     contains : function(o){
12587         return this.indexOf(o) != -1;
12588     },
12589    
12590 /**
12591  * Returns true if the collection contains the passed Object as a key.
12592  * @param {String} key The key to look for in the collection.
12593  * @return {Boolean} True if the collection contains the Object as a key.
12594  */
12595     containsKey : function(key){
12596         return typeof this.map[key] != "undefined";
12597     },
12598    
12599 /**
12600  * Removes all items from the collection.
12601  */
12602     clear : function(){
12603         this.length = 0;
12604         this.items = [];
12605         this.keys = [];
12606         this.map = {};
12607         this.fireEvent("clear");
12608     },
12609    
12610 /**
12611  * Returns the first item in the collection.
12612  * @return {Object} the first item in the collection..
12613  */
12614     first : function(){
12615         return this.items[0]; 
12616     },
12617    
12618 /**
12619  * Returns the last item in the collection.
12620  * @return {Object} the last item in the collection..
12621  */
12622     last : function(){
12623         return this.items[this.length-1];   
12624     },
12625     
12626     _sort : function(property, dir, fn){
12627         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12628         fn = fn || function(a, b){
12629             return a-b;
12630         };
12631         var c = [], k = this.keys, items = this.items;
12632         for(var i = 0, len = items.length; i < len; i++){
12633             c[c.length] = {key: k[i], value: items[i], index: i};
12634         }
12635         c.sort(function(a, b){
12636             var v = fn(a[property], b[property]) * dsc;
12637             if(v == 0){
12638                 v = (a.index < b.index ? -1 : 1);
12639             }
12640             return v;
12641         });
12642         for(var i = 0, len = c.length; i < len; i++){
12643             items[i] = c[i].value;
12644             k[i] = c[i].key;
12645         }
12646         this.fireEvent("sort", this);
12647     },
12648     
12649     /**
12650      * Sorts this collection with the passed comparison function
12651      * @param {String} direction (optional) "ASC" or "DESC"
12652      * @param {Function} fn (optional) comparison function
12653      */
12654     sort : function(dir, fn){
12655         this._sort("value", dir, fn);
12656     },
12657     
12658     /**
12659      * Sorts this collection by keys
12660      * @param {String} direction (optional) "ASC" or "DESC"
12661      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12662      */
12663     keySort : function(dir, fn){
12664         this._sort("key", dir, fn || function(a, b){
12665             return String(a).toUpperCase()-String(b).toUpperCase();
12666         });
12667     },
12668     
12669     /**
12670      * Returns a range of items in this collection
12671      * @param {Number} startIndex (optional) defaults to 0
12672      * @param {Number} endIndex (optional) default to the last item
12673      * @return {Array} An array of items
12674      */
12675     getRange : function(start, end){
12676         var items = this.items;
12677         if(items.length < 1){
12678             return [];
12679         }
12680         start = start || 0;
12681         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12682         var r = [];
12683         if(start <= end){
12684             for(var i = start; i <= end; i++) {
12685                     r[r.length] = items[i];
12686             }
12687         }else{
12688             for(var i = start; i >= end; i--) {
12689                     r[r.length] = items[i];
12690             }
12691         }
12692         return r;
12693     },
12694         
12695     /**
12696      * Filter the <i>objects</i> in this collection by a specific property. 
12697      * Returns a new collection that has been filtered.
12698      * @param {String} property A property on your objects
12699      * @param {String/RegExp} value Either string that the property values 
12700      * should start with or a RegExp to test against the property
12701      * @return {MixedCollection} The new filtered collection
12702      */
12703     filter : function(property, value){
12704         if(!value.exec){ // not a regex
12705             value = String(value);
12706             if(value.length == 0){
12707                 return this.clone();
12708             }
12709             value = new RegExp("^" + Roo.escapeRe(value), "i");
12710         }
12711         return this.filterBy(function(o){
12712             return o && value.test(o[property]);
12713         });
12714         },
12715     
12716     /**
12717      * Filter by a function. * Returns a new collection that has been filtered.
12718      * The passed function will be called with each 
12719      * object in the collection. If the function returns true, the value is included 
12720      * otherwise it is filtered.
12721      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12722      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12723      * @return {MixedCollection} The new filtered collection
12724      */
12725     filterBy : function(fn, scope){
12726         var r = new Roo.util.MixedCollection();
12727         r.getKey = this.getKey;
12728         var k = this.keys, it = this.items;
12729         for(var i = 0, len = it.length; i < len; i++){
12730             if(fn.call(scope||this, it[i], k[i])){
12731                                 r.add(k[i], it[i]);
12732                         }
12733         }
12734         return r;
12735     },
12736     
12737     /**
12738      * Creates a duplicate of this collection
12739      * @return {MixedCollection}
12740      */
12741     clone : function(){
12742         var r = new Roo.util.MixedCollection();
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             r.add(k[i], it[i]);
12746         }
12747         r.getKey = this.getKey;
12748         return r;
12749     }
12750 });
12751 /**
12752  * Returns the item associated with the passed key or index.
12753  * @method
12754  * @param {String/Number} key The key or index of the item.
12755  * @return {Object} The item associated with the passed key.
12756  */
12757 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12758  * Based on:
12759  * Ext JS Library 1.1.1
12760  * Copyright(c) 2006-2007, Ext JS, LLC.
12761  *
12762  * Originally Released Under LGPL - original licence link has changed is not relivant.
12763  *
12764  * Fork - LGPL
12765  * <script type="text/javascript">
12766  */
12767 /**
12768  * @class Roo.util.JSON
12769  * Modified version of Douglas Crockford"s json.js that doesn"t
12770  * mess with the Object prototype 
12771  * http://www.json.org/js.html
12772  * @singleton
12773  */
12774 Roo.util.JSON = new (function(){
12775     var useHasOwn = {}.hasOwnProperty ? true : false;
12776     
12777     // crashes Safari in some instances
12778     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12779     
12780     var pad = function(n) {
12781         return n < 10 ? "0" + n : n;
12782     };
12783     
12784     var m = {
12785         "\b": '\\b',
12786         "\t": '\\t',
12787         "\n": '\\n',
12788         "\f": '\\f',
12789         "\r": '\\r',
12790         '"' : '\\"',
12791         "\\": '\\\\'
12792     };
12793
12794     var encodeString = function(s){
12795         if (/["\\\x00-\x1f]/.test(s)) {
12796             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12797                 var c = m[b];
12798                 if(c){
12799                     return c;
12800                 }
12801                 c = b.charCodeAt();
12802                 return "\\u00" +
12803                     Math.floor(c / 16).toString(16) +
12804                     (c % 16).toString(16);
12805             }) + '"';
12806         }
12807         return '"' + s + '"';
12808     };
12809     
12810     var encodeArray = function(o){
12811         var a = ["["], b, i, l = o.length, v;
12812             for (i = 0; i < l; i += 1) {
12813                 v = o[i];
12814                 switch (typeof v) {
12815                     case "undefined":
12816                     case "function":
12817                     case "unknown":
12818                         break;
12819                     default:
12820                         if (b) {
12821                             a.push(',');
12822                         }
12823                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12824                         b = true;
12825                 }
12826             }
12827             a.push("]");
12828             return a.join("");
12829     };
12830     
12831     var encodeDate = function(o){
12832         return '"' + o.getFullYear() + "-" +
12833                 pad(o.getMonth() + 1) + "-" +
12834                 pad(o.getDate()) + "T" +
12835                 pad(o.getHours()) + ":" +
12836                 pad(o.getMinutes()) + ":" +
12837                 pad(o.getSeconds()) + '"';
12838     };
12839     
12840     /**
12841      * Encodes an Object, Array or other value
12842      * @param {Mixed} o The variable to encode
12843      * @return {String} The JSON string
12844      */
12845     this.encode = function(o)
12846     {
12847         // should this be extended to fully wrap stringify..
12848         
12849         if(typeof o == "undefined" || o === null){
12850             return "null";
12851         }else if(o instanceof Array){
12852             return encodeArray(o);
12853         }else if(o instanceof Date){
12854             return encodeDate(o);
12855         }else if(typeof o == "string"){
12856             return encodeString(o);
12857         }else if(typeof o == "number"){
12858             return isFinite(o) ? String(o) : "null";
12859         }else if(typeof o == "boolean"){
12860             return String(o);
12861         }else {
12862             var a = ["{"], b, i, v;
12863             for (i in o) {
12864                 if(!useHasOwn || o.hasOwnProperty(i)) {
12865                     v = o[i];
12866                     switch (typeof v) {
12867                     case "undefined":
12868                     case "function":
12869                     case "unknown":
12870                         break;
12871                     default:
12872                         if(b){
12873                             a.push(',');
12874                         }
12875                         a.push(this.encode(i), ":",
12876                                 v === null ? "null" : this.encode(v));
12877                         b = true;
12878                     }
12879                 }
12880             }
12881             a.push("}");
12882             return a.join("");
12883         }
12884     };
12885     
12886     /**
12887      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12888      * @param {String} json The JSON string
12889      * @return {Object} The resulting object
12890      */
12891     this.decode = function(json){
12892         
12893         return  /** eval:var:json */ eval("(" + json + ')');
12894     };
12895 })();
12896 /** 
12897  * Shorthand for {@link Roo.util.JSON#encode}
12898  * @member Roo encode 
12899  * @method */
12900 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12901 /** 
12902  * Shorthand for {@link Roo.util.JSON#decode}
12903  * @member Roo decode 
12904  * @method */
12905 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12906 /*
12907  * Based on:
12908  * Ext JS Library 1.1.1
12909  * Copyright(c) 2006-2007, Ext JS, LLC.
12910  *
12911  * Originally Released Under LGPL - original licence link has changed is not relivant.
12912  *
12913  * Fork - LGPL
12914  * <script type="text/javascript">
12915  */
12916  
12917 /**
12918  * @class Roo.util.Format
12919  * Reusable data formatting functions
12920  * @singleton
12921  */
12922 Roo.util.Format = function(){
12923     var trimRe = /^\s+|\s+$/g;
12924     return {
12925         /**
12926          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12927          * @param {String} value The string to truncate
12928          * @param {Number} length The maximum length to allow before truncating
12929          * @return {String} The converted text
12930          */
12931         ellipsis : function(value, len){
12932             if(value && value.length > len){
12933                 return value.substr(0, len-3)+"...";
12934             }
12935             return value;
12936         },
12937
12938         /**
12939          * Checks a reference and converts it to empty string if it is undefined
12940          * @param {Mixed} value Reference to check
12941          * @return {Mixed} Empty string if converted, otherwise the original value
12942          */
12943         undef : function(value){
12944             return typeof value != "undefined" ? value : "";
12945         },
12946
12947         /**
12948          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12949          * @param {String} value The string to encode
12950          * @return {String} The encoded text
12951          */
12952         htmlEncode : function(value){
12953             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12954         },
12955
12956         /**
12957          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12958          * @param {String} value The string to decode
12959          * @return {String} The decoded text
12960          */
12961         htmlDecode : function(value){
12962             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12963         },
12964
12965         /**
12966          * Trims any whitespace from either side of a string
12967          * @param {String} value The text to trim
12968          * @return {String} The trimmed text
12969          */
12970         trim : function(value){
12971             return String(value).replace(trimRe, "");
12972         },
12973
12974         /**
12975          * Returns a substring from within an original string
12976          * @param {String} value The original text
12977          * @param {Number} start The start index of the substring
12978          * @param {Number} length The length of the substring
12979          * @return {String} The substring
12980          */
12981         substr : function(value, start, length){
12982             return String(value).substr(start, length);
12983         },
12984
12985         /**
12986          * Converts a string to all lower case letters
12987          * @param {String} value The text to convert
12988          * @return {String} The converted text
12989          */
12990         lowercase : function(value){
12991             return String(value).toLowerCase();
12992         },
12993
12994         /**
12995          * Converts a string to all upper case letters
12996          * @param {String} value The text to convert
12997          * @return {String} The converted text
12998          */
12999         uppercase : function(value){
13000             return String(value).toUpperCase();
13001         },
13002
13003         /**
13004          * Converts the first character only of a string to upper case
13005          * @param {String} value The text to convert
13006          * @return {String} The converted text
13007          */
13008         capitalize : function(value){
13009             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13010         },
13011
13012         // private
13013         call : function(value, fn){
13014             if(arguments.length > 2){
13015                 var args = Array.prototype.slice.call(arguments, 2);
13016                 args.unshift(value);
13017                  
13018                 return /** eval:var:value */  eval(fn).apply(window, args);
13019             }else{
13020                 /** eval:var:value */
13021                 return /** eval:var:value */ eval(fn).call(window, value);
13022             }
13023         },
13024
13025        
13026         /**
13027          * safer version of Math.toFixed..??/
13028          * @param {Number/String} value The numeric value to format
13029          * @param {Number/String} value Decimal places 
13030          * @return {String} The formatted currency string
13031          */
13032         toFixed : function(v, n)
13033         {
13034             // why not use to fixed - precision is buggered???
13035             if (!n) {
13036                 return Math.round(v-0);
13037             }
13038             var fact = Math.pow(10,n+1);
13039             v = (Math.round((v-0)*fact))/fact;
13040             var z = (''+fact).substring(2);
13041             if (v == Math.floor(v)) {
13042                 return Math.floor(v) + '.' + z;
13043             }
13044             
13045             // now just padd decimals..
13046             var ps = String(v).split('.');
13047             var fd = (ps[1] + z);
13048             var r = fd.substring(0,n); 
13049             var rm = fd.substring(n); 
13050             if (rm < 5) {
13051                 return ps[0] + '.' + r;
13052             }
13053             r*=1; // turn it into a number;
13054             r++;
13055             if (String(r).length != n) {
13056                 ps[0]*=1;
13057                 ps[0]++;
13058                 r = String(r).substring(1); // chop the end off.
13059             }
13060             
13061             return ps[0] + '.' + r;
13062              
13063         },
13064         
13065         /**
13066          * Format a number as US currency
13067          * @param {Number/String} value The numeric value to format
13068          * @return {String} The formatted currency string
13069          */
13070         usMoney : function(v){
13071             v = (Math.round((v-0)*100))/100;
13072             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13073             v = String(v);
13074             var ps = v.split('.');
13075             var whole = ps[0];
13076             var sub = ps[1] ? '.'+ ps[1] : '.00';
13077             var r = /(\d+)(\d{3})/;
13078             while (r.test(whole)) {
13079                 whole = whole.replace(r, '$1' + ',' + '$2');
13080             }
13081             return "$" + whole + sub ;
13082         },
13083         
13084         /**
13085          * Parse a value into a formatted date using the specified format pattern.
13086          * @param {Mixed} value The value to format
13087          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13088          * @return {String} The formatted date string
13089          */
13090         date : function(v, format){
13091             if(!v){
13092                 return "";
13093             }
13094             if(!(v instanceof Date)){
13095                 v = new Date(Date.parse(v));
13096             }
13097             return v.dateFormat(format || "m/d/Y");
13098         },
13099
13100         /**
13101          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13102          * @param {String} format Any valid date format string
13103          * @return {Function} The date formatting function
13104          */
13105         dateRenderer : function(format){
13106             return function(v){
13107                 return Roo.util.Format.date(v, format);  
13108             };
13109         },
13110
13111         // private
13112         stripTagsRE : /<\/?[^>]+>/gi,
13113         
13114         /**
13115          * Strips all HTML tags
13116          * @param {Mixed} value The text from which to strip tags
13117          * @return {String} The stripped text
13118          */
13119         stripTags : function(v){
13120             return !v ? v : String(v).replace(this.stripTagsRE, "");
13121         }
13122     };
13123 }();/*
13124  * Based on:
13125  * Ext JS Library 1.1.1
13126  * Copyright(c) 2006-2007, Ext JS, LLC.
13127  *
13128  * Originally Released Under LGPL - original licence link has changed is not relivant.
13129  *
13130  * Fork - LGPL
13131  * <script type="text/javascript">
13132  */
13133
13134
13135  
13136
13137 /**
13138  * @class Roo.MasterTemplate
13139  * @extends Roo.Template
13140  * Provides a template that can have child templates. The syntax is:
13141 <pre><code>
13142 var t = new Roo.MasterTemplate(
13143         '&lt;select name="{name}"&gt;',
13144                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13145         '&lt;/select&gt;'
13146 );
13147 t.add('options', {value: 'foo', text: 'bar'});
13148 // or you can add multiple child elements in one shot
13149 t.addAll('options', [
13150     {value: 'foo', text: 'bar'},
13151     {value: 'foo2', text: 'bar2'},
13152     {value: 'foo3', text: 'bar3'}
13153 ]);
13154 // then append, applying the master template values
13155 t.append('my-form', {name: 'my-select'});
13156 </code></pre>
13157 * A name attribute for the child template is not required if you have only one child
13158 * template or you want to refer to them by index.
13159  */
13160 Roo.MasterTemplate = function(){
13161     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13162     this.originalHtml = this.html;
13163     var st = {};
13164     var m, re = this.subTemplateRe;
13165     re.lastIndex = 0;
13166     var subIndex = 0;
13167     while(m = re.exec(this.html)){
13168         var name = m[1], content = m[2];
13169         st[subIndex] = {
13170             name: name,
13171             index: subIndex,
13172             buffer: [],
13173             tpl : new Roo.Template(content)
13174         };
13175         if(name){
13176             st[name] = st[subIndex];
13177         }
13178         st[subIndex].tpl.compile();
13179         st[subIndex].tpl.call = this.call.createDelegate(this);
13180         subIndex++;
13181     }
13182     this.subCount = subIndex;
13183     this.subs = st;
13184 };
13185 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13186     /**
13187     * The regular expression used to match sub templates
13188     * @type RegExp
13189     * @property
13190     */
13191     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13192
13193     /**
13194      * Applies the passed values to a child template.
13195      * @param {String/Number} name (optional) The name or index of the child template
13196      * @param {Array/Object} values The values to be applied to the template
13197      * @return {MasterTemplate} this
13198      */
13199      add : function(name, values){
13200         if(arguments.length == 1){
13201             values = arguments[0];
13202             name = 0;
13203         }
13204         var s = this.subs[name];
13205         s.buffer[s.buffer.length] = s.tpl.apply(values);
13206         return this;
13207     },
13208
13209     /**
13210      * Applies all the passed values to a child template.
13211      * @param {String/Number} name (optional) The name or index of the child template
13212      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13213      * @param {Boolean} reset (optional) True to reset the template first
13214      * @return {MasterTemplate} this
13215      */
13216     fill : function(name, values, reset){
13217         var a = arguments;
13218         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13219             values = a[0];
13220             name = 0;
13221             reset = a[1];
13222         }
13223         if(reset){
13224             this.reset();
13225         }
13226         for(var i = 0, len = values.length; i < len; i++){
13227             this.add(name, values[i]);
13228         }
13229         return this;
13230     },
13231
13232     /**
13233      * Resets the template for reuse
13234      * @return {MasterTemplate} this
13235      */
13236      reset : function(){
13237         var s = this.subs;
13238         for(var i = 0; i < this.subCount; i++){
13239             s[i].buffer = [];
13240         }
13241         return this;
13242     },
13243
13244     applyTemplate : function(values){
13245         var s = this.subs;
13246         var replaceIndex = -1;
13247         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13248             return s[++replaceIndex].buffer.join("");
13249         });
13250         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13251     },
13252
13253     apply : function(){
13254         return this.applyTemplate.apply(this, arguments);
13255     },
13256
13257     compile : function(){return this;}
13258 });
13259
13260 /**
13261  * Alias for fill().
13262  * @method
13263  */
13264 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13265  /**
13266  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13267  * var tpl = Roo.MasterTemplate.from('element-id');
13268  * @param {String/HTMLElement} el
13269  * @param {Object} config
13270  * @static
13271  */
13272 Roo.MasterTemplate.from = function(el, config){
13273     el = Roo.getDom(el);
13274     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13275 };/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285
13286  
13287 /**
13288  * @class Roo.util.CSS
13289  * Utility class for manipulating CSS rules
13290  * @singleton
13291  */
13292 Roo.util.CSS = function(){
13293         var rules = null;
13294         var doc = document;
13295
13296     var camelRe = /(-[a-z])/gi;
13297     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13298
13299    return {
13300    /**
13301     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13302     * tag and appended to the HEAD of the document.
13303     * @param {String|Object} cssText The text containing the css rules
13304     * @param {String} id An id to add to the stylesheet for later removal
13305     * @return {StyleSheet}
13306     */
13307     createStyleSheet : function(cssText, id){
13308         var ss;
13309         var head = doc.getElementsByTagName("head")[0];
13310         var nrules = doc.createElement("style");
13311         nrules.setAttribute("type", "text/css");
13312         if(id){
13313             nrules.setAttribute("id", id);
13314         }
13315         if (typeof(cssText) != 'string') {
13316             // support object maps..
13317             // not sure if this a good idea.. 
13318             // perhaps it should be merged with the general css handling
13319             // and handle js style props.
13320             var cssTextNew = [];
13321             for(var n in cssText) {
13322                 var citems = [];
13323                 for(var k in cssText[n]) {
13324                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13325                 }
13326                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13327                 
13328             }
13329             cssText = cssTextNew.join("\n");
13330             
13331         }
13332        
13333        
13334        if(Roo.isIE){
13335            head.appendChild(nrules);
13336            ss = nrules.styleSheet;
13337            ss.cssText = cssText;
13338        }else{
13339            try{
13340                 nrules.appendChild(doc.createTextNode(cssText));
13341            }catch(e){
13342                nrules.cssText = cssText; 
13343            }
13344            head.appendChild(nrules);
13345            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13346        }
13347        this.cacheStyleSheet(ss);
13348        return ss;
13349    },
13350
13351    /**
13352     * Removes a style or link tag by id
13353     * @param {String} id The id of the tag
13354     */
13355    removeStyleSheet : function(id){
13356        var existing = doc.getElementById(id);
13357        if(existing){
13358            existing.parentNode.removeChild(existing);
13359        }
13360    },
13361
13362    /**
13363     * Dynamically swaps an existing stylesheet reference for a new one
13364     * @param {String} id The id of an existing link tag to remove
13365     * @param {String} url The href of the new stylesheet to include
13366     */
13367    swapStyleSheet : function(id, url){
13368        this.removeStyleSheet(id);
13369        var ss = doc.createElement("link");
13370        ss.setAttribute("rel", "stylesheet");
13371        ss.setAttribute("type", "text/css");
13372        ss.setAttribute("id", id);
13373        ss.setAttribute("href", url);
13374        doc.getElementsByTagName("head")[0].appendChild(ss);
13375    },
13376    
13377    /**
13378     * Refresh the rule cache if you have dynamically added stylesheets
13379     * @return {Object} An object (hash) of rules indexed by selector
13380     */
13381    refreshCache : function(){
13382        return this.getRules(true);
13383    },
13384
13385    // private
13386    cacheStyleSheet : function(stylesheet){
13387        if(!rules){
13388            rules = {};
13389        }
13390        try{// try catch for cross domain access issue
13391            var ssRules = stylesheet.cssRules || stylesheet.rules;
13392            for(var j = ssRules.length-1; j >= 0; --j){
13393                rules[ssRules[j].selectorText] = ssRules[j];
13394            }
13395        }catch(e){}
13396    },
13397    
13398    /**
13399     * Gets all css rules for the document
13400     * @param {Boolean} refreshCache true to refresh the internal cache
13401     * @return {Object} An object (hash) of rules indexed by selector
13402     */
13403    getRules : function(refreshCache){
13404                 if(rules == null || refreshCache){
13405                         rules = {};
13406                         var ds = doc.styleSheets;
13407                         for(var i =0, len = ds.length; i < len; i++){
13408                             try{
13409                         this.cacheStyleSheet(ds[i]);
13410                     }catch(e){} 
13411                 }
13412                 }
13413                 return rules;
13414         },
13415         
13416         /**
13417     * Gets an an individual CSS rule by selector(s)
13418     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13419     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13420     * @return {CSSRule} The CSS rule or null if one is not found
13421     */
13422    getRule : function(selector, refreshCache){
13423                 var rs = this.getRules(refreshCache);
13424                 if(!(selector instanceof Array)){
13425                     return rs[selector];
13426                 }
13427                 for(var i = 0; i < selector.length; i++){
13428                         if(rs[selector[i]]){
13429                                 return rs[selector[i]];
13430                         }
13431                 }
13432                 return null;
13433         },
13434         
13435         
13436         /**
13437     * Updates a rule property
13438     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13439     * @param {String} property The css property
13440     * @param {String} value The new value for the property
13441     * @return {Boolean} true If a rule was found and updated
13442     */
13443    updateRule : function(selector, property, value){
13444                 if(!(selector instanceof Array)){
13445                         var rule = this.getRule(selector);
13446                         if(rule){
13447                                 rule.style[property.replace(camelRe, camelFn)] = value;
13448                                 return true;
13449                         }
13450                 }else{
13451                         for(var i = 0; i < selector.length; i++){
13452                                 if(this.updateRule(selector[i], property, value)){
13453                                         return true;
13454                                 }
13455                         }
13456                 }
13457                 return false;
13458         }
13459    };   
13460 }();/*
13461  * Based on:
13462  * Ext JS Library 1.1.1
13463  * Copyright(c) 2006-2007, Ext JS, LLC.
13464  *
13465  * Originally Released Under LGPL - original licence link has changed is not relivant.
13466  *
13467  * Fork - LGPL
13468  * <script type="text/javascript">
13469  */
13470
13471  
13472
13473 /**
13474  * @class Roo.util.ClickRepeater
13475  * @extends Roo.util.Observable
13476  * 
13477  * A wrapper class which can be applied to any element. Fires a "click" event while the
13478  * mouse is pressed. The interval between firings may be specified in the config but
13479  * defaults to 10 milliseconds.
13480  * 
13481  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13482  * 
13483  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13484  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13485  * Similar to an autorepeat key delay.
13486  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13487  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13488  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13489  *           "interval" and "delay" are ignored. "immediate" is honored.
13490  * @cfg {Boolean} preventDefault True to prevent the default click event
13491  * @cfg {Boolean} stopDefault True to stop the default click event
13492  * 
13493  * @history
13494  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13495  *     2007-02-02 jvs Renamed to ClickRepeater
13496  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13497  *
13498  *  @constructor
13499  * @param {String/HTMLElement/Element} el The element to listen on
13500  * @param {Object} config
13501  **/
13502 Roo.util.ClickRepeater = function(el, config)
13503 {
13504     this.el = Roo.get(el);
13505     this.el.unselectable();
13506
13507     Roo.apply(this, config);
13508
13509     this.addEvents({
13510     /**
13511      * @event mousedown
13512      * Fires when the mouse button is depressed.
13513      * @param {Roo.util.ClickRepeater} this
13514      */
13515         "mousedown" : true,
13516     /**
13517      * @event click
13518      * Fires on a specified interval during the time the element is pressed.
13519      * @param {Roo.util.ClickRepeater} this
13520      */
13521         "click" : true,
13522     /**
13523      * @event mouseup
13524      * Fires when the mouse key is released.
13525      * @param {Roo.util.ClickRepeater} this
13526      */
13527         "mouseup" : true
13528     });
13529
13530     this.el.on("mousedown", this.handleMouseDown, this);
13531     if(this.preventDefault || this.stopDefault){
13532         this.el.on("click", function(e){
13533             if(this.preventDefault){
13534                 e.preventDefault();
13535             }
13536             if(this.stopDefault){
13537                 e.stopEvent();
13538             }
13539         }, this);
13540     }
13541
13542     // allow inline handler
13543     if(this.handler){
13544         this.on("click", this.handler,  this.scope || this);
13545     }
13546
13547     Roo.util.ClickRepeater.superclass.constructor.call(this);
13548 };
13549
13550 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13551     interval : 20,
13552     delay: 250,
13553     preventDefault : true,
13554     stopDefault : false,
13555     timer : 0,
13556
13557     // private
13558     handleMouseDown : function(){
13559         clearTimeout(this.timer);
13560         this.el.blur();
13561         if(this.pressClass){
13562             this.el.addClass(this.pressClass);
13563         }
13564         this.mousedownTime = new Date();
13565
13566         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13567         this.el.on("mouseout", this.handleMouseOut, this);
13568
13569         this.fireEvent("mousedown", this);
13570         this.fireEvent("click", this);
13571         
13572         this.timer = this.click.defer(this.delay || this.interval, this);
13573     },
13574
13575     // private
13576     click : function(){
13577         this.fireEvent("click", this);
13578         this.timer = this.click.defer(this.getInterval(), this);
13579     },
13580
13581     // private
13582     getInterval: function(){
13583         if(!this.accelerate){
13584             return this.interval;
13585         }
13586         var pressTime = this.mousedownTime.getElapsed();
13587         if(pressTime < 500){
13588             return 400;
13589         }else if(pressTime < 1700){
13590             return 320;
13591         }else if(pressTime < 2600){
13592             return 250;
13593         }else if(pressTime < 3500){
13594             return 180;
13595         }else if(pressTime < 4400){
13596             return 140;
13597         }else if(pressTime < 5300){
13598             return 80;
13599         }else if(pressTime < 6200){
13600             return 50;
13601         }else{
13602             return 10;
13603         }
13604     },
13605
13606     // private
13607     handleMouseOut : function(){
13608         clearTimeout(this.timer);
13609         if(this.pressClass){
13610             this.el.removeClass(this.pressClass);
13611         }
13612         this.el.on("mouseover", this.handleMouseReturn, this);
13613     },
13614
13615     // private
13616     handleMouseReturn : function(){
13617         this.el.un("mouseover", this.handleMouseReturn);
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.click();
13622     },
13623
13624     // private
13625     handleMouseUp : function(){
13626         clearTimeout(this.timer);
13627         this.el.un("mouseover", this.handleMouseReturn);
13628         this.el.un("mouseout", this.handleMouseOut);
13629         Roo.get(document).un("mouseup", this.handleMouseUp);
13630         this.el.removeClass(this.pressClass);
13631         this.fireEvent("mouseup", this);
13632     }
13633 });/*
13634  * Based on:
13635  * Ext JS Library 1.1.1
13636  * Copyright(c) 2006-2007, Ext JS, LLC.
13637  *
13638  * Originally Released Under LGPL - original licence link has changed is not relivant.
13639  *
13640  * Fork - LGPL
13641  * <script type="text/javascript">
13642  */
13643
13644  
13645 /**
13646  * @class Roo.KeyNav
13647  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13648  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13649  * way to implement custom navigation schemes for any UI component.</p>
13650  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13651  * pageUp, pageDown, del, home, end.  Usage:</p>
13652  <pre><code>
13653 var nav = new Roo.KeyNav("my-element", {
13654     "left" : function(e){
13655         this.moveLeft(e.ctrlKey);
13656     },
13657     "right" : function(e){
13658         this.moveRight(e.ctrlKey);
13659     },
13660     "enter" : function(e){
13661         this.save();
13662     },
13663     scope : this
13664 });
13665 </code></pre>
13666  * @constructor
13667  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13668  * @param {Object} config The config
13669  */
13670 Roo.KeyNav = function(el, config){
13671     this.el = Roo.get(el);
13672     Roo.apply(this, config);
13673     if(!this.disabled){
13674         this.disabled = true;
13675         this.enable();
13676     }
13677 };
13678
13679 Roo.KeyNav.prototype = {
13680     /**
13681      * @cfg {Boolean} disabled
13682      * True to disable this KeyNav instance (defaults to false)
13683      */
13684     disabled : false,
13685     /**
13686      * @cfg {String} defaultEventAction
13687      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13688      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13689      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13690      */
13691     defaultEventAction: "stopEvent",
13692     /**
13693      * @cfg {Boolean} forceKeyDown
13694      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13695      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13696      * handle keydown instead of keypress.
13697      */
13698     forceKeyDown : false,
13699
13700     // private
13701     prepareEvent : function(e){
13702         var k = e.getKey();
13703         var h = this.keyToHandler[k];
13704         //if(h && this[h]){
13705         //    e.stopPropagation();
13706         //}
13707         if(Roo.isSafari && h && k >= 37 && k <= 40){
13708             e.stopEvent();
13709         }
13710     },
13711
13712     // private
13713     relay : function(e){
13714         var k = e.getKey();
13715         var h = this.keyToHandler[k];
13716         if(h && this[h]){
13717             if(this.doRelay(e, this[h], h) !== true){
13718                 e[this.defaultEventAction]();
13719             }
13720         }
13721     },
13722
13723     // private
13724     doRelay : function(e, h, hname){
13725         return h.call(this.scope || this, e);
13726     },
13727
13728     // possible handlers
13729     enter : false,
13730     left : false,
13731     right : false,
13732     up : false,
13733     down : false,
13734     tab : false,
13735     esc : false,
13736     pageUp : false,
13737     pageDown : false,
13738     del : false,
13739     home : false,
13740     end : false,
13741
13742     // quick lookup hash
13743     keyToHandler : {
13744         37 : "left",
13745         39 : "right",
13746         38 : "up",
13747         40 : "down",
13748         33 : "pageUp",
13749         34 : "pageDown",
13750         46 : "del",
13751         36 : "home",
13752         35 : "end",
13753         13 : "enter",
13754         27 : "esc",
13755         9  : "tab"
13756     },
13757
13758         /**
13759          * Enable this KeyNav
13760          */
13761         enable: function(){
13762                 if(this.disabled){
13763             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13764             // the EventObject will normalize Safari automatically
13765             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13766                 this.el.on("keydown", this.relay,  this);
13767             }else{
13768                 this.el.on("keydown", this.prepareEvent,  this);
13769                 this.el.on("keypress", this.relay,  this);
13770             }
13771                     this.disabled = false;
13772                 }
13773         },
13774
13775         /**
13776          * Disable this KeyNav
13777          */
13778         disable: function(){
13779                 if(!this.disabled){
13780                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.un("keydown", this.relay);
13782             }else{
13783                 this.el.un("keydown", this.prepareEvent);
13784                 this.el.un("keypress", this.relay);
13785             }
13786                     this.disabled = true;
13787                 }
13788         }
13789 };/*
13790  * Based on:
13791  * Ext JS Library 1.1.1
13792  * Copyright(c) 2006-2007, Ext JS, LLC.
13793  *
13794  * Originally Released Under LGPL - original licence link has changed is not relivant.
13795  *
13796  * Fork - LGPL
13797  * <script type="text/javascript">
13798  */
13799
13800  
13801 /**
13802  * @class Roo.KeyMap
13803  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13804  * The constructor accepts the same config object as defined by {@link #addBinding}.
13805  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13806  * combination it will call the function with this signature (if the match is a multi-key
13807  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13808  * A KeyMap can also handle a string representation of keys.<br />
13809  * Usage:
13810  <pre><code>
13811 // map one key by key code
13812 var map = new Roo.KeyMap("my-element", {
13813     key: 13, // or Roo.EventObject.ENTER
13814     fn: myHandler,
13815     scope: myObject
13816 });
13817
13818 // map multiple keys to one action by string
13819 var map = new Roo.KeyMap("my-element", {
13820     key: "a\r\n\t",
13821     fn: myHandler,
13822     scope: myObject
13823 });
13824
13825 // map multiple keys to multiple actions by strings and array of codes
13826 var map = new Roo.KeyMap("my-element", [
13827     {
13828         key: [10,13],
13829         fn: function(){ alert("Return was pressed"); }
13830     }, {
13831         key: "abc",
13832         fn: function(){ alert('a, b or c was pressed'); }
13833     }, {
13834         key: "\t",
13835         ctrl:true,
13836         shift:true,
13837         fn: function(){ alert('Control + shift + tab was pressed.'); }
13838     }
13839 ]);
13840 </code></pre>
13841  * <b>Note: A KeyMap starts enabled</b>
13842  * @constructor
13843  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13844  * @param {Object} config The config (see {@link #addBinding})
13845  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13846  */
13847 Roo.KeyMap = function(el, config, eventName){
13848     this.el  = Roo.get(el);
13849     this.eventName = eventName || "keydown";
13850     this.bindings = [];
13851     if(config){
13852         this.addBinding(config);
13853     }
13854     this.enable();
13855 };
13856
13857 Roo.KeyMap.prototype = {
13858     /**
13859      * True to stop the event from bubbling and prevent the default browser action if the
13860      * key was handled by the KeyMap (defaults to false)
13861      * @type Boolean
13862      */
13863     stopEvent : false,
13864
13865     /**
13866      * Add a new binding to this KeyMap. The following config object properties are supported:
13867      * <pre>
13868 Property    Type             Description
13869 ----------  ---------------  ----------------------------------------------------------------------
13870 key         String/Array     A single keycode or an array of keycodes to handle
13871 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13872 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13873 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13874 fn          Function         The function to call when KeyMap finds the expected key combination
13875 scope       Object           The scope of the callback function
13876 </pre>
13877      *
13878      * Usage:
13879      * <pre><code>
13880 // Create a KeyMap
13881 var map = new Roo.KeyMap(document, {
13882     key: Roo.EventObject.ENTER,
13883     fn: handleKey,
13884     scope: this
13885 });
13886
13887 //Add a new binding to the existing KeyMap later
13888 map.addBinding({
13889     key: 'abc',
13890     shift: true,
13891     fn: handleKey,
13892     scope: this
13893 });
13894 </code></pre>
13895      * @param {Object/Array} config A single KeyMap config or an array of configs
13896      */
13897         addBinding : function(config){
13898         if(config instanceof Array){
13899             for(var i = 0, len = config.length; i < len; i++){
13900                 this.addBinding(config[i]);
13901             }
13902             return;
13903         }
13904         var keyCode = config.key,
13905             shift = config.shift, 
13906             ctrl = config.ctrl, 
13907             alt = config.alt,
13908             fn = config.fn,
13909             scope = config.scope;
13910         if(typeof keyCode == "string"){
13911             var ks = [];
13912             var keyString = keyCode.toUpperCase();
13913             for(var j = 0, len = keyString.length; j < len; j++){
13914                 ks.push(keyString.charCodeAt(j));
13915             }
13916             keyCode = ks;
13917         }
13918         var keyArray = keyCode instanceof Array;
13919         var handler = function(e){
13920             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13921                 var k = e.getKey();
13922                 if(keyArray){
13923                     for(var i = 0, len = keyCode.length; i < len; i++){
13924                         if(keyCode[i] == k){
13925                           if(this.stopEvent){
13926                               e.stopEvent();
13927                           }
13928                           fn.call(scope || window, k, e);
13929                           return;
13930                         }
13931                     }
13932                 }else{
13933                     if(k == keyCode){
13934                         if(this.stopEvent){
13935                            e.stopEvent();
13936                         }
13937                         fn.call(scope || window, k, e);
13938                     }
13939                 }
13940             }
13941         };
13942         this.bindings.push(handler);  
13943         },
13944
13945     /**
13946      * Shorthand for adding a single key listener
13947      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13948      * following options:
13949      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13950      * @param {Function} fn The function to call
13951      * @param {Object} scope (optional) The scope of the function
13952      */
13953     on : function(key, fn, scope){
13954         var keyCode, shift, ctrl, alt;
13955         if(typeof key == "object" && !(key instanceof Array)){
13956             keyCode = key.key;
13957             shift = key.shift;
13958             ctrl = key.ctrl;
13959             alt = key.alt;
13960         }else{
13961             keyCode = key;
13962         }
13963         this.addBinding({
13964             key: keyCode,
13965             shift: shift,
13966             ctrl: ctrl,
13967             alt: alt,
13968             fn: fn,
13969             scope: scope
13970         })
13971     },
13972
13973     // private
13974     handleKeyDown : function(e){
13975             if(this.enabled){ //just in case
13976             var b = this.bindings;
13977             for(var i = 0, len = b.length; i < len; i++){
13978                 b[i].call(this, e);
13979             }
13980             }
13981         },
13982         
13983         /**
13984          * Returns true if this KeyMap is enabled
13985          * @return {Boolean} 
13986          */
13987         isEnabled : function(){
13988             return this.enabled;  
13989         },
13990         
13991         /**
13992          * Enables this KeyMap
13993          */
13994         enable: function(){
13995                 if(!this.enabled){
13996                     this.el.on(this.eventName, this.handleKeyDown, this);
13997                     this.enabled = true;
13998                 }
13999         },
14000
14001         /**
14002          * Disable this KeyMap
14003          */
14004         disable: function(){
14005                 if(this.enabled){
14006                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14007                     this.enabled = false;
14008                 }
14009         }
14010 };/*
14011  * Based on:
14012  * Ext JS Library 1.1.1
14013  * Copyright(c) 2006-2007, Ext JS, LLC.
14014  *
14015  * Originally Released Under LGPL - original licence link has changed is not relivant.
14016  *
14017  * Fork - LGPL
14018  * <script type="text/javascript">
14019  */
14020
14021  
14022 /**
14023  * @class Roo.util.TextMetrics
14024  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14025  * wide, in pixels, a given block of text will be.
14026  * @singleton
14027  */
14028 Roo.util.TextMetrics = function(){
14029     var shared;
14030     return {
14031         /**
14032          * Measures the size of the specified text
14033          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14034          * that can affect the size of the rendered text
14035          * @param {String} text The text to measure
14036          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14037          * in order to accurately measure the text height
14038          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14039          */
14040         measure : function(el, text, fixedWidth){
14041             if(!shared){
14042                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14043             }
14044             shared.bind(el);
14045             shared.setFixedWidth(fixedWidth || 'auto');
14046             return shared.getSize(text);
14047         },
14048
14049         /**
14050          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14051          * the overhead of multiple calls to initialize the style properties on each measurement.
14052          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14053          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14054          * in order to accurately measure the text height
14055          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14056          */
14057         createInstance : function(el, fixedWidth){
14058             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14059         }
14060     };
14061 }();
14062
14063  
14064
14065 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14066     var ml = new Roo.Element(document.createElement('div'));
14067     document.body.appendChild(ml.dom);
14068     ml.position('absolute');
14069     ml.setLeftTop(-1000, -1000);
14070     ml.hide();
14071
14072     if(fixedWidth){
14073         ml.setWidth(fixedWidth);
14074     }
14075      
14076     var instance = {
14077         /**
14078          * Returns the size of the specified text based on the internal element's style and width properties
14079          * @memberOf Roo.util.TextMetrics.Instance#
14080          * @param {String} text The text to measure
14081          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14082          */
14083         getSize : function(text){
14084             ml.update(text);
14085             var s = ml.getSize();
14086             ml.update('');
14087             return s;
14088         },
14089
14090         /**
14091          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14092          * that can affect the size of the rendered text
14093          * @memberOf Roo.util.TextMetrics.Instance#
14094          * @param {String/HTMLElement} el The element, dom node or id
14095          */
14096         bind : function(el){
14097             ml.setStyle(
14098                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14099             );
14100         },
14101
14102         /**
14103          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14104          * to set a fixed width in order to accurately measure the text height.
14105          * @memberOf Roo.util.TextMetrics.Instance#
14106          * @param {Number} width The width to set on the element
14107          */
14108         setFixedWidth : function(width){
14109             ml.setWidth(width);
14110         },
14111
14112         /**
14113          * Returns the measured width of the specified text
14114          * @memberOf Roo.util.TextMetrics.Instance#
14115          * @param {String} text The text to measure
14116          * @return {Number} width The width in pixels
14117          */
14118         getWidth : function(text){
14119             ml.dom.style.width = 'auto';
14120             return this.getSize(text).width;
14121         },
14122
14123         /**
14124          * Returns the measured height of the specified text.  For multiline text, be sure to call
14125          * {@link #setFixedWidth} if necessary.
14126          * @memberOf Roo.util.TextMetrics.Instance#
14127          * @param {String} text The text to measure
14128          * @return {Number} height The height in pixels
14129          */
14130         getHeight : function(text){
14131             return this.getSize(text).height;
14132         }
14133     };
14134
14135     instance.bind(bindTo);
14136
14137     return instance;
14138 };
14139
14140 // backwards compat
14141 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14142  * Based on:
14143  * Ext JS Library 1.1.1
14144  * Copyright(c) 2006-2007, Ext JS, LLC.
14145  *
14146  * Originally Released Under LGPL - original licence link has changed is not relivant.
14147  *
14148  * Fork - LGPL
14149  * <script type="text/javascript">
14150  */
14151
14152 /**
14153  * @class Roo.state.Provider
14154  * Abstract base class for state provider implementations. This class provides methods
14155  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14156  * Provider interface.
14157  */
14158 Roo.state.Provider = function(){
14159     /**
14160      * @event statechange
14161      * Fires when a state change occurs.
14162      * @param {Provider} this This state provider
14163      * @param {String} key The state key which was changed
14164      * @param {String} value The encoded value for the state
14165      */
14166     this.addEvents({
14167         "statechange": true
14168     });
14169     this.state = {};
14170     Roo.state.Provider.superclass.constructor.call(this);
14171 };
14172 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14173     /**
14174      * Returns the current value for a key
14175      * @param {String} name The key name
14176      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14177      * @return {Mixed} The state data
14178      */
14179     get : function(name, defaultValue){
14180         return typeof this.state[name] == "undefined" ?
14181             defaultValue : this.state[name];
14182     },
14183     
14184     /**
14185      * Clears a value from the state
14186      * @param {String} name The key name
14187      */
14188     clear : function(name){
14189         delete this.state[name];
14190         this.fireEvent("statechange", this, name, null);
14191     },
14192     
14193     /**
14194      * Sets the value for a key
14195      * @param {String} name The key name
14196      * @param {Mixed} value The value to set
14197      */
14198     set : function(name, value){
14199         this.state[name] = value;
14200         this.fireEvent("statechange", this, name, value);
14201     },
14202     
14203     /**
14204      * Decodes a string previously encoded with {@link #encodeValue}.
14205      * @param {String} value The value to decode
14206      * @return {Mixed} The decoded value
14207      */
14208     decodeValue : function(cookie){
14209         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14210         var matches = re.exec(unescape(cookie));
14211         if(!matches || !matches[1]) return; // non state cookie
14212         var type = matches[1];
14213         var v = matches[2];
14214         switch(type){
14215             case "n":
14216                 return parseFloat(v);
14217             case "d":
14218                 return new Date(Date.parse(v));
14219             case "b":
14220                 return (v == "1");
14221             case "a":
14222                 var all = [];
14223                 var values = v.split("^");
14224                 for(var i = 0, len = values.length; i < len; i++){
14225                     all.push(this.decodeValue(values[i]));
14226                 }
14227                 return all;
14228            case "o":
14229                 var all = {};
14230                 var values = v.split("^");
14231                 for(var i = 0, len = values.length; i < len; i++){
14232                     var kv = values[i].split("=");
14233                     all[kv[0]] = this.decodeValue(kv[1]);
14234                 }
14235                 return all;
14236            default:
14237                 return v;
14238         }
14239     },
14240     
14241     /**
14242      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14243      * @param {Mixed} value The value to encode
14244      * @return {String} The encoded value
14245      */
14246     encodeValue : function(v){
14247         var enc;
14248         if(typeof v == "number"){
14249             enc = "n:" + v;
14250         }else if(typeof v == "boolean"){
14251             enc = "b:" + (v ? "1" : "0");
14252         }else if(v instanceof Date){
14253             enc = "d:" + v.toGMTString();
14254         }else if(v instanceof Array){
14255             var flat = "";
14256             for(var i = 0, len = v.length; i < len; i++){
14257                 flat += this.encodeValue(v[i]);
14258                 if(i != len-1) flat += "^";
14259             }
14260             enc = "a:" + flat;
14261         }else if(typeof v == "object"){
14262             var flat = "";
14263             for(var key in v){
14264                 if(typeof v[key] != "function"){
14265                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14266                 }
14267             }
14268             enc = "o:" + flat.substring(0, flat.length-1);
14269         }else{
14270             enc = "s:" + v;
14271         }
14272         return escape(enc);        
14273     }
14274 });
14275
14276 /*
14277  * Based on:
14278  * Ext JS Library 1.1.1
14279  * Copyright(c) 2006-2007, Ext JS, LLC.
14280  *
14281  * Originally Released Under LGPL - original licence link has changed is not relivant.
14282  *
14283  * Fork - LGPL
14284  * <script type="text/javascript">
14285  */
14286 /**
14287  * @class Roo.state.Manager
14288  * This is the global state manager. By default all components that are "state aware" check this class
14289  * for state information if you don't pass them a custom state provider. In order for this class
14290  * to be useful, it must be initialized with a provider when your application initializes.
14291  <pre><code>
14292 // in your initialization function
14293 init : function(){
14294    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14295    ...
14296    // supposed you have a {@link Roo.BorderLayout}
14297    var layout = new Roo.BorderLayout(...);
14298    layout.restoreState();
14299    // or a {Roo.BasicDialog}
14300    var dialog = new Roo.BasicDialog(...);
14301    dialog.restoreState();
14302  </code></pre>
14303  * @singleton
14304  */
14305 Roo.state.Manager = function(){
14306     var provider = new Roo.state.Provider();
14307     
14308     return {
14309         /**
14310          * Configures the default state provider for your application
14311          * @param {Provider} stateProvider The state provider to set
14312          */
14313         setProvider : function(stateProvider){
14314             provider = stateProvider;
14315         },
14316         
14317         /**
14318          * Returns the current value for a key
14319          * @param {String} name The key name
14320          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14321          * @return {Mixed} The state data
14322          */
14323         get : function(key, defaultValue){
14324             return provider.get(key, defaultValue);
14325         },
14326         
14327         /**
14328          * Sets the value for a key
14329          * @param {String} name The key name
14330          * @param {Mixed} value The state data
14331          */
14332          set : function(key, value){
14333             provider.set(key, value);
14334         },
14335         
14336         /**
14337          * Clears a value from the state
14338          * @param {String} name The key name
14339          */
14340         clear : function(key){
14341             provider.clear(key);
14342         },
14343         
14344         /**
14345          * Gets the currently configured state provider
14346          * @return {Provider} The state provider
14347          */
14348         getProvider : function(){
14349             return provider;
14350         }
14351     };
14352 }();
14353 /*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363 /**
14364  * @class Roo.state.CookieProvider
14365  * @extends Roo.state.Provider
14366  * The default Provider implementation which saves state via cookies.
14367  * <br />Usage:
14368  <pre><code>
14369    var cp = new Roo.state.CookieProvider({
14370        path: "/cgi-bin/",
14371        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14372        domain: "roojs.com"
14373    })
14374    Roo.state.Manager.setProvider(cp);
14375  </code></pre>
14376  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14377  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14378  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14379  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14380  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14381  * domain the page is running on including the 'www' like 'www.roojs.com')
14382  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14383  * @constructor
14384  * Create a new CookieProvider
14385  * @param {Object} config The configuration object
14386  */
14387 Roo.state.CookieProvider = function(config){
14388     Roo.state.CookieProvider.superclass.constructor.call(this);
14389     this.path = "/";
14390     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14391     this.domain = null;
14392     this.secure = false;
14393     Roo.apply(this, config);
14394     this.state = this.readCookies();
14395 };
14396
14397 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14398     // private
14399     set : function(name, value){
14400         if(typeof value == "undefined" || value === null){
14401             this.clear(name);
14402             return;
14403         }
14404         this.setCookie(name, value);
14405         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14406     },
14407
14408     // private
14409     clear : function(name){
14410         this.clearCookie(name);
14411         Roo.state.CookieProvider.superclass.clear.call(this, name);
14412     },
14413
14414     // private
14415     readCookies : function(){
14416         var cookies = {};
14417         var c = document.cookie + ";";
14418         var re = /\s?(.*?)=(.*?);/g;
14419         var matches;
14420         while((matches = re.exec(c)) != null){
14421             var name = matches[1];
14422             var value = matches[2];
14423             if(name && name.substring(0,3) == "ys-"){
14424                 cookies[name.substr(3)] = this.decodeValue(value);
14425             }
14426         }
14427         return cookies;
14428     },
14429
14430     // private
14431     setCookie : function(name, value){
14432         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14433            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14434            ((this.path == null) ? "" : ("; path=" + this.path)) +
14435            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14436            ((this.secure == true) ? "; secure" : "");
14437     },
14438
14439     // private
14440     clearCookie : function(name){
14441         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14442            ((this.path == null) ? "" : ("; path=" + this.path)) +
14443            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14444            ((this.secure == true) ? "; secure" : "");
14445     }
14446 });/*
14447  * Based on:
14448  * Ext JS Library 1.1.1
14449  * Copyright(c) 2006-2007, Ext JS, LLC.
14450  *
14451  * Originally Released Under LGPL - original licence link has changed is not relivant.
14452  *
14453  * Fork - LGPL
14454  * <script type="text/javascript">
14455  */
14456
14457
14458
14459 /*
14460  * These classes are derivatives of the similarly named classes in the YUI Library.
14461  * The original license:
14462  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14463  * Code licensed under the BSD License:
14464  * http://developer.yahoo.net/yui/license.txt
14465  */
14466
14467 (function() {
14468
14469 var Event=Roo.EventManager;
14470 var Dom=Roo.lib.Dom;
14471
14472 /**
14473  * @class Roo.dd.DragDrop
14474  * @extends Roo.util.Observable
14475  * Defines the interface and base operation of items that that can be
14476  * dragged or can be drop targets.  It was designed to be extended, overriding
14477  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14478  * Up to three html elements can be associated with a DragDrop instance:
14479  * <ul>
14480  * <li>linked element: the element that is passed into the constructor.
14481  * This is the element which defines the boundaries for interaction with
14482  * other DragDrop objects.</li>
14483  * <li>handle element(s): The drag operation only occurs if the element that
14484  * was clicked matches a handle element.  By default this is the linked
14485  * element, but there are times that you will want only a portion of the
14486  * linked element to initiate the drag operation, and the setHandleElId()
14487  * method provides a way to define this.</li>
14488  * <li>drag element: this represents the element that would be moved along
14489  * with the cursor during a drag operation.  By default, this is the linked
14490  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14491  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14492  * </li>
14493  * </ul>
14494  * This class should not be instantiated until the onload event to ensure that
14495  * the associated elements are available.
14496  * The following would define a DragDrop obj that would interact with any
14497  * other DragDrop obj in the "group1" group:
14498  * <pre>
14499  *  dd = new Roo.dd.DragDrop("div1", "group1");
14500  * </pre>
14501  * Since none of the event handlers have been implemented, nothing would
14502  * actually happen if you were to run the code above.  Normally you would
14503  * override this class or one of the default implementations, but you can
14504  * also override the methods you want on an instance of the class...
14505  * <pre>
14506  *  dd.onDragDrop = function(e, id) {
14507  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14508  *  }
14509  * </pre>
14510  * @constructor
14511  * @param {String} id of the element that is linked to this instance
14512  * @param {String} sGroup the group of related DragDrop objects
14513  * @param {object} config an object containing configurable attributes
14514  *                Valid properties for DragDrop:
14515  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14516  */
14517 Roo.dd.DragDrop = function(id, sGroup, config) {
14518     if (id) {
14519         this.init(id, sGroup, config);
14520     }
14521     
14522 };
14523
14524 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14525
14526     /**
14527      * The id of the element associated with this object.  This is what we
14528      * refer to as the "linked element" because the size and position of
14529      * this element is used to determine when the drag and drop objects have
14530      * interacted.
14531      * @property id
14532      * @type String
14533      */
14534     id: null,
14535
14536     /**
14537      * Configuration attributes passed into the constructor
14538      * @property config
14539      * @type object
14540      */
14541     config: null,
14542
14543     /**
14544      * The id of the element that will be dragged.  By default this is same
14545      * as the linked element , but could be changed to another element. Ex:
14546      * Roo.dd.DDProxy
14547      * @property dragElId
14548      * @type String
14549      * @private
14550      */
14551     dragElId: null,
14552
14553     /**
14554      * the id of the element that initiates the drag operation.  By default
14555      * this is the linked element, but could be changed to be a child of this
14556      * element.  This lets us do things like only starting the drag when the
14557      * header element within the linked html element is clicked.
14558      * @property handleElId
14559      * @type String
14560      * @private
14561      */
14562     handleElId: null,
14563
14564     /**
14565      * An associative array of HTML tags that will be ignored if clicked.
14566      * @property invalidHandleTypes
14567      * @type {string: string}
14568      */
14569     invalidHandleTypes: null,
14570
14571     /**
14572      * An associative array of ids for elements that will be ignored if clicked
14573      * @property invalidHandleIds
14574      * @type {string: string}
14575      */
14576     invalidHandleIds: null,
14577
14578     /**
14579      * An indexted array of css class names for elements that will be ignored
14580      * if clicked.
14581      * @property invalidHandleClasses
14582      * @type string[]
14583      */
14584     invalidHandleClasses: null,
14585
14586     /**
14587      * The linked element's absolute X position at the time the drag was
14588      * started
14589      * @property startPageX
14590      * @type int
14591      * @private
14592      */
14593     startPageX: 0,
14594
14595     /**
14596      * The linked element's absolute X position at the time the drag was
14597      * started
14598      * @property startPageY
14599      * @type int
14600      * @private
14601      */
14602     startPageY: 0,
14603
14604     /**
14605      * The group defines a logical collection of DragDrop objects that are
14606      * related.  Instances only get events when interacting with other
14607      * DragDrop object in the same group.  This lets us define multiple
14608      * groups using a single DragDrop subclass if we want.
14609      * @property groups
14610      * @type {string: string}
14611      */
14612     groups: null,
14613
14614     /**
14615      * Individual drag/drop instances can be locked.  This will prevent
14616      * onmousedown start drag.
14617      * @property locked
14618      * @type boolean
14619      * @private
14620      */
14621     locked: false,
14622
14623     /**
14624      * Lock this instance
14625      * @method lock
14626      */
14627     lock: function() { this.locked = true; },
14628
14629     /**
14630      * Unlock this instace
14631      * @method unlock
14632      */
14633     unlock: function() { this.locked = false; },
14634
14635     /**
14636      * By default, all insances can be a drop target.  This can be disabled by
14637      * setting isTarget to false.
14638      * @method isTarget
14639      * @type boolean
14640      */
14641     isTarget: true,
14642
14643     /**
14644      * The padding configured for this drag and drop object for calculating
14645      * the drop zone intersection with this object.
14646      * @method padding
14647      * @type int[]
14648      */
14649     padding: null,
14650
14651     /**
14652      * Cached reference to the linked element
14653      * @property _domRef
14654      * @private
14655      */
14656     _domRef: null,
14657
14658     /**
14659      * Internal typeof flag
14660      * @property __ygDragDrop
14661      * @private
14662      */
14663     __ygDragDrop: true,
14664
14665     /**
14666      * Set to true when horizontal contraints are applied
14667      * @property constrainX
14668      * @type boolean
14669      * @private
14670      */
14671     constrainX: false,
14672
14673     /**
14674      * Set to true when vertical contraints are applied
14675      * @property constrainY
14676      * @type boolean
14677      * @private
14678      */
14679     constrainY: false,
14680
14681     /**
14682      * The left constraint
14683      * @property minX
14684      * @type int
14685      * @private
14686      */
14687     minX: 0,
14688
14689     /**
14690      * The right constraint
14691      * @property maxX
14692      * @type int
14693      * @private
14694      */
14695     maxX: 0,
14696
14697     /**
14698      * The up constraint
14699      * @property minY
14700      * @type int
14701      * @type int
14702      * @private
14703      */
14704     minY: 0,
14705
14706     /**
14707      * The down constraint
14708      * @property maxY
14709      * @type int
14710      * @private
14711      */
14712     maxY: 0,
14713
14714     /**
14715      * Maintain offsets when we resetconstraints.  Set to true when you want
14716      * the position of the element relative to its parent to stay the same
14717      * when the page changes
14718      *
14719      * @property maintainOffset
14720      * @type boolean
14721      */
14722     maintainOffset: false,
14723
14724     /**
14725      * Array of pixel locations the element will snap to if we specified a
14726      * horizontal graduation/interval.  This array is generated automatically
14727      * when you define a tick interval.
14728      * @property xTicks
14729      * @type int[]
14730      */
14731     xTicks: null,
14732
14733     /**
14734      * Array of pixel locations the element will snap to if we specified a
14735      * vertical graduation/interval.  This array is generated automatically
14736      * when you define a tick interval.
14737      * @property yTicks
14738      * @type int[]
14739      */
14740     yTicks: null,
14741
14742     /**
14743      * By default the drag and drop instance will only respond to the primary
14744      * button click (left button for a right-handed mouse).  Set to true to
14745      * allow drag and drop to start with any mouse click that is propogated
14746      * by the browser
14747      * @property primaryButtonOnly
14748      * @type boolean
14749      */
14750     primaryButtonOnly: true,
14751
14752     /**
14753      * The availabe property is false until the linked dom element is accessible.
14754      * @property available
14755      * @type boolean
14756      */
14757     available: false,
14758
14759     /**
14760      * By default, drags can only be initiated if the mousedown occurs in the
14761      * region the linked element is.  This is done in part to work around a
14762      * bug in some browsers that mis-report the mousedown if the previous
14763      * mouseup happened outside of the window.  This property is set to true
14764      * if outer handles are defined.
14765      *
14766      * @property hasOuterHandles
14767      * @type boolean
14768      * @default false
14769      */
14770     hasOuterHandles: false,
14771
14772     /**
14773      * Code that executes immediately before the startDrag event
14774      * @method b4StartDrag
14775      * @private
14776      */
14777     b4StartDrag: function(x, y) { },
14778
14779     /**
14780      * Abstract method called after a drag/drop object is clicked
14781      * and the drag or mousedown time thresholds have beeen met.
14782      * @method startDrag
14783      * @param {int} X click location
14784      * @param {int} Y click location
14785      */
14786     startDrag: function(x, y) { /* override this */ },
14787
14788     /**
14789      * Code that executes immediately before the onDrag event
14790      * @method b4Drag
14791      * @private
14792      */
14793     b4Drag: function(e) { },
14794
14795     /**
14796      * Abstract method called during the onMouseMove event while dragging an
14797      * object.
14798      * @method onDrag
14799      * @param {Event} e the mousemove event
14800      */
14801     onDrag: function(e) { /* override this */ },
14802
14803     /**
14804      * Abstract method called when this element fist begins hovering over
14805      * another DragDrop obj
14806      * @method onDragEnter
14807      * @param {Event} e the mousemove event
14808      * @param {String|DragDrop[]} id In POINT mode, the element
14809      * id this is hovering over.  In INTERSECT mode, an array of one or more
14810      * dragdrop items being hovered over.
14811      */
14812     onDragEnter: function(e, id) { /* override this */ },
14813
14814     /**
14815      * Code that executes immediately before the onDragOver event
14816      * @method b4DragOver
14817      * @private
14818      */
14819     b4DragOver: function(e) { },
14820
14821     /**
14822      * Abstract method called when this element is hovering over another
14823      * DragDrop obj
14824      * @method onDragOver
14825      * @param {Event} e the mousemove event
14826      * @param {String|DragDrop[]} id In POINT mode, the element
14827      * id this is hovering over.  In INTERSECT mode, an array of dd items
14828      * being hovered over.
14829      */
14830     onDragOver: function(e, id) { /* override this */ },
14831
14832     /**
14833      * Code that executes immediately before the onDragOut event
14834      * @method b4DragOut
14835      * @private
14836      */
14837     b4DragOut: function(e) { },
14838
14839     /**
14840      * Abstract method called when we are no longer hovering over an element
14841      * @method onDragOut
14842      * @param {Event} e the mousemove event
14843      * @param {String|DragDrop[]} id In POINT mode, the element
14844      * id this was hovering over.  In INTERSECT mode, an array of dd items
14845      * that the mouse is no longer over.
14846      */
14847     onDragOut: function(e, id) { /* override this */ },
14848
14849     /**
14850      * Code that executes immediately before the onDragDrop event
14851      * @method b4DragDrop
14852      * @private
14853      */
14854     b4DragDrop: function(e) { },
14855
14856     /**
14857      * Abstract method called when this item is dropped on another DragDrop
14858      * obj
14859      * @method onDragDrop
14860      * @param {Event} e the mouseup event
14861      * @param {String|DragDrop[]} id In POINT mode, the element
14862      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14863      * was dropped on.
14864      */
14865     onDragDrop: function(e, id) { /* override this */ },
14866
14867     /**
14868      * Abstract method called when this item is dropped on an area with no
14869      * drop target
14870      * @method onInvalidDrop
14871      * @param {Event} e the mouseup event
14872      */
14873     onInvalidDrop: function(e) { /* override this */ },
14874
14875     /**
14876      * Code that executes immediately before the endDrag event
14877      * @method b4EndDrag
14878      * @private
14879      */
14880     b4EndDrag: function(e) { },
14881
14882     /**
14883      * Fired when we are done dragging the object
14884      * @method endDrag
14885      * @param {Event} e the mouseup event
14886      */
14887     endDrag: function(e) { /* override this */ },
14888
14889     /**
14890      * Code executed immediately before the onMouseDown event
14891      * @method b4MouseDown
14892      * @param {Event} e the mousedown event
14893      * @private
14894      */
14895     b4MouseDown: function(e) {  },
14896
14897     /**
14898      * Event handler that fires when a drag/drop obj gets a mousedown
14899      * @method onMouseDown
14900      * @param {Event} e the mousedown event
14901      */
14902     onMouseDown: function(e) { /* override this */ },
14903
14904     /**
14905      * Event handler that fires when a drag/drop obj gets a mouseup
14906      * @method onMouseUp
14907      * @param {Event} e the mouseup event
14908      */
14909     onMouseUp: function(e) { /* override this */ },
14910
14911     /**
14912      * Override the onAvailable method to do what is needed after the initial
14913      * position was determined.
14914      * @method onAvailable
14915      */
14916     onAvailable: function () {
14917     },
14918
14919     /*
14920      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14921      * @type Object
14922      */
14923     defaultPadding : {left:0, right:0, top:0, bottom:0},
14924
14925     /*
14926      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14927  *
14928  * Usage:
14929  <pre><code>
14930  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14931                 { dragElId: "existingProxyDiv" });
14932  dd.startDrag = function(){
14933      this.constrainTo("parent-id");
14934  };
14935  </code></pre>
14936  * Or you can initalize it using the {@link Roo.Element} object:
14937  <pre><code>
14938  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14939      startDrag : function(){
14940          this.constrainTo("parent-id");
14941      }
14942  });
14943  </code></pre>
14944      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14945      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14946      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14947      * an object containing the sides to pad. For example: {right:10, bottom:10}
14948      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14949      */
14950     constrainTo : function(constrainTo, pad, inContent){
14951         if(typeof pad == "number"){
14952             pad = {left: pad, right:pad, top:pad, bottom:pad};
14953         }
14954         pad = pad || this.defaultPadding;
14955         var b = Roo.get(this.getEl()).getBox();
14956         var ce = Roo.get(constrainTo);
14957         var s = ce.getScroll();
14958         var c, cd = ce.dom;
14959         if(cd == document.body){
14960             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14961         }else{
14962             xy = ce.getXY();
14963             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14964         }
14965
14966
14967         var topSpace = b.y - c.y;
14968         var leftSpace = b.x - c.x;
14969
14970         this.resetConstraints();
14971         this.setXConstraint(leftSpace - (pad.left||0), // left
14972                 c.width - leftSpace - b.width - (pad.right||0) //right
14973         );
14974         this.setYConstraint(topSpace - (pad.top||0), //top
14975                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14976         );
14977     },
14978
14979     /**
14980      * Returns a reference to the linked element
14981      * @method getEl
14982      * @return {HTMLElement} the html element
14983      */
14984     getEl: function() {
14985         if (!this._domRef) {
14986             this._domRef = Roo.getDom(this.id);
14987         }
14988
14989         return this._domRef;
14990     },
14991
14992     /**
14993      * Returns a reference to the actual element to drag.  By default this is
14994      * the same as the html element, but it can be assigned to another
14995      * element. An example of this can be found in Roo.dd.DDProxy
14996      * @method getDragEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getDragEl: function() {
15000         return Roo.getDom(this.dragElId);
15001     },
15002
15003     /**
15004      * Sets up the DragDrop object.  Must be called in the constructor of any
15005      * Roo.dd.DragDrop subclass
15006      * @method init
15007      * @param id the id of the linked element
15008      * @param {String} sGroup the group of related items
15009      * @param {object} config configuration attributes
15010      */
15011     init: function(id, sGroup, config) {
15012         this.initTarget(id, sGroup, config);
15013         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15014         // Event.on(this.id, "selectstart", Event.preventDefault);
15015     },
15016
15017     /**
15018      * Initializes Targeting functionality only... the object does not
15019      * get a mousedown handler.
15020      * @method initTarget
15021      * @param id the id of the linked element
15022      * @param {String} sGroup the group of related items
15023      * @param {object} config configuration attributes
15024      */
15025     initTarget: function(id, sGroup, config) {
15026
15027         // configuration attributes
15028         this.config = config || {};
15029
15030         // create a local reference to the drag and drop manager
15031         this.DDM = Roo.dd.DDM;
15032         // initialize the groups array
15033         this.groups = {};
15034
15035         // assume that we have an element reference instead of an id if the
15036         // parameter is not a string
15037         if (typeof id !== "string") {
15038             id = Roo.id(id);
15039         }
15040
15041         // set the id
15042         this.id = id;
15043
15044         // add to an interaction group
15045         this.addToGroup((sGroup) ? sGroup : "default");
15046
15047         // We don't want to register this as the handle with the manager
15048         // so we just set the id rather than calling the setter.
15049         this.handleElId = id;
15050
15051         // the linked element is the element that gets dragged by default
15052         this.setDragElId(id);
15053
15054         // by default, clicked anchors will not start drag operations.
15055         this.invalidHandleTypes = { A: "A" };
15056         this.invalidHandleIds = {};
15057         this.invalidHandleClasses = [];
15058
15059         this.applyConfig();
15060
15061         this.handleOnAvailable();
15062     },
15063
15064     /**
15065      * Applies the configuration parameters that were passed into the constructor.
15066      * This is supposed to happen at each level through the inheritance chain.  So
15067      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15068      * DragDrop in order to get all of the parameters that are available in
15069      * each object.
15070      * @method applyConfig
15071      */
15072     applyConfig: function() {
15073
15074         // configurable properties:
15075         //    padding, isTarget, maintainOffset, primaryButtonOnly
15076         this.padding           = this.config.padding || [0, 0, 0, 0];
15077         this.isTarget          = (this.config.isTarget !== false);
15078         this.maintainOffset    = (this.config.maintainOffset);
15079         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15080
15081     },
15082
15083     /**
15084      * Executed when the linked element is available
15085      * @method handleOnAvailable
15086      * @private
15087      */
15088     handleOnAvailable: function() {
15089         this.available = true;
15090         this.resetConstraints();
15091         this.onAvailable();
15092     },
15093
15094      /**
15095      * Configures the padding for the target zone in px.  Effectively expands
15096      * (or reduces) the virtual object size for targeting calculations.
15097      * Supports css-style shorthand; if only one parameter is passed, all sides
15098      * will have that padding, and if only two are passed, the top and bottom
15099      * will have the first param, the left and right the second.
15100      * @method setPadding
15101      * @param {int} iTop    Top pad
15102      * @param {int} iRight  Right pad
15103      * @param {int} iBot    Bot pad
15104      * @param {int} iLeft   Left pad
15105      */
15106     setPadding: function(iTop, iRight, iBot, iLeft) {
15107         // this.padding = [iLeft, iRight, iTop, iBot];
15108         if (!iRight && 0 !== iRight) {
15109             this.padding = [iTop, iTop, iTop, iTop];
15110         } else if (!iBot && 0 !== iBot) {
15111             this.padding = [iTop, iRight, iTop, iRight];
15112         } else {
15113             this.padding = [iTop, iRight, iBot, iLeft];
15114         }
15115     },
15116
15117     /**
15118      * Stores the initial placement of the linked element.
15119      * @method setInitialPosition
15120      * @param {int} diffX   the X offset, default 0
15121      * @param {int} diffY   the Y offset, default 0
15122      */
15123     setInitPosition: function(diffX, diffY) {
15124         var el = this.getEl();
15125
15126         if (!this.DDM.verifyEl(el)) {
15127             return;
15128         }
15129
15130         var dx = diffX || 0;
15131         var dy = diffY || 0;
15132
15133         var p = Dom.getXY( el );
15134
15135         this.initPageX = p[0] - dx;
15136         this.initPageY = p[1] - dy;
15137
15138         this.lastPageX = p[0];
15139         this.lastPageY = p[1];
15140
15141
15142         this.setStartPosition(p);
15143     },
15144
15145     /**
15146      * Sets the start position of the element.  This is set when the obj
15147      * is initialized, the reset when a drag is started.
15148      * @method setStartPosition
15149      * @param pos current position (from previous lookup)
15150      * @private
15151      */
15152     setStartPosition: function(pos) {
15153         var p = pos || Dom.getXY( this.getEl() );
15154         this.deltaSetXY = null;
15155
15156         this.startPageX = p[0];
15157         this.startPageY = p[1];
15158     },
15159
15160     /**
15161      * Add this instance to a group of related drag/drop objects.  All
15162      * instances belong to at least one group, and can belong to as many
15163      * groups as needed.
15164      * @method addToGroup
15165      * @param sGroup {string} the name of the group
15166      */
15167     addToGroup: function(sGroup) {
15168         this.groups[sGroup] = true;
15169         this.DDM.regDragDrop(this, sGroup);
15170     },
15171
15172     /**
15173      * Remove's this instance from the supplied interaction group
15174      * @method removeFromGroup
15175      * @param {string}  sGroup  The group to drop
15176      */
15177     removeFromGroup: function(sGroup) {
15178         if (this.groups[sGroup]) {
15179             delete this.groups[sGroup];
15180         }
15181
15182         this.DDM.removeDDFromGroup(this, sGroup);
15183     },
15184
15185     /**
15186      * Allows you to specify that an element other than the linked element
15187      * will be moved with the cursor during a drag
15188      * @method setDragElId
15189      * @param id {string} the id of the element that will be used to initiate the drag
15190      */
15191     setDragElId: function(id) {
15192         this.dragElId = id;
15193     },
15194
15195     /**
15196      * Allows you to specify a child of the linked element that should be
15197      * used to initiate the drag operation.  An example of this would be if
15198      * you have a content div with text and links.  Clicking anywhere in the
15199      * content area would normally start the drag operation.  Use this method
15200      * to specify that an element inside of the content div is the element
15201      * that starts the drag operation.
15202      * @method setHandleElId
15203      * @param id {string} the id of the element that will be used to
15204      * initiate the drag.
15205      */
15206     setHandleElId: function(id) {
15207         if (typeof id !== "string") {
15208             id = Roo.id(id);
15209         }
15210         this.handleElId = id;
15211         this.DDM.regHandle(this.id, id);
15212     },
15213
15214     /**
15215      * Allows you to set an element outside of the linked element as a drag
15216      * handle
15217      * @method setOuterHandleElId
15218      * @param id the id of the element that will be used to initiate the drag
15219      */
15220     setOuterHandleElId: function(id) {
15221         if (typeof id !== "string") {
15222             id = Roo.id(id);
15223         }
15224         Event.on(id, "mousedown",
15225                 this.handleMouseDown, this);
15226         this.setHandleElId(id);
15227
15228         this.hasOuterHandles = true;
15229     },
15230
15231     /**
15232      * Remove all drag and drop hooks for this element
15233      * @method unreg
15234      */
15235     unreg: function() {
15236         Event.un(this.id, "mousedown",
15237                 this.handleMouseDown);
15238         this._domRef = null;
15239         this.DDM._remove(this);
15240     },
15241
15242     destroy : function(){
15243         this.unreg();
15244     },
15245
15246     /**
15247      * Returns true if this instance is locked, or the drag drop mgr is locked
15248      * (meaning that all drag/drop is disabled on the page.)
15249      * @method isLocked
15250      * @return {boolean} true if this obj or all drag/drop is locked, else
15251      * false
15252      */
15253     isLocked: function() {
15254         return (this.DDM.isLocked() || this.locked);
15255     },
15256
15257     /**
15258      * Fired when this object is clicked
15259      * @method handleMouseDown
15260      * @param {Event} e
15261      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15262      * @private
15263      */
15264     handleMouseDown: function(e, oDD){
15265         if (this.primaryButtonOnly && e.button != 0) {
15266             return;
15267         }
15268
15269         if (this.isLocked()) {
15270             return;
15271         }
15272
15273         this.DDM.refreshCache(this.groups);
15274
15275         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15276         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15277         } else {
15278             if (this.clickValidator(e)) {
15279
15280                 // set the initial element position
15281                 this.setStartPosition();
15282
15283
15284                 this.b4MouseDown(e);
15285                 this.onMouseDown(e);
15286
15287                 this.DDM.handleMouseDown(e, this);
15288
15289                 this.DDM.stopEvent(e);
15290             } else {
15291
15292
15293             }
15294         }
15295     },
15296
15297     clickValidator: function(e) {
15298         var target = e.getTarget();
15299         return ( this.isValidHandleChild(target) &&
15300                     (this.id == this.handleElId ||
15301                         this.DDM.handleWasClicked(target, this.id)) );
15302     },
15303
15304     /**
15305      * Allows you to specify a tag name that should not start a drag operation
15306      * when clicked.  This is designed to facilitate embedding links within a
15307      * drag handle that do something other than start the drag.
15308      * @method addInvalidHandleType
15309      * @param {string} tagName the type of element to exclude
15310      */
15311     addInvalidHandleType: function(tagName) {
15312         var type = tagName.toUpperCase();
15313         this.invalidHandleTypes[type] = type;
15314     },
15315
15316     /**
15317      * Lets you to specify an element id for a child of a drag handle
15318      * that should not initiate a drag
15319      * @method addInvalidHandleId
15320      * @param {string} id the element id of the element you wish to ignore
15321      */
15322     addInvalidHandleId: function(id) {
15323         if (typeof id !== "string") {
15324             id = Roo.id(id);
15325         }
15326         this.invalidHandleIds[id] = id;
15327     },
15328
15329     /**
15330      * Lets you specify a css class of elements that will not initiate a drag
15331      * @method addInvalidHandleClass
15332      * @param {string} cssClass the class of the elements you wish to ignore
15333      */
15334     addInvalidHandleClass: function(cssClass) {
15335         this.invalidHandleClasses.push(cssClass);
15336     },
15337
15338     /**
15339      * Unsets an excluded tag name set by addInvalidHandleType
15340      * @method removeInvalidHandleType
15341      * @param {string} tagName the type of element to unexclude
15342      */
15343     removeInvalidHandleType: function(tagName) {
15344         var type = tagName.toUpperCase();
15345         // this.invalidHandleTypes[type] = null;
15346         delete this.invalidHandleTypes[type];
15347     },
15348
15349     /**
15350      * Unsets an invalid handle id
15351      * @method removeInvalidHandleId
15352      * @param {string} id the id of the element to re-enable
15353      */
15354     removeInvalidHandleId: function(id) {
15355         if (typeof id !== "string") {
15356             id = Roo.id(id);
15357         }
15358         delete this.invalidHandleIds[id];
15359     },
15360
15361     /**
15362      * Unsets an invalid css class
15363      * @method removeInvalidHandleClass
15364      * @param {string} cssClass the class of the element(s) you wish to
15365      * re-enable
15366      */
15367     removeInvalidHandleClass: function(cssClass) {
15368         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15369             if (this.invalidHandleClasses[i] == cssClass) {
15370                 delete this.invalidHandleClasses[i];
15371             }
15372         }
15373     },
15374
15375     /**
15376      * Checks the tag exclusion list to see if this click should be ignored
15377      * @method isValidHandleChild
15378      * @param {HTMLElement} node the HTMLElement to evaluate
15379      * @return {boolean} true if this is a valid tag type, false if not
15380      */
15381     isValidHandleChild: function(node) {
15382
15383         var valid = true;
15384         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15385         var nodeName;
15386         try {
15387             nodeName = node.nodeName.toUpperCase();
15388         } catch(e) {
15389             nodeName = node.nodeName;
15390         }
15391         valid = valid && !this.invalidHandleTypes[nodeName];
15392         valid = valid && !this.invalidHandleIds[node.id];
15393
15394         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15395             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15396         }
15397
15398
15399         return valid;
15400
15401     },
15402
15403     /**
15404      * Create the array of horizontal tick marks if an interval was specified
15405      * in setXConstraint().
15406      * @method setXTicks
15407      * @private
15408      */
15409     setXTicks: function(iStartX, iTickSize) {
15410         this.xTicks = [];
15411         this.xTickSize = iTickSize;
15412
15413         var tickMap = {};
15414
15415         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15416             if (!tickMap[i]) {
15417                 this.xTicks[this.xTicks.length] = i;
15418                 tickMap[i] = true;
15419             }
15420         }
15421
15422         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15423             if (!tickMap[i]) {
15424                 this.xTicks[this.xTicks.length] = i;
15425                 tickMap[i] = true;
15426             }
15427         }
15428
15429         this.xTicks.sort(this.DDM.numericSort) ;
15430     },
15431
15432     /**
15433      * Create the array of vertical tick marks if an interval was specified in
15434      * setYConstraint().
15435      * @method setYTicks
15436      * @private
15437      */
15438     setYTicks: function(iStartY, iTickSize) {
15439         this.yTicks = [];
15440         this.yTickSize = iTickSize;
15441
15442         var tickMap = {};
15443
15444         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15445             if (!tickMap[i]) {
15446                 this.yTicks[this.yTicks.length] = i;
15447                 tickMap[i] = true;
15448             }
15449         }
15450
15451         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15452             if (!tickMap[i]) {
15453                 this.yTicks[this.yTicks.length] = i;
15454                 tickMap[i] = true;
15455             }
15456         }
15457
15458         this.yTicks.sort(this.DDM.numericSort) ;
15459     },
15460
15461     /**
15462      * By default, the element can be dragged any place on the screen.  Use
15463      * this method to limit the horizontal travel of the element.  Pass in
15464      * 0,0 for the parameters if you want to lock the drag to the y axis.
15465      * @method setXConstraint
15466      * @param {int} iLeft the number of pixels the element can move to the left
15467      * @param {int} iRight the number of pixels the element can move to the
15468      * right
15469      * @param {int} iTickSize optional parameter for specifying that the
15470      * element
15471      * should move iTickSize pixels at a time.
15472      */
15473     setXConstraint: function(iLeft, iRight, iTickSize) {
15474         this.leftConstraint = iLeft;
15475         this.rightConstraint = iRight;
15476
15477         this.minX = this.initPageX - iLeft;
15478         this.maxX = this.initPageX + iRight;
15479         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15480
15481         this.constrainX = true;
15482     },
15483
15484     /**
15485      * Clears any constraints applied to this instance.  Also clears ticks
15486      * since they can't exist independent of a constraint at this time.
15487      * @method clearConstraints
15488      */
15489     clearConstraints: function() {
15490         this.constrainX = false;
15491         this.constrainY = false;
15492         this.clearTicks();
15493     },
15494
15495     /**
15496      * Clears any tick interval defined for this instance
15497      * @method clearTicks
15498      */
15499     clearTicks: function() {
15500         this.xTicks = null;
15501         this.yTicks = null;
15502         this.xTickSize = 0;
15503         this.yTickSize = 0;
15504     },
15505
15506     /**
15507      * By default, the element can be dragged any place on the screen.  Set
15508      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15509      * parameters if you want to lock the drag to the x axis.
15510      * @method setYConstraint
15511      * @param {int} iUp the number of pixels the element can move up
15512      * @param {int} iDown the number of pixels the element can move down
15513      * @param {int} iTickSize optional parameter for specifying that the
15514      * element should move iTickSize pixels at a time.
15515      */
15516     setYConstraint: function(iUp, iDown, iTickSize) {
15517         this.topConstraint = iUp;
15518         this.bottomConstraint = iDown;
15519
15520         this.minY = this.initPageY - iUp;
15521         this.maxY = this.initPageY + iDown;
15522         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15523
15524         this.constrainY = true;
15525
15526     },
15527
15528     /**
15529      * resetConstraints must be called if you manually reposition a dd element.
15530      * @method resetConstraints
15531      * @param {boolean} maintainOffset
15532      */
15533     resetConstraints: function() {
15534
15535
15536         // Maintain offsets if necessary
15537         if (this.initPageX || this.initPageX === 0) {
15538             // figure out how much this thing has moved
15539             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15540             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15541
15542             this.setInitPosition(dx, dy);
15543
15544         // This is the first time we have detected the element's position
15545         } else {
15546             this.setInitPosition();
15547         }
15548
15549         if (this.constrainX) {
15550             this.setXConstraint( this.leftConstraint,
15551                                  this.rightConstraint,
15552                                  this.xTickSize        );
15553         }
15554
15555         if (this.constrainY) {
15556             this.setYConstraint( this.topConstraint,
15557                                  this.bottomConstraint,
15558                                  this.yTickSize         );
15559         }
15560     },
15561
15562     /**
15563      * Normally the drag element is moved pixel by pixel, but we can specify
15564      * that it move a number of pixels at a time.  This method resolves the
15565      * location when we have it set up like this.
15566      * @method getTick
15567      * @param {int} val where we want to place the object
15568      * @param {int[]} tickArray sorted array of valid points
15569      * @return {int} the closest tick
15570      * @private
15571      */
15572     getTick: function(val, tickArray) {
15573
15574         if (!tickArray) {
15575             // If tick interval is not defined, it is effectively 1 pixel,
15576             // so we return the value passed to us.
15577             return val;
15578         } else if (tickArray[0] >= val) {
15579             // The value is lower than the first tick, so we return the first
15580             // tick.
15581             return tickArray[0];
15582         } else {
15583             for (var i=0, len=tickArray.length; i<len; ++i) {
15584                 var next = i + 1;
15585                 if (tickArray[next] && tickArray[next] >= val) {
15586                     var diff1 = val - tickArray[i];
15587                     var diff2 = tickArray[next] - val;
15588                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15589                 }
15590             }
15591
15592             // The value is larger than the last tick, so we return the last
15593             // tick.
15594             return tickArray[tickArray.length - 1];
15595         }
15596     },
15597
15598     /**
15599      * toString method
15600      * @method toString
15601      * @return {string} string representation of the dd obj
15602      */
15603     toString: function() {
15604         return ("DragDrop " + this.id);
15605     }
15606
15607 });
15608
15609 })();
15610 /*
15611  * Based on:
15612  * Ext JS Library 1.1.1
15613  * Copyright(c) 2006-2007, Ext JS, LLC.
15614  *
15615  * Originally Released Under LGPL - original licence link has changed is not relivant.
15616  *
15617  * Fork - LGPL
15618  * <script type="text/javascript">
15619  */
15620
15621
15622 /**
15623  * The drag and drop utility provides a framework for building drag and drop
15624  * applications.  In addition to enabling drag and drop for specific elements,
15625  * the drag and drop elements are tracked by the manager class, and the
15626  * interactions between the various elements are tracked during the drag and
15627  * the implementing code is notified about these important moments.
15628  */
15629
15630 // Only load the library once.  Rewriting the manager class would orphan
15631 // existing drag and drop instances.
15632 if (!Roo.dd.DragDropMgr) {
15633
15634 /**
15635  * @class Roo.dd.DragDropMgr
15636  * DragDropMgr is a singleton that tracks the element interaction for
15637  * all DragDrop items in the window.  Generally, you will not call
15638  * this class directly, but it does have helper methods that could
15639  * be useful in your DragDrop implementations.
15640  * @singleton
15641  */
15642 Roo.dd.DragDropMgr = function() {
15643
15644     var Event = Roo.EventManager;
15645
15646     return {
15647
15648         /**
15649          * Two dimensional Array of registered DragDrop objects.  The first
15650          * dimension is the DragDrop item group, the second the DragDrop
15651          * object.
15652          * @property ids
15653          * @type {string: string}
15654          * @private
15655          * @static
15656          */
15657         ids: {},
15658
15659         /**
15660          * Array of element ids defined as drag handles.  Used to determine
15661          * if the element that generated the mousedown event is actually the
15662          * handle and not the html element itself.
15663          * @property handleIds
15664          * @type {string: string}
15665          * @private
15666          * @static
15667          */
15668         handleIds: {},
15669
15670         /**
15671          * the DragDrop object that is currently being dragged
15672          * @property dragCurrent
15673          * @type DragDrop
15674          * @private
15675          * @static
15676          **/
15677         dragCurrent: null,
15678
15679         /**
15680          * the DragDrop object(s) that are being hovered over
15681          * @property dragOvers
15682          * @type Array
15683          * @private
15684          * @static
15685          */
15686         dragOvers: {},
15687
15688         /**
15689          * the X distance between the cursor and the object being dragged
15690          * @property deltaX
15691          * @type int
15692          * @private
15693          * @static
15694          */
15695         deltaX: 0,
15696
15697         /**
15698          * the Y distance between the cursor and the object being dragged
15699          * @property deltaY
15700          * @type int
15701          * @private
15702          * @static
15703          */
15704         deltaY: 0,
15705
15706         /**
15707          * Flag to determine if we should prevent the default behavior of the
15708          * events we define. By default this is true, but this can be set to
15709          * false if you need the default behavior (not recommended)
15710          * @property preventDefault
15711          * @type boolean
15712          * @static
15713          */
15714         preventDefault: true,
15715
15716         /**
15717          * Flag to determine if we should stop the propagation of the events
15718          * we generate. This is true by default but you may want to set it to
15719          * false if the html element contains other features that require the
15720          * mouse click.
15721          * @property stopPropagation
15722          * @type boolean
15723          * @static
15724          */
15725         stopPropagation: true,
15726
15727         /**
15728          * Internal flag that is set to true when drag and drop has been
15729          * intialized
15730          * @property initialized
15731          * @private
15732          * @static
15733          */
15734         initalized: false,
15735
15736         /**
15737          * All drag and drop can be disabled.
15738          * @property locked
15739          * @private
15740          * @static
15741          */
15742         locked: false,
15743
15744         /**
15745          * Called the first time an element is registered.
15746          * @method init
15747          * @private
15748          * @static
15749          */
15750         init: function() {
15751             this.initialized = true;
15752         },
15753
15754         /**
15755          * In point mode, drag and drop interaction is defined by the
15756          * location of the cursor during the drag/drop
15757          * @property POINT
15758          * @type int
15759          * @static
15760          */
15761         POINT: 0,
15762
15763         /**
15764          * In intersect mode, drag and drop interactio nis defined by the
15765          * overlap of two or more drag and drop objects.
15766          * @property INTERSECT
15767          * @type int
15768          * @static
15769          */
15770         INTERSECT: 1,
15771
15772         /**
15773          * The current drag and drop mode.  Default: POINT
15774          * @property mode
15775          * @type int
15776          * @static
15777          */
15778         mode: 0,
15779
15780         /**
15781          * Runs method on all drag and drop objects
15782          * @method _execOnAll
15783          * @private
15784          * @static
15785          */
15786         _execOnAll: function(sMethod, args) {
15787             for (var i in this.ids) {
15788                 for (var j in this.ids[i]) {
15789                     var oDD = this.ids[i][j];
15790                     if (! this.isTypeOfDD(oDD)) {
15791                         continue;
15792                     }
15793                     oDD[sMethod].apply(oDD, args);
15794                 }
15795             }
15796         },
15797
15798         /**
15799          * Drag and drop initialization.  Sets up the global event handlers
15800          * @method _onLoad
15801          * @private
15802          * @static
15803          */
15804         _onLoad: function() {
15805
15806             this.init();
15807
15808
15809             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15810             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15811             Event.on(window,   "unload",    this._onUnload, this, true);
15812             Event.on(window,   "resize",    this._onResize, this, true);
15813             // Event.on(window,   "mouseout",    this._test);
15814
15815         },
15816
15817         /**
15818          * Reset constraints on all drag and drop objs
15819          * @method _onResize
15820          * @private
15821          * @static
15822          */
15823         _onResize: function(e) {
15824             this._execOnAll("resetConstraints", []);
15825         },
15826
15827         /**
15828          * Lock all drag and drop functionality
15829          * @method lock
15830          * @static
15831          */
15832         lock: function() { this.locked = true; },
15833
15834         /**
15835          * Unlock all drag and drop functionality
15836          * @method unlock
15837          * @static
15838          */
15839         unlock: function() { this.locked = false; },
15840
15841         /**
15842          * Is drag and drop locked?
15843          * @method isLocked
15844          * @return {boolean} True if drag and drop is locked, false otherwise.
15845          * @static
15846          */
15847         isLocked: function() { return this.locked; },
15848
15849         /**
15850          * Location cache that is set for all drag drop objects when a drag is
15851          * initiated, cleared when the drag is finished.
15852          * @property locationCache
15853          * @private
15854          * @static
15855          */
15856         locationCache: {},
15857
15858         /**
15859          * Set useCache to false if you want to force object the lookup of each
15860          * drag and drop linked element constantly during a drag.
15861          * @property useCache
15862          * @type boolean
15863          * @static
15864          */
15865         useCache: true,
15866
15867         /**
15868          * The number of pixels that the mouse needs to move after the
15869          * mousedown before the drag is initiated.  Default=3;
15870          * @property clickPixelThresh
15871          * @type int
15872          * @static
15873          */
15874         clickPixelThresh: 3,
15875
15876         /**
15877          * The number of milliseconds after the mousedown event to initiate the
15878          * drag if we don't get a mouseup event. Default=1000
15879          * @property clickTimeThresh
15880          * @type int
15881          * @static
15882          */
15883         clickTimeThresh: 350,
15884
15885         /**
15886          * Flag that indicates that either the drag pixel threshold or the
15887          * mousdown time threshold has been met
15888          * @property dragThreshMet
15889          * @type boolean
15890          * @private
15891          * @static
15892          */
15893         dragThreshMet: false,
15894
15895         /**
15896          * Timeout used for the click time threshold
15897          * @property clickTimeout
15898          * @type Object
15899          * @private
15900          * @static
15901          */
15902         clickTimeout: null,
15903
15904         /**
15905          * The X position of the mousedown event stored for later use when a
15906          * drag threshold is met.
15907          * @property startX
15908          * @type int
15909          * @private
15910          * @static
15911          */
15912         startX: 0,
15913
15914         /**
15915          * The Y position of the mousedown event stored for later use when a
15916          * drag threshold is met.
15917          * @property startY
15918          * @type int
15919          * @private
15920          * @static
15921          */
15922         startY: 0,
15923
15924         /**
15925          * Each DragDrop instance must be registered with the DragDropMgr.
15926          * This is executed in DragDrop.init()
15927          * @method regDragDrop
15928          * @param {DragDrop} oDD the DragDrop object to register
15929          * @param {String} sGroup the name of the group this element belongs to
15930          * @static
15931          */
15932         regDragDrop: function(oDD, sGroup) {
15933             if (!this.initialized) { this.init(); }
15934
15935             if (!this.ids[sGroup]) {
15936                 this.ids[sGroup] = {};
15937             }
15938             this.ids[sGroup][oDD.id] = oDD;
15939         },
15940
15941         /**
15942          * Removes the supplied dd instance from the supplied group. Executed
15943          * by DragDrop.removeFromGroup, so don't call this function directly.
15944          * @method removeDDFromGroup
15945          * @private
15946          * @static
15947          */
15948         removeDDFromGroup: function(oDD, sGroup) {
15949             if (!this.ids[sGroup]) {
15950                 this.ids[sGroup] = {};
15951             }
15952
15953             var obj = this.ids[sGroup];
15954             if (obj && obj[oDD.id]) {
15955                 delete obj[oDD.id];
15956             }
15957         },
15958
15959         /**
15960          * Unregisters a drag and drop item.  This is executed in
15961          * DragDrop.unreg, use that method instead of calling this directly.
15962          * @method _remove
15963          * @private
15964          * @static
15965          */
15966         _remove: function(oDD) {
15967             for (var g in oDD.groups) {
15968                 if (g && this.ids[g][oDD.id]) {
15969                     delete this.ids[g][oDD.id];
15970                 }
15971             }
15972             delete this.handleIds[oDD.id];
15973         },
15974
15975         /**
15976          * Each DragDrop handle element must be registered.  This is done
15977          * automatically when executing DragDrop.setHandleElId()
15978          * @method regHandle
15979          * @param {String} sDDId the DragDrop id this element is a handle for
15980          * @param {String} sHandleId the id of the element that is the drag
15981          * handle
15982          * @static
15983          */
15984         regHandle: function(sDDId, sHandleId) {
15985             if (!this.handleIds[sDDId]) {
15986                 this.handleIds[sDDId] = {};
15987             }
15988             this.handleIds[sDDId][sHandleId] = sHandleId;
15989         },
15990
15991         /**
15992          * Utility function to determine if a given element has been
15993          * registered as a drag drop item.
15994          * @method isDragDrop
15995          * @param {String} id the element id to check
15996          * @return {boolean} true if this element is a DragDrop item,
15997          * false otherwise
15998          * @static
15999          */
16000         isDragDrop: function(id) {
16001             return ( this.getDDById(id) ) ? true : false;
16002         },
16003
16004         /**
16005          * Returns the drag and drop instances that are in all groups the
16006          * passed in instance belongs to.
16007          * @method getRelated
16008          * @param {DragDrop} p_oDD the obj to get related data for
16009          * @param {boolean} bTargetsOnly if true, only return targetable objs
16010          * @return {DragDrop[]} the related instances
16011          * @static
16012          */
16013         getRelated: function(p_oDD, bTargetsOnly) {
16014             var oDDs = [];
16015             for (var i in p_oDD.groups) {
16016                 for (j in this.ids[i]) {
16017                     var dd = this.ids[i][j];
16018                     if (! this.isTypeOfDD(dd)) {
16019                         continue;
16020                     }
16021                     if (!bTargetsOnly || dd.isTarget) {
16022                         oDDs[oDDs.length] = dd;
16023                     }
16024                 }
16025             }
16026
16027             return oDDs;
16028         },
16029
16030         /**
16031          * Returns true if the specified dd target is a legal target for
16032          * the specifice drag obj
16033          * @method isLegalTarget
16034          * @param {DragDrop} the drag obj
16035          * @param {DragDrop} the target
16036          * @return {boolean} true if the target is a legal target for the
16037          * dd obj
16038          * @static
16039          */
16040         isLegalTarget: function (oDD, oTargetDD) {
16041             var targets = this.getRelated(oDD, true);
16042             for (var i=0, len=targets.length;i<len;++i) {
16043                 if (targets[i].id == oTargetDD.id) {
16044                     return true;
16045                 }
16046             }
16047
16048             return false;
16049         },
16050
16051         /**
16052          * My goal is to be able to transparently determine if an object is
16053          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16054          * returns "object", oDD.constructor.toString() always returns
16055          * "DragDrop" and not the name of the subclass.  So for now it just
16056          * evaluates a well-known variable in DragDrop.
16057          * @method isTypeOfDD
16058          * @param {Object} the object to evaluate
16059          * @return {boolean} true if typeof oDD = DragDrop
16060          * @static
16061          */
16062         isTypeOfDD: function (oDD) {
16063             return (oDD && oDD.__ygDragDrop);
16064         },
16065
16066         /**
16067          * Utility function to determine if a given element has been
16068          * registered as a drag drop handle for the given Drag Drop object.
16069          * @method isHandle
16070          * @param {String} id the element id to check
16071          * @return {boolean} true if this element is a DragDrop handle, false
16072          * otherwise
16073          * @static
16074          */
16075         isHandle: function(sDDId, sHandleId) {
16076             return ( this.handleIds[sDDId] &&
16077                             this.handleIds[sDDId][sHandleId] );
16078         },
16079
16080         /**
16081          * Returns the DragDrop instance for a given id
16082          * @method getDDById
16083          * @param {String} id the id of the DragDrop object
16084          * @return {DragDrop} the drag drop object, null if it is not found
16085          * @static
16086          */
16087         getDDById: function(id) {
16088             for (var i in this.ids) {
16089                 if (this.ids[i][id]) {
16090                     return this.ids[i][id];
16091                 }
16092             }
16093             return null;
16094         },
16095
16096         /**
16097          * Fired after a registered DragDrop object gets the mousedown event.
16098          * Sets up the events required to track the object being dragged
16099          * @method handleMouseDown
16100          * @param {Event} e the event
16101          * @param oDD the DragDrop object being dragged
16102          * @private
16103          * @static
16104          */
16105         handleMouseDown: function(e, oDD) {
16106             if(Roo.QuickTips){
16107                 Roo.QuickTips.disable();
16108             }
16109             this.currentTarget = e.getTarget();
16110
16111             this.dragCurrent = oDD;
16112
16113             var el = oDD.getEl();
16114
16115             // track start position
16116             this.startX = e.getPageX();
16117             this.startY = e.getPageY();
16118
16119             this.deltaX = this.startX - el.offsetLeft;
16120             this.deltaY = this.startY - el.offsetTop;
16121
16122             this.dragThreshMet = false;
16123
16124             this.clickTimeout = setTimeout(
16125                     function() {
16126                         var DDM = Roo.dd.DDM;
16127                         DDM.startDrag(DDM.startX, DDM.startY);
16128                     },
16129                     this.clickTimeThresh );
16130         },
16131
16132         /**
16133          * Fired when either the drag pixel threshol or the mousedown hold
16134          * time threshold has been met.
16135          * @method startDrag
16136          * @param x {int} the X position of the original mousedown
16137          * @param y {int} the Y position of the original mousedown
16138          * @static
16139          */
16140         startDrag: function(x, y) {
16141             clearTimeout(this.clickTimeout);
16142             if (this.dragCurrent) {
16143                 this.dragCurrent.b4StartDrag(x, y);
16144                 this.dragCurrent.startDrag(x, y);
16145             }
16146             this.dragThreshMet = true;
16147         },
16148
16149         /**
16150          * Internal function to handle the mouseup event.  Will be invoked
16151          * from the context of the document.
16152          * @method handleMouseUp
16153          * @param {Event} e the event
16154          * @private
16155          * @static
16156          */
16157         handleMouseUp: function(e) {
16158
16159             if(Roo.QuickTips){
16160                 Roo.QuickTips.enable();
16161             }
16162             if (! this.dragCurrent) {
16163                 return;
16164             }
16165
16166             clearTimeout(this.clickTimeout);
16167
16168             if (this.dragThreshMet) {
16169                 this.fireEvents(e, true);
16170             } else {
16171             }
16172
16173             this.stopDrag(e);
16174
16175             this.stopEvent(e);
16176         },
16177
16178         /**
16179          * Utility to stop event propagation and event default, if these
16180          * features are turned on.
16181          * @method stopEvent
16182          * @param {Event} e the event as returned by this.getEvent()
16183          * @static
16184          */
16185         stopEvent: function(e){
16186             if(this.stopPropagation) {
16187                 e.stopPropagation();
16188             }
16189
16190             if (this.preventDefault) {
16191                 e.preventDefault();
16192             }
16193         },
16194
16195         /**
16196          * Internal function to clean up event handlers after the drag
16197          * operation is complete
16198          * @method stopDrag
16199          * @param {Event} e the event
16200          * @private
16201          * @static
16202          */
16203         stopDrag: function(e) {
16204             // Fire the drag end event for the item that was dragged
16205             if (this.dragCurrent) {
16206                 if (this.dragThreshMet) {
16207                     this.dragCurrent.b4EndDrag(e);
16208                     this.dragCurrent.endDrag(e);
16209                 }
16210
16211                 this.dragCurrent.onMouseUp(e);
16212             }
16213
16214             this.dragCurrent = null;
16215             this.dragOvers = {};
16216         },
16217
16218         /**
16219          * Internal function to handle the mousemove event.  Will be invoked
16220          * from the context of the html element.
16221          *
16222          * @TODO figure out what we can do about mouse events lost when the
16223          * user drags objects beyond the window boundary.  Currently we can
16224          * detect this in internet explorer by verifying that the mouse is
16225          * down during the mousemove event.  Firefox doesn't give us the
16226          * button state on the mousemove event.
16227          * @method handleMouseMove
16228          * @param {Event} e the event
16229          * @private
16230          * @static
16231          */
16232         handleMouseMove: function(e) {
16233             if (! this.dragCurrent) {
16234                 return true;
16235             }
16236
16237             // var button = e.which || e.button;
16238
16239             // check for IE mouseup outside of page boundary
16240             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16241                 this.stopEvent(e);
16242                 return this.handleMouseUp(e);
16243             }
16244
16245             if (!this.dragThreshMet) {
16246                 var diffX = Math.abs(this.startX - e.getPageX());
16247                 var diffY = Math.abs(this.startY - e.getPageY());
16248                 if (diffX > this.clickPixelThresh ||
16249                             diffY > this.clickPixelThresh) {
16250                     this.startDrag(this.startX, this.startY);
16251                 }
16252             }
16253
16254             if (this.dragThreshMet) {
16255                 this.dragCurrent.b4Drag(e);
16256                 this.dragCurrent.onDrag(e);
16257                 if(!this.dragCurrent.moveOnly){
16258                     this.fireEvents(e, false);
16259                 }
16260             }
16261
16262             this.stopEvent(e);
16263
16264             return true;
16265         },
16266
16267         /**
16268          * Iterates over all of the DragDrop elements to find ones we are
16269          * hovering over or dropping on
16270          * @method fireEvents
16271          * @param {Event} e the event
16272          * @param {boolean} isDrop is this a drop op or a mouseover op?
16273          * @private
16274          * @static
16275          */
16276         fireEvents: function(e, isDrop) {
16277             var dc = this.dragCurrent;
16278
16279             // If the user did the mouse up outside of the window, we could
16280             // get here even though we have ended the drag.
16281             if (!dc || dc.isLocked()) {
16282                 return;
16283             }
16284
16285             var pt = e.getPoint();
16286
16287             // cache the previous dragOver array
16288             var oldOvers = [];
16289
16290             var outEvts   = [];
16291             var overEvts  = [];
16292             var dropEvts  = [];
16293             var enterEvts = [];
16294
16295             // Check to see if the object(s) we were hovering over is no longer
16296             // being hovered over so we can fire the onDragOut event
16297             for (var i in this.dragOvers) {
16298
16299                 var ddo = this.dragOvers[i];
16300
16301                 if (! this.isTypeOfDD(ddo)) {
16302                     continue;
16303                 }
16304
16305                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16306                     outEvts.push( ddo );
16307                 }
16308
16309                 oldOvers[i] = true;
16310                 delete this.dragOvers[i];
16311             }
16312
16313             for (var sGroup in dc.groups) {
16314
16315                 if ("string" != typeof sGroup) {
16316                     continue;
16317                 }
16318
16319                 for (i in this.ids[sGroup]) {
16320                     var oDD = this.ids[sGroup][i];
16321                     if (! this.isTypeOfDD(oDD)) {
16322                         continue;
16323                     }
16324
16325                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16326                         if (this.isOverTarget(pt, oDD, this.mode)) {
16327                             // look for drop interactions
16328                             if (isDrop) {
16329                                 dropEvts.push( oDD );
16330                             // look for drag enter and drag over interactions
16331                             } else {
16332
16333                                 // initial drag over: dragEnter fires
16334                                 if (!oldOvers[oDD.id]) {
16335                                     enterEvts.push( oDD );
16336                                 // subsequent drag overs: dragOver fires
16337                                 } else {
16338                                     overEvts.push( oDD );
16339                                 }
16340
16341                                 this.dragOvers[oDD.id] = oDD;
16342                             }
16343                         }
16344                     }
16345                 }
16346             }
16347
16348             if (this.mode) {
16349                 if (outEvts.length) {
16350                     dc.b4DragOut(e, outEvts);
16351                     dc.onDragOut(e, outEvts);
16352                 }
16353
16354                 if (enterEvts.length) {
16355                     dc.onDragEnter(e, enterEvts);
16356                 }
16357
16358                 if (overEvts.length) {
16359                     dc.b4DragOver(e, overEvts);
16360                     dc.onDragOver(e, overEvts);
16361                 }
16362
16363                 if (dropEvts.length) {
16364                     dc.b4DragDrop(e, dropEvts);
16365                     dc.onDragDrop(e, dropEvts);
16366                 }
16367
16368             } else {
16369                 // fire dragout events
16370                 var len = 0;
16371                 for (i=0, len=outEvts.length; i<len; ++i) {
16372                     dc.b4DragOut(e, outEvts[i].id);
16373                     dc.onDragOut(e, outEvts[i].id);
16374                 }
16375
16376                 // fire enter events
16377                 for (i=0,len=enterEvts.length; i<len; ++i) {
16378                     // dc.b4DragEnter(e, oDD.id);
16379                     dc.onDragEnter(e, enterEvts[i].id);
16380                 }
16381
16382                 // fire over events
16383                 for (i=0,len=overEvts.length; i<len; ++i) {
16384                     dc.b4DragOver(e, overEvts[i].id);
16385                     dc.onDragOver(e, overEvts[i].id);
16386                 }
16387
16388                 // fire drop events
16389                 for (i=0, len=dropEvts.length; i<len; ++i) {
16390                     dc.b4DragDrop(e, dropEvts[i].id);
16391                     dc.onDragDrop(e, dropEvts[i].id);
16392                 }
16393
16394             }
16395
16396             // notify about a drop that did not find a target
16397             if (isDrop && !dropEvts.length) {
16398                 dc.onInvalidDrop(e);
16399             }
16400
16401         },
16402
16403         /**
16404          * Helper function for getting the best match from the list of drag
16405          * and drop objects returned by the drag and drop events when we are
16406          * in INTERSECT mode.  It returns either the first object that the
16407          * cursor is over, or the object that has the greatest overlap with
16408          * the dragged element.
16409          * @method getBestMatch
16410          * @param  {DragDrop[]} dds The array of drag and drop objects
16411          * targeted
16412          * @return {DragDrop}       The best single match
16413          * @static
16414          */
16415         getBestMatch: function(dds) {
16416             var winner = null;
16417             // Return null if the input is not what we expect
16418             //if (!dds || !dds.length || dds.length == 0) {
16419                // winner = null;
16420             // If there is only one item, it wins
16421             //} else if (dds.length == 1) {
16422
16423             var len = dds.length;
16424
16425             if (len == 1) {
16426                 winner = dds[0];
16427             } else {
16428                 // Loop through the targeted items
16429                 for (var i=0; i<len; ++i) {
16430                     var dd = dds[i];
16431                     // If the cursor is over the object, it wins.  If the
16432                     // cursor is over multiple matches, the first one we come
16433                     // to wins.
16434                     if (dd.cursorIsOver) {
16435                         winner = dd;
16436                         break;
16437                     // Otherwise the object with the most overlap wins
16438                     } else {
16439                         if (!winner ||
16440                             winner.overlap.getArea() < dd.overlap.getArea()) {
16441                             winner = dd;
16442                         }
16443                     }
16444                 }
16445             }
16446
16447             return winner;
16448         },
16449
16450         /**
16451          * Refreshes the cache of the top-left and bottom-right points of the
16452          * drag and drop objects in the specified group(s).  This is in the
16453          * format that is stored in the drag and drop instance, so typical
16454          * usage is:
16455          * <code>
16456          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16457          * </code>
16458          * Alternatively:
16459          * <code>
16460          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16461          * </code>
16462          * @TODO this really should be an indexed array.  Alternatively this
16463          * method could accept both.
16464          * @method refreshCache
16465          * @param {Object} groups an associative array of groups to refresh
16466          * @static
16467          */
16468         refreshCache: function(groups) {
16469             for (var sGroup in groups) {
16470                 if ("string" != typeof sGroup) {
16471                     continue;
16472                 }
16473                 for (var i in this.ids[sGroup]) {
16474                     var oDD = this.ids[sGroup][i];
16475
16476                     if (this.isTypeOfDD(oDD)) {
16477                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16478                         var loc = this.getLocation(oDD);
16479                         if (loc) {
16480                             this.locationCache[oDD.id] = loc;
16481                         } else {
16482                             delete this.locationCache[oDD.id];
16483                             // this will unregister the drag and drop object if
16484                             // the element is not in a usable state
16485                             // oDD.unreg();
16486                         }
16487                     }
16488                 }
16489             }
16490         },
16491
16492         /**
16493          * This checks to make sure an element exists and is in the DOM.  The
16494          * main purpose is to handle cases where innerHTML is used to remove
16495          * drag and drop objects from the DOM.  IE provides an 'unspecified
16496          * error' when trying to access the offsetParent of such an element
16497          * @method verifyEl
16498          * @param {HTMLElement} el the element to check
16499          * @return {boolean} true if the element looks usable
16500          * @static
16501          */
16502         verifyEl: function(el) {
16503             if (el) {
16504                 var parent;
16505                 if(Roo.isIE){
16506                     try{
16507                         parent = el.offsetParent;
16508                     }catch(e){}
16509                 }else{
16510                     parent = el.offsetParent;
16511                 }
16512                 if (parent) {
16513                     return true;
16514                 }
16515             }
16516
16517             return false;
16518         },
16519
16520         /**
16521          * Returns a Region object containing the drag and drop element's position
16522          * and size, including the padding configured for it
16523          * @method getLocation
16524          * @param {DragDrop} oDD the drag and drop object to get the
16525          *                       location for
16526          * @return {Roo.lib.Region} a Region object representing the total area
16527          *                             the element occupies, including any padding
16528          *                             the instance is configured for.
16529          * @static
16530          */
16531         getLocation: function(oDD) {
16532             if (! this.isTypeOfDD(oDD)) {
16533                 return null;
16534             }
16535
16536             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16537
16538             try {
16539                 pos= Roo.lib.Dom.getXY(el);
16540             } catch (e) { }
16541
16542             if (!pos) {
16543                 return null;
16544             }
16545
16546             x1 = pos[0];
16547             x2 = x1 + el.offsetWidth;
16548             y1 = pos[1];
16549             y2 = y1 + el.offsetHeight;
16550
16551             t = y1 - oDD.padding[0];
16552             r = x2 + oDD.padding[1];
16553             b = y2 + oDD.padding[2];
16554             l = x1 - oDD.padding[3];
16555
16556             return new Roo.lib.Region( t, r, b, l );
16557         },
16558
16559         /**
16560          * Checks the cursor location to see if it over the target
16561          * @method isOverTarget
16562          * @param {Roo.lib.Point} pt The point to evaluate
16563          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16564          * @return {boolean} true if the mouse is over the target
16565          * @private
16566          * @static
16567          */
16568         isOverTarget: function(pt, oTarget, intersect) {
16569             // use cache if available
16570             var loc = this.locationCache[oTarget.id];
16571             if (!loc || !this.useCache) {
16572                 loc = this.getLocation(oTarget);
16573                 this.locationCache[oTarget.id] = loc;
16574
16575             }
16576
16577             if (!loc) {
16578                 return false;
16579             }
16580
16581             oTarget.cursorIsOver = loc.contains( pt );
16582
16583             // DragDrop is using this as a sanity check for the initial mousedown
16584             // in this case we are done.  In POINT mode, if the drag obj has no
16585             // contraints, we are also done. Otherwise we need to evaluate the
16586             // location of the target as related to the actual location of the
16587             // dragged element.
16588             var dc = this.dragCurrent;
16589             if (!dc || !dc.getTargetCoord ||
16590                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16591                 return oTarget.cursorIsOver;
16592             }
16593
16594             oTarget.overlap = null;
16595
16596             // Get the current location of the drag element, this is the
16597             // location of the mouse event less the delta that represents
16598             // where the original mousedown happened on the element.  We
16599             // need to consider constraints and ticks as well.
16600             var pos = dc.getTargetCoord(pt.x, pt.y);
16601
16602             var el = dc.getDragEl();
16603             var curRegion = new Roo.lib.Region( pos.y,
16604                                                    pos.x + el.offsetWidth,
16605                                                    pos.y + el.offsetHeight,
16606                                                    pos.x );
16607
16608             var overlap = curRegion.intersect(loc);
16609
16610             if (overlap) {
16611                 oTarget.overlap = overlap;
16612                 return (intersect) ? true : oTarget.cursorIsOver;
16613             } else {
16614                 return false;
16615             }
16616         },
16617
16618         /**
16619          * unload event handler
16620          * @method _onUnload
16621          * @private
16622          * @static
16623          */
16624         _onUnload: function(e, me) {
16625             Roo.dd.DragDropMgr.unregAll();
16626         },
16627
16628         /**
16629          * Cleans up the drag and drop events and objects.
16630          * @method unregAll
16631          * @private
16632          * @static
16633          */
16634         unregAll: function() {
16635
16636             if (this.dragCurrent) {
16637                 this.stopDrag();
16638                 this.dragCurrent = null;
16639             }
16640
16641             this._execOnAll("unreg", []);
16642
16643             for (i in this.elementCache) {
16644                 delete this.elementCache[i];
16645             }
16646
16647             this.elementCache = {};
16648             this.ids = {};
16649         },
16650
16651         /**
16652          * A cache of DOM elements
16653          * @property elementCache
16654          * @private
16655          * @static
16656          */
16657         elementCache: {},
16658
16659         /**
16660          * Get the wrapper for the DOM element specified
16661          * @method getElWrapper
16662          * @param {String} id the id of the element to get
16663          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16664          * @private
16665          * @deprecated This wrapper isn't that useful
16666          * @static
16667          */
16668         getElWrapper: function(id) {
16669             var oWrapper = this.elementCache[id];
16670             if (!oWrapper || !oWrapper.el) {
16671                 oWrapper = this.elementCache[id] =
16672                     new this.ElementWrapper(Roo.getDom(id));
16673             }
16674             return oWrapper;
16675         },
16676
16677         /**
16678          * Returns the actual DOM element
16679          * @method getElement
16680          * @param {String} id the id of the elment to get
16681          * @return {Object} The element
16682          * @deprecated use Roo.getDom instead
16683          * @static
16684          */
16685         getElement: function(id) {
16686             return Roo.getDom(id);
16687         },
16688
16689         /**
16690          * Returns the style property for the DOM element (i.e.,
16691          * document.getElById(id).style)
16692          * @method getCss
16693          * @param {String} id the id of the elment to get
16694          * @return {Object} The style property of the element
16695          * @deprecated use Roo.getDom instead
16696          * @static
16697          */
16698         getCss: function(id) {
16699             var el = Roo.getDom(id);
16700             return (el) ? el.style : null;
16701         },
16702
16703         /**
16704          * Inner class for cached elements
16705          * @class DragDropMgr.ElementWrapper
16706          * @for DragDropMgr
16707          * @private
16708          * @deprecated
16709          */
16710         ElementWrapper: function(el) {
16711                 /**
16712                  * The element
16713                  * @property el
16714                  */
16715                 this.el = el || null;
16716                 /**
16717                  * The element id
16718                  * @property id
16719                  */
16720                 this.id = this.el && el.id;
16721                 /**
16722                  * A reference to the style property
16723                  * @property css
16724                  */
16725                 this.css = this.el && el.style;
16726             },
16727
16728         /**
16729          * Returns the X position of an html element
16730          * @method getPosX
16731          * @param el the element for which to get the position
16732          * @return {int} the X coordinate
16733          * @for DragDropMgr
16734          * @deprecated use Roo.lib.Dom.getX instead
16735          * @static
16736          */
16737         getPosX: function(el) {
16738             return Roo.lib.Dom.getX(el);
16739         },
16740
16741         /**
16742          * Returns the Y position of an html element
16743          * @method getPosY
16744          * @param el the element for which to get the position
16745          * @return {int} the Y coordinate
16746          * @deprecated use Roo.lib.Dom.getY instead
16747          * @static
16748          */
16749         getPosY: function(el) {
16750             return Roo.lib.Dom.getY(el);
16751         },
16752
16753         /**
16754          * Swap two nodes.  In IE, we use the native method, for others we
16755          * emulate the IE behavior
16756          * @method swapNode
16757          * @param n1 the first node to swap
16758          * @param n2 the other node to swap
16759          * @static
16760          */
16761         swapNode: function(n1, n2) {
16762             if (n1.swapNode) {
16763                 n1.swapNode(n2);
16764             } else {
16765                 var p = n2.parentNode;
16766                 var s = n2.nextSibling;
16767
16768                 if (s == n1) {
16769                     p.insertBefore(n1, n2);
16770                 } else if (n2 == n1.nextSibling) {
16771                     p.insertBefore(n2, n1);
16772                 } else {
16773                     n1.parentNode.replaceChild(n2, n1);
16774                     p.insertBefore(n1, s);
16775                 }
16776             }
16777         },
16778
16779         /**
16780          * Returns the current scroll position
16781          * @method getScroll
16782          * @private
16783          * @static
16784          */
16785         getScroll: function () {
16786             var t, l, dde=document.documentElement, db=document.body;
16787             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16788                 t = dde.scrollTop;
16789                 l = dde.scrollLeft;
16790             } else if (db) {
16791                 t = db.scrollTop;
16792                 l = db.scrollLeft;
16793             } else {
16794
16795             }
16796             return { top: t, left: l };
16797         },
16798
16799         /**
16800          * Returns the specified element style property
16801          * @method getStyle
16802          * @param {HTMLElement} el          the element
16803          * @param {string}      styleProp   the style property
16804          * @return {string} The value of the style property
16805          * @deprecated use Roo.lib.Dom.getStyle
16806          * @static
16807          */
16808         getStyle: function(el, styleProp) {
16809             return Roo.fly(el).getStyle(styleProp);
16810         },
16811
16812         /**
16813          * Gets the scrollTop
16814          * @method getScrollTop
16815          * @return {int} the document's scrollTop
16816          * @static
16817          */
16818         getScrollTop: function () { return this.getScroll().top; },
16819
16820         /**
16821          * Gets the scrollLeft
16822          * @method getScrollLeft
16823          * @return {int} the document's scrollTop
16824          * @static
16825          */
16826         getScrollLeft: function () { return this.getScroll().left; },
16827
16828         /**
16829          * Sets the x/y position of an element to the location of the
16830          * target element.
16831          * @method moveToEl
16832          * @param {HTMLElement} moveEl      The element to move
16833          * @param {HTMLElement} targetEl    The position reference element
16834          * @static
16835          */
16836         moveToEl: function (moveEl, targetEl) {
16837             var aCoord = Roo.lib.Dom.getXY(targetEl);
16838             Roo.lib.Dom.setXY(moveEl, aCoord);
16839         },
16840
16841         /**
16842          * Numeric array sort function
16843          * @method numericSort
16844          * @static
16845          */
16846         numericSort: function(a, b) { return (a - b); },
16847
16848         /**
16849          * Internal counter
16850          * @property _timeoutCount
16851          * @private
16852          * @static
16853          */
16854         _timeoutCount: 0,
16855
16856         /**
16857          * Trying to make the load order less important.  Without this we get
16858          * an error if this file is loaded before the Event Utility.
16859          * @method _addListeners
16860          * @private
16861          * @static
16862          */
16863         _addListeners: function() {
16864             var DDM = Roo.dd.DDM;
16865             if ( Roo.lib.Event && document ) {
16866                 DDM._onLoad();
16867             } else {
16868                 if (DDM._timeoutCount > 2000) {
16869                 } else {
16870                     setTimeout(DDM._addListeners, 10);
16871                     if (document && document.body) {
16872                         DDM._timeoutCount += 1;
16873                     }
16874                 }
16875             }
16876         },
16877
16878         /**
16879          * Recursively searches the immediate parent and all child nodes for
16880          * the handle element in order to determine wheter or not it was
16881          * clicked.
16882          * @method handleWasClicked
16883          * @param node the html element to inspect
16884          * @static
16885          */
16886         handleWasClicked: function(node, id) {
16887             if (this.isHandle(id, node.id)) {
16888                 return true;
16889             } else {
16890                 // check to see if this is a text node child of the one we want
16891                 var p = node.parentNode;
16892
16893                 while (p) {
16894                     if (this.isHandle(id, p.id)) {
16895                         return true;
16896                     } else {
16897                         p = p.parentNode;
16898                     }
16899                 }
16900             }
16901
16902             return false;
16903         }
16904
16905     };
16906
16907 }();
16908
16909 // shorter alias, save a few bytes
16910 Roo.dd.DDM = Roo.dd.DragDropMgr;
16911 Roo.dd.DDM._addListeners();
16912
16913 }/*
16914  * Based on:
16915  * Ext JS Library 1.1.1
16916  * Copyright(c) 2006-2007, Ext JS, LLC.
16917  *
16918  * Originally Released Under LGPL - original licence link has changed is not relivant.
16919  *
16920  * Fork - LGPL
16921  * <script type="text/javascript">
16922  */
16923
16924 /**
16925  * @class Roo.dd.DD
16926  * A DragDrop implementation where the linked element follows the
16927  * mouse cursor during a drag.
16928  * @extends Roo.dd.DragDrop
16929  * @constructor
16930  * @param {String} id the id of the linked element
16931  * @param {String} sGroup the group of related DragDrop items
16932  * @param {object} config an object containing configurable attributes
16933  *                Valid properties for DD:
16934  *                    scroll
16935  */
16936 Roo.dd.DD = function(id, sGroup, config) {
16937     if (id) {
16938         this.init(id, sGroup, config);
16939     }
16940 };
16941
16942 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16943
16944     /**
16945      * When set to true, the utility automatically tries to scroll the browser
16946      * window wehn a drag and drop element is dragged near the viewport boundary.
16947      * Defaults to true.
16948      * @property scroll
16949      * @type boolean
16950      */
16951     scroll: true,
16952
16953     /**
16954      * Sets the pointer offset to the distance between the linked element's top
16955      * left corner and the location the element was clicked
16956      * @method autoOffset
16957      * @param {int} iPageX the X coordinate of the click
16958      * @param {int} iPageY the Y coordinate of the click
16959      */
16960     autoOffset: function(iPageX, iPageY) {
16961         var x = iPageX - this.startPageX;
16962         var y = iPageY - this.startPageY;
16963         this.setDelta(x, y);
16964     },
16965
16966     /**
16967      * Sets the pointer offset.  You can call this directly to force the
16968      * offset to be in a particular location (e.g., pass in 0,0 to set it
16969      * to the center of the object)
16970      * @method setDelta
16971      * @param {int} iDeltaX the distance from the left
16972      * @param {int} iDeltaY the distance from the top
16973      */
16974     setDelta: function(iDeltaX, iDeltaY) {
16975         this.deltaX = iDeltaX;
16976         this.deltaY = iDeltaY;
16977     },
16978
16979     /**
16980      * Sets the drag element to the location of the mousedown or click event,
16981      * maintaining the cursor location relative to the location on the element
16982      * that was clicked.  Override this if you want to place the element in a
16983      * location other than where the cursor is.
16984      * @method setDragElPos
16985      * @param {int} iPageX the X coordinate of the mousedown or drag event
16986      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16987      */
16988     setDragElPos: function(iPageX, iPageY) {
16989         // the first time we do this, we are going to check to make sure
16990         // the element has css positioning
16991
16992         var el = this.getDragEl();
16993         this.alignElWithMouse(el, iPageX, iPageY);
16994     },
16995
16996     /**
16997      * Sets the element to the location of the mousedown or click event,
16998      * maintaining the cursor location relative to the location on the element
16999      * that was clicked.  Override this if you want to place the element in a
17000      * location other than where the cursor is.
17001      * @method alignElWithMouse
17002      * @param {HTMLElement} el the element to move
17003      * @param {int} iPageX the X coordinate of the mousedown or drag event
17004      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17005      */
17006     alignElWithMouse: function(el, iPageX, iPageY) {
17007         var oCoord = this.getTargetCoord(iPageX, iPageY);
17008         var fly = el.dom ? el : Roo.fly(el);
17009         if (!this.deltaSetXY) {
17010             var aCoord = [oCoord.x, oCoord.y];
17011             fly.setXY(aCoord);
17012             var newLeft = fly.getLeft(true);
17013             var newTop  = fly.getTop(true);
17014             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17015         } else {
17016             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17017         }
17018
17019         this.cachePosition(oCoord.x, oCoord.y);
17020         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17021         return oCoord;
17022     },
17023
17024     /**
17025      * Saves the most recent position so that we can reset the constraints and
17026      * tick marks on-demand.  We need to know this so that we can calculate the
17027      * number of pixels the element is offset from its original position.
17028      * @method cachePosition
17029      * @param iPageX the current x position (optional, this just makes it so we
17030      * don't have to look it up again)
17031      * @param iPageY the current y position (optional, this just makes it so we
17032      * don't have to look it up again)
17033      */
17034     cachePosition: function(iPageX, iPageY) {
17035         if (iPageX) {
17036             this.lastPageX = iPageX;
17037             this.lastPageY = iPageY;
17038         } else {
17039             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17040             this.lastPageX = aCoord[0];
17041             this.lastPageY = aCoord[1];
17042         }
17043     },
17044
17045     /**
17046      * Auto-scroll the window if the dragged object has been moved beyond the
17047      * visible window boundary.
17048      * @method autoScroll
17049      * @param {int} x the drag element's x position
17050      * @param {int} y the drag element's y position
17051      * @param {int} h the height of the drag element
17052      * @param {int} w the width of the drag element
17053      * @private
17054      */
17055     autoScroll: function(x, y, h, w) {
17056
17057         if (this.scroll) {
17058             // The client height
17059             var clientH = Roo.lib.Dom.getViewWidth();
17060
17061             // The client width
17062             var clientW = Roo.lib.Dom.getViewHeight();
17063
17064             // The amt scrolled down
17065             var st = this.DDM.getScrollTop();
17066
17067             // The amt scrolled right
17068             var sl = this.DDM.getScrollLeft();
17069
17070             // Location of the bottom of the element
17071             var bot = h + y;
17072
17073             // Location of the right of the element
17074             var right = w + x;
17075
17076             // The distance from the cursor to the bottom of the visible area,
17077             // adjusted so that we don't scroll if the cursor is beyond the
17078             // element drag constraints
17079             var toBot = (clientH + st - y - this.deltaY);
17080
17081             // The distance from the cursor to the right of the visible area
17082             var toRight = (clientW + sl - x - this.deltaX);
17083
17084
17085             // How close to the edge the cursor must be before we scroll
17086             // var thresh = (document.all) ? 100 : 40;
17087             var thresh = 40;
17088
17089             // How many pixels to scroll per autoscroll op.  This helps to reduce
17090             // clunky scrolling. IE is more sensitive about this ... it needs this
17091             // value to be higher.
17092             var scrAmt = (document.all) ? 80 : 30;
17093
17094             // Scroll down if we are near the bottom of the visible page and the
17095             // obj extends below the crease
17096             if ( bot > clientH && toBot < thresh ) {
17097                 window.scrollTo(sl, st + scrAmt);
17098             }
17099
17100             // Scroll up if the window is scrolled down and the top of the object
17101             // goes above the top border
17102             if ( y < st && st > 0 && y - st < thresh ) {
17103                 window.scrollTo(sl, st - scrAmt);
17104             }
17105
17106             // Scroll right if the obj is beyond the right border and the cursor is
17107             // near the border.
17108             if ( right > clientW && toRight < thresh ) {
17109                 window.scrollTo(sl + scrAmt, st);
17110             }
17111
17112             // Scroll left if the window has been scrolled to the right and the obj
17113             // extends past the left border
17114             if ( x < sl && sl > 0 && x - sl < thresh ) {
17115                 window.scrollTo(sl - scrAmt, st);
17116             }
17117         }
17118     },
17119
17120     /**
17121      * Finds the location the element should be placed if we want to move
17122      * it to where the mouse location less the click offset would place us.
17123      * @method getTargetCoord
17124      * @param {int} iPageX the X coordinate of the click
17125      * @param {int} iPageY the Y coordinate of the click
17126      * @return an object that contains the coordinates (Object.x and Object.y)
17127      * @private
17128      */
17129     getTargetCoord: function(iPageX, iPageY) {
17130
17131
17132         var x = iPageX - this.deltaX;
17133         var y = iPageY - this.deltaY;
17134
17135         if (this.constrainX) {
17136             if (x < this.minX) { x = this.minX; }
17137             if (x > this.maxX) { x = this.maxX; }
17138         }
17139
17140         if (this.constrainY) {
17141             if (y < this.minY) { y = this.minY; }
17142             if (y > this.maxY) { y = this.maxY; }
17143         }
17144
17145         x = this.getTick(x, this.xTicks);
17146         y = this.getTick(y, this.yTicks);
17147
17148
17149         return {x:x, y:y};
17150     },
17151
17152     /*
17153      * Sets up config options specific to this class. Overrides
17154      * Roo.dd.DragDrop, but all versions of this method through the
17155      * inheritance chain are called
17156      */
17157     applyConfig: function() {
17158         Roo.dd.DD.superclass.applyConfig.call(this);
17159         this.scroll = (this.config.scroll !== false);
17160     },
17161
17162     /*
17163      * Event that fires prior to the onMouseDown event.  Overrides
17164      * Roo.dd.DragDrop.
17165      */
17166     b4MouseDown: function(e) {
17167         // this.resetConstraints();
17168         this.autoOffset(e.getPageX(),
17169                             e.getPageY());
17170     },
17171
17172     /*
17173      * Event that fires prior to the onDrag event.  Overrides
17174      * Roo.dd.DragDrop.
17175      */
17176     b4Drag: function(e) {
17177         this.setDragElPos(e.getPageX(),
17178                             e.getPageY());
17179     },
17180
17181     toString: function() {
17182         return ("DD " + this.id);
17183     }
17184
17185     //////////////////////////////////////////////////////////////////////////
17186     // Debugging ygDragDrop events that can be overridden
17187     //////////////////////////////////////////////////////////////////////////
17188     /*
17189     startDrag: function(x, y) {
17190     },
17191
17192     onDrag: function(e) {
17193     },
17194
17195     onDragEnter: function(e, id) {
17196     },
17197
17198     onDragOver: function(e, id) {
17199     },
17200
17201     onDragOut: function(e, id) {
17202     },
17203
17204     onDragDrop: function(e, id) {
17205     },
17206
17207     endDrag: function(e) {
17208     }
17209
17210     */
17211
17212 });/*
17213  * Based on:
17214  * Ext JS Library 1.1.1
17215  * Copyright(c) 2006-2007, Ext JS, LLC.
17216  *
17217  * Originally Released Under LGPL - original licence link has changed is not relivant.
17218  *
17219  * Fork - LGPL
17220  * <script type="text/javascript">
17221  */
17222
17223 /**
17224  * @class Roo.dd.DDProxy
17225  * A DragDrop implementation that inserts an empty, bordered div into
17226  * the document that follows the cursor during drag operations.  At the time of
17227  * the click, the frame div is resized to the dimensions of the linked html
17228  * element, and moved to the exact location of the linked element.
17229  *
17230  * References to the "frame" element refer to the single proxy element that
17231  * was created to be dragged in place of all DDProxy elements on the
17232  * page.
17233  *
17234  * @extends Roo.dd.DD
17235  * @constructor
17236  * @param {String} id the id of the linked html element
17237  * @param {String} sGroup the group of related DragDrop objects
17238  * @param {object} config an object containing configurable attributes
17239  *                Valid properties for DDProxy in addition to those in DragDrop:
17240  *                   resizeFrame, centerFrame, dragElId
17241  */
17242 Roo.dd.DDProxy = function(id, sGroup, config) {
17243     if (id) {
17244         this.init(id, sGroup, config);
17245         this.initFrame();
17246     }
17247 };
17248
17249 /**
17250  * The default drag frame div id
17251  * @property Roo.dd.DDProxy.dragElId
17252  * @type String
17253  * @static
17254  */
17255 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17256
17257 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17258
17259     /**
17260      * By default we resize the drag frame to be the same size as the element
17261      * we want to drag (this is to get the frame effect).  We can turn it off
17262      * if we want a different behavior.
17263      * @property resizeFrame
17264      * @type boolean
17265      */
17266     resizeFrame: true,
17267
17268     /**
17269      * By default the frame is positioned exactly where the drag element is, so
17270      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17271      * you do not have constraints on the obj is to have the drag frame centered
17272      * around the cursor.  Set centerFrame to true for this effect.
17273      * @property centerFrame
17274      * @type boolean
17275      */
17276     centerFrame: false,
17277
17278     /**
17279      * Creates the proxy element if it does not yet exist
17280      * @method createFrame
17281      */
17282     createFrame: function() {
17283         var self = this;
17284         var body = document.body;
17285
17286         if (!body || !body.firstChild) {
17287             setTimeout( function() { self.createFrame(); }, 50 );
17288             return;
17289         }
17290
17291         var div = this.getDragEl();
17292
17293         if (!div) {
17294             div    = document.createElement("div");
17295             div.id = this.dragElId;
17296             var s  = div.style;
17297
17298             s.position   = "absolute";
17299             s.visibility = "hidden";
17300             s.cursor     = "move";
17301             s.border     = "2px solid #aaa";
17302             s.zIndex     = 999;
17303
17304             // appendChild can blow up IE if invoked prior to the window load event
17305             // while rendering a table.  It is possible there are other scenarios
17306             // that would cause this to happen as well.
17307             body.insertBefore(div, body.firstChild);
17308         }
17309     },
17310
17311     /**
17312      * Initialization for the drag frame element.  Must be called in the
17313      * constructor of all subclasses
17314      * @method initFrame
17315      */
17316     initFrame: function() {
17317         this.createFrame();
17318     },
17319
17320     applyConfig: function() {
17321         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17322
17323         this.resizeFrame = (this.config.resizeFrame !== false);
17324         this.centerFrame = (this.config.centerFrame);
17325         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17326     },
17327
17328     /**
17329      * Resizes the drag frame to the dimensions of the clicked object, positions
17330      * it over the object, and finally displays it
17331      * @method showFrame
17332      * @param {int} iPageX X click position
17333      * @param {int} iPageY Y click position
17334      * @private
17335      */
17336     showFrame: function(iPageX, iPageY) {
17337         var el = this.getEl();
17338         var dragEl = this.getDragEl();
17339         var s = dragEl.style;
17340
17341         this._resizeProxy();
17342
17343         if (this.centerFrame) {
17344             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17345                            Math.round(parseInt(s.height, 10)/2) );
17346         }
17347
17348         this.setDragElPos(iPageX, iPageY);
17349
17350         Roo.fly(dragEl).show();
17351     },
17352
17353     /**
17354      * The proxy is automatically resized to the dimensions of the linked
17355      * element when a drag is initiated, unless resizeFrame is set to false
17356      * @method _resizeProxy
17357      * @private
17358      */
17359     _resizeProxy: function() {
17360         if (this.resizeFrame) {
17361             var el = this.getEl();
17362             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17363         }
17364     },
17365
17366     // overrides Roo.dd.DragDrop
17367     b4MouseDown: function(e) {
17368         var x = e.getPageX();
17369         var y = e.getPageY();
17370         this.autoOffset(x, y);
17371         this.setDragElPos(x, y);
17372     },
17373
17374     // overrides Roo.dd.DragDrop
17375     b4StartDrag: function(x, y) {
17376         // show the drag frame
17377         this.showFrame(x, y);
17378     },
17379
17380     // overrides Roo.dd.DragDrop
17381     b4EndDrag: function(e) {
17382         Roo.fly(this.getDragEl()).hide();
17383     },
17384
17385     // overrides Roo.dd.DragDrop
17386     // By default we try to move the element to the last location of the frame.
17387     // This is so that the default behavior mirrors that of Roo.dd.DD.
17388     endDrag: function(e) {
17389
17390         var lel = this.getEl();
17391         var del = this.getDragEl();
17392
17393         // Show the drag frame briefly so we can get its position
17394         del.style.visibility = "";
17395
17396         this.beforeMove();
17397         // Hide the linked element before the move to get around a Safari
17398         // rendering bug.
17399         lel.style.visibility = "hidden";
17400         Roo.dd.DDM.moveToEl(lel, del);
17401         del.style.visibility = "hidden";
17402         lel.style.visibility = "";
17403
17404         this.afterDrag();
17405     },
17406
17407     beforeMove : function(){
17408
17409     },
17410
17411     afterDrag : function(){
17412
17413     },
17414
17415     toString: function() {
17416         return ("DDProxy " + this.id);
17417     }
17418
17419 });
17420 /*
17421  * Based on:
17422  * Ext JS Library 1.1.1
17423  * Copyright(c) 2006-2007, Ext JS, LLC.
17424  *
17425  * Originally Released Under LGPL - original licence link has changed is not relivant.
17426  *
17427  * Fork - LGPL
17428  * <script type="text/javascript">
17429  */
17430
17431  /**
17432  * @class Roo.dd.DDTarget
17433  * A DragDrop implementation that does not move, but can be a drop
17434  * target.  You would get the same result by simply omitting implementation
17435  * for the event callbacks, but this way we reduce the processing cost of the
17436  * event listener and the callbacks.
17437  * @extends Roo.dd.DragDrop
17438  * @constructor
17439  * @param {String} id the id of the element that is a drop target
17440  * @param {String} sGroup the group of related DragDrop objects
17441  * @param {object} config an object containing configurable attributes
17442  *                 Valid properties for DDTarget in addition to those in
17443  *                 DragDrop:
17444  *                    none
17445  */
17446 Roo.dd.DDTarget = function(id, sGroup, config) {
17447     if (id) {
17448         this.initTarget(id, sGroup, config);
17449     }
17450     if (config.listeners || config.events) { 
17451        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17452             listeners : config.listeners || {}, 
17453             events : config.events || {} 
17454         });    
17455     }
17456 };
17457
17458 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17459 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17460     toString: function() {
17461         return ("DDTarget " + this.id);
17462     }
17463 });
17464 /*
17465  * Based on:
17466  * Ext JS Library 1.1.1
17467  * Copyright(c) 2006-2007, Ext JS, LLC.
17468  *
17469  * Originally Released Under LGPL - original licence link has changed is not relivant.
17470  *
17471  * Fork - LGPL
17472  * <script type="text/javascript">
17473  */
17474  
17475
17476 /**
17477  * @class Roo.dd.ScrollManager
17478  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17479  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17480  * @singleton
17481  */
17482 Roo.dd.ScrollManager = function(){
17483     var ddm = Roo.dd.DragDropMgr;
17484     var els = {};
17485     var dragEl = null;
17486     var proc = {};
17487     
17488     var onStop = function(e){
17489         dragEl = null;
17490         clearProc();
17491     };
17492     
17493     var triggerRefresh = function(){
17494         if(ddm.dragCurrent){
17495              ddm.refreshCache(ddm.dragCurrent.groups);
17496         }
17497     };
17498     
17499     var doScroll = function(){
17500         if(ddm.dragCurrent){
17501             var dds = Roo.dd.ScrollManager;
17502             if(!dds.animate){
17503                 if(proc.el.scroll(proc.dir, dds.increment)){
17504                     triggerRefresh();
17505                 }
17506             }else{
17507                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17508             }
17509         }
17510     };
17511     
17512     var clearProc = function(){
17513         if(proc.id){
17514             clearInterval(proc.id);
17515         }
17516         proc.id = 0;
17517         proc.el = null;
17518         proc.dir = "";
17519     };
17520     
17521     var startProc = function(el, dir){
17522         clearProc();
17523         proc.el = el;
17524         proc.dir = dir;
17525         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17526     };
17527     
17528     var onFire = function(e, isDrop){
17529         if(isDrop || !ddm.dragCurrent){ return; }
17530         var dds = Roo.dd.ScrollManager;
17531         if(!dragEl || dragEl != ddm.dragCurrent){
17532             dragEl = ddm.dragCurrent;
17533             // refresh regions on drag start
17534             dds.refreshCache();
17535         }
17536         
17537         var xy = Roo.lib.Event.getXY(e);
17538         var pt = new Roo.lib.Point(xy[0], xy[1]);
17539         for(var id in els){
17540             var el = els[id], r = el._region;
17541             if(r && r.contains(pt) && el.isScrollable()){
17542                 if(r.bottom - pt.y <= dds.thresh){
17543                     if(proc.el != el){
17544                         startProc(el, "down");
17545                     }
17546                     return;
17547                 }else if(r.right - pt.x <= dds.thresh){
17548                     if(proc.el != el){
17549                         startProc(el, "left");
17550                     }
17551                     return;
17552                 }else if(pt.y - r.top <= dds.thresh){
17553                     if(proc.el != el){
17554                         startProc(el, "up");
17555                     }
17556                     return;
17557                 }else if(pt.x - r.left <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "right");
17560                     }
17561                     return;
17562                 }
17563             }
17564         }
17565         clearProc();
17566     };
17567     
17568     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17569     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17570     
17571     return {
17572         /**
17573          * Registers new overflow element(s) to auto scroll
17574          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17575          */
17576         register : function(el){
17577             if(el instanceof Array){
17578                 for(var i = 0, len = el.length; i < len; i++) {
17579                         this.register(el[i]);
17580                 }
17581             }else{
17582                 el = Roo.get(el);
17583                 els[el.id] = el;
17584             }
17585         },
17586         
17587         /**
17588          * Unregisters overflow element(s) so they are no longer scrolled
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17590          */
17591         unregister : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.unregister(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 delete els[el.id];
17599             }
17600         },
17601         
17602         /**
17603          * The number of pixels from the edge of a container the pointer needs to be to 
17604          * trigger scrolling (defaults to 25)
17605          * @type Number
17606          */
17607         thresh : 25,
17608         
17609         /**
17610          * The number of pixels to scroll in each scroll increment (defaults to 50)
17611          * @type Number
17612          */
17613         increment : 100,
17614         
17615         /**
17616          * The frequency of scrolls in milliseconds (defaults to 500)
17617          * @type Number
17618          */
17619         frequency : 500,
17620         
17621         /**
17622          * True to animate the scroll (defaults to true)
17623          * @type Boolean
17624          */
17625         animate: true,
17626         
17627         /**
17628          * The animation duration in seconds - 
17629          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17630          * @type Number
17631          */
17632         animDuration: .4,
17633         
17634         /**
17635          * Manually trigger a cache refresh.
17636          */
17637         refreshCache : function(){
17638             for(var id in els){
17639                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17640                     els[id]._region = els[id].getRegion();
17641                 }
17642             }
17643         }
17644     };
17645 }();/*
17646  * Based on:
17647  * Ext JS Library 1.1.1
17648  * Copyright(c) 2006-2007, Ext JS, LLC.
17649  *
17650  * Originally Released Under LGPL - original licence link has changed is not relivant.
17651  *
17652  * Fork - LGPL
17653  * <script type="text/javascript">
17654  */
17655  
17656
17657 /**
17658  * @class Roo.dd.Registry
17659  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17660  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17661  * @singleton
17662  */
17663 Roo.dd.Registry = function(){
17664     var elements = {}; 
17665     var handles = {}; 
17666     var autoIdSeed = 0;
17667
17668     var getId = function(el, autogen){
17669         if(typeof el == "string"){
17670             return el;
17671         }
17672         var id = el.id;
17673         if(!id && autogen !== false){
17674             id = "roodd-" + (++autoIdSeed);
17675             el.id = id;
17676         }
17677         return id;
17678     };
17679     
17680     return {
17681     /**
17682      * Register a drag drop element
17683      * @param {String|HTMLElement} element The id or DOM node to register
17684      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17685      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17686      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17687      * populated in the data object (if applicable):
17688      * <pre>
17689 Value      Description<br />
17690 ---------  ------------------------------------------<br />
17691 handles    Array of DOM nodes that trigger dragging<br />
17692            for the element being registered<br />
17693 isHandle   True if the element passed in triggers<br />
17694            dragging itself, else false
17695 </pre>
17696      */
17697         register : function(el, data){
17698             data = data || {};
17699             if(typeof el == "string"){
17700                 el = document.getElementById(el);
17701             }
17702             data.ddel = el;
17703             elements[getId(el)] = data;
17704             if(data.isHandle !== false){
17705                 handles[data.ddel.id] = data;
17706             }
17707             if(data.handles){
17708                 var hs = data.handles;
17709                 for(var i = 0, len = hs.length; i < len; i++){
17710                         handles[getId(hs[i])] = data;
17711                 }
17712             }
17713         },
17714
17715     /**
17716      * Unregister a drag drop element
17717      * @param {String|HTMLElement}  element The id or DOM node to unregister
17718      */
17719         unregister : function(el){
17720             var id = getId(el, false);
17721             var data = elements[id];
17722             if(data){
17723                 delete elements[id];
17724                 if(data.handles){
17725                     var hs = data.handles;
17726                     for(var i = 0, len = hs.length; i < len; i++){
17727                         delete handles[getId(hs[i], false)];
17728                     }
17729                 }
17730             }
17731         },
17732
17733     /**
17734      * Returns the handle registered for a DOM Node by id
17735      * @param {String|HTMLElement} id The DOM node or id to look up
17736      * @return {Object} handle The custom handle data
17737      */
17738         getHandle : function(id){
17739             if(typeof id != "string"){ // must be element?
17740                 id = id.id;
17741             }
17742             return handles[id];
17743         },
17744
17745     /**
17746      * Returns the handle that is registered for the DOM node that is the target of the event
17747      * @param {Event} e The event
17748      * @return {Object} handle The custom handle data
17749      */
17750         getHandleFromEvent : function(e){
17751             var t = Roo.lib.Event.getTarget(e);
17752             return t ? handles[t.id] : null;
17753         },
17754
17755     /**
17756      * Returns a custom data object that is registered for a DOM node by id
17757      * @param {String|HTMLElement} id The DOM node or id to look up
17758      * @return {Object} data The custom data
17759      */
17760         getTarget : function(id){
17761             if(typeof id != "string"){ // must be element?
17762                 id = id.id;
17763             }
17764             return elements[id];
17765         },
17766
17767     /**
17768      * Returns a custom data object that is registered for the DOM node that is the target of the event
17769      * @param {Event} e The event
17770      * @return {Object} data The custom data
17771      */
17772         getTargetFromEvent : function(e){
17773             var t = Roo.lib.Event.getTarget(e);
17774             return t ? elements[t.id] || handles[t.id] : null;
17775         }
17776     };
17777 }();/*
17778  * Based on:
17779  * Ext JS Library 1.1.1
17780  * Copyright(c) 2006-2007, Ext JS, LLC.
17781  *
17782  * Originally Released Under LGPL - original licence link has changed is not relivant.
17783  *
17784  * Fork - LGPL
17785  * <script type="text/javascript">
17786  */
17787  
17788
17789 /**
17790  * @class Roo.dd.StatusProxy
17791  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17792  * default drag proxy used by all Roo.dd components.
17793  * @constructor
17794  * @param {Object} config
17795  */
17796 Roo.dd.StatusProxy = function(config){
17797     Roo.apply(this, config);
17798     this.id = this.id || Roo.id();
17799     this.el = new Roo.Layer({
17800         dh: {
17801             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17802                 {tag: "div", cls: "x-dd-drop-icon"},
17803                 {tag: "div", cls: "x-dd-drag-ghost"}
17804             ]
17805         }, 
17806         shadow: !config || config.shadow !== false
17807     });
17808     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17809     this.dropStatus = this.dropNotAllowed;
17810 };
17811
17812 Roo.dd.StatusProxy.prototype = {
17813     /**
17814      * @cfg {String} dropAllowed
17815      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17816      */
17817     dropAllowed : "x-dd-drop-ok",
17818     /**
17819      * @cfg {String} dropNotAllowed
17820      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17821      */
17822     dropNotAllowed : "x-dd-drop-nodrop",
17823
17824     /**
17825      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17826      * over the current target element.
17827      * @param {String} cssClass The css class for the new drop status indicator image
17828      */
17829     setStatus : function(cssClass){
17830         cssClass = cssClass || this.dropNotAllowed;
17831         if(this.dropStatus != cssClass){
17832             this.el.replaceClass(this.dropStatus, cssClass);
17833             this.dropStatus = cssClass;
17834         }
17835     },
17836
17837     /**
17838      * Resets the status indicator to the default dropNotAllowed value
17839      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17840      */
17841     reset : function(clearGhost){
17842         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17843         this.dropStatus = this.dropNotAllowed;
17844         if(clearGhost){
17845             this.ghost.update("");
17846         }
17847     },
17848
17849     /**
17850      * Updates the contents of the ghost element
17851      * @param {String} html The html that will replace the current innerHTML of the ghost element
17852      */
17853     update : function(html){
17854         if(typeof html == "string"){
17855             this.ghost.update(html);
17856         }else{
17857             this.ghost.update("");
17858             html.style.margin = "0";
17859             this.ghost.dom.appendChild(html);
17860         }
17861         // ensure float = none set?? cant remember why though.
17862         var el = this.ghost.dom.firstChild;
17863                 if(el){
17864                         Roo.fly(el).setStyle('float', 'none');
17865                 }
17866     },
17867     
17868     /**
17869      * Returns the underlying proxy {@link Roo.Layer}
17870      * @return {Roo.Layer} el
17871     */
17872     getEl : function(){
17873         return this.el;
17874     },
17875
17876     /**
17877      * Returns the ghost element
17878      * @return {Roo.Element} el
17879      */
17880     getGhost : function(){
17881         return this.ghost;
17882     },
17883
17884     /**
17885      * Hides the proxy
17886      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17887      */
17888     hide : function(clear){
17889         this.el.hide();
17890         if(clear){
17891             this.reset(true);
17892         }
17893     },
17894
17895     /**
17896      * Stops the repair animation if it's currently running
17897      */
17898     stop : function(){
17899         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17900             this.anim.stop();
17901         }
17902     },
17903
17904     /**
17905      * Displays this proxy
17906      */
17907     show : function(){
17908         this.el.show();
17909     },
17910
17911     /**
17912      * Force the Layer to sync its shadow and shim positions to the element
17913      */
17914     sync : function(){
17915         this.el.sync();
17916     },
17917
17918     /**
17919      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17920      * invalid drop operation by the item being dragged.
17921      * @param {Array} xy The XY position of the element ([x, y])
17922      * @param {Function} callback The function to call after the repair is complete
17923      * @param {Object} scope The scope in which to execute the callback
17924      */
17925     repair : function(xy, callback, scope){
17926         this.callback = callback;
17927         this.scope = scope;
17928         if(xy && this.animRepair !== false){
17929             this.el.addClass("x-dd-drag-repair");
17930             this.el.hideUnders(true);
17931             this.anim = this.el.shift({
17932                 duration: this.repairDuration || .5,
17933                 easing: 'easeOut',
17934                 xy: xy,
17935                 stopFx: true,
17936                 callback: this.afterRepair,
17937                 scope: this
17938             });
17939         }else{
17940             this.afterRepair();
17941         }
17942     },
17943
17944     // private
17945     afterRepair : function(){
17946         this.hide(true);
17947         if(typeof this.callback == "function"){
17948             this.callback.call(this.scope || this);
17949         }
17950         this.callback = null;
17951         this.scope = null;
17952     }
17953 };/*
17954  * Based on:
17955  * Ext JS Library 1.1.1
17956  * Copyright(c) 2006-2007, Ext JS, LLC.
17957  *
17958  * Originally Released Under LGPL - original licence link has changed is not relivant.
17959  *
17960  * Fork - LGPL
17961  * <script type="text/javascript">
17962  */
17963
17964 /**
17965  * @class Roo.dd.DragSource
17966  * @extends Roo.dd.DDProxy
17967  * A simple class that provides the basic implementation needed to make any element draggable.
17968  * @constructor
17969  * @param {String/HTMLElement/Element} el The container element
17970  * @param {Object} config
17971  */
17972 Roo.dd.DragSource = function(el, config){
17973     this.el = Roo.get(el);
17974     this.dragData = {};
17975     
17976     Roo.apply(this, config);
17977     
17978     if(!this.proxy){
17979         this.proxy = new Roo.dd.StatusProxy();
17980     }
17981
17982     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17983           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17984     
17985     this.dragging = false;
17986 };
17987
17988 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17989     /**
17990      * @cfg {String} dropAllowed
17991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17992      */
17993     dropAllowed : "x-dd-drop-ok",
17994     /**
17995      * @cfg {String} dropNotAllowed
17996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17997      */
17998     dropNotAllowed : "x-dd-drop-nodrop",
17999
18000     /**
18001      * Returns the data object associated with this drag source
18002      * @return {Object} data An object containing arbitrary data
18003      */
18004     getDragData : function(e){
18005         return this.dragData;
18006     },
18007
18008     // private
18009     onDragEnter : function(e, id){
18010         var target = Roo.dd.DragDropMgr.getDDById(id);
18011         this.cachedTarget = target;
18012         if(this.beforeDragEnter(target, e, id) !== false){
18013             if(target.isNotifyTarget){
18014                 var status = target.notifyEnter(this, e, this.dragData);
18015                 this.proxy.setStatus(status);
18016             }else{
18017                 this.proxy.setStatus(this.dropAllowed);
18018             }
18019             
18020             if(this.afterDragEnter){
18021                 /**
18022                  * An empty function by default, but provided so that you can perform a custom action
18023                  * when the dragged item enters the drop target by providing an implementation.
18024                  * @param {Roo.dd.DragDrop} target The drop target
18025                  * @param {Event} e The event object
18026                  * @param {String} id The id of the dragged element
18027                  * @method afterDragEnter
18028                  */
18029                 this.afterDragEnter(target, e, id);
18030             }
18031         }
18032     },
18033
18034     /**
18035      * An empty function by default, but provided so that you can perform a custom action
18036      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18037      * @param {Roo.dd.DragDrop} target The drop target
18038      * @param {Event} e The event object
18039      * @param {String} id The id of the dragged element
18040      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18041      */
18042     beforeDragEnter : function(target, e, id){
18043         return true;
18044     },
18045
18046     // private
18047     alignElWithMouse: function() {
18048         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18049         this.proxy.sync();
18050     },
18051
18052     // private
18053     onDragOver : function(e, id){
18054         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18055         if(this.beforeDragOver(target, e, id) !== false){
18056             if(target.isNotifyTarget){
18057                 var status = target.notifyOver(this, e, this.dragData);
18058                 this.proxy.setStatus(status);
18059             }
18060
18061             if(this.afterDragOver){
18062                 /**
18063                  * An empty function by default, but provided so that you can perform a custom action
18064                  * while the dragged item is over the drop target by providing an implementation.
18065                  * @param {Roo.dd.DragDrop} target The drop target
18066                  * @param {Event} e The event object
18067                  * @param {String} id The id of the dragged element
18068                  * @method afterDragOver
18069                  */
18070                 this.afterDragOver(target, e, id);
18071             }
18072         }
18073     },
18074
18075     /**
18076      * An empty function by default, but provided so that you can perform a custom action
18077      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18078      * @param {Roo.dd.DragDrop} target The drop target
18079      * @param {Event} e The event object
18080      * @param {String} id The id of the dragged element
18081      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18082      */
18083     beforeDragOver : function(target, e, id){
18084         return true;
18085     },
18086
18087     // private
18088     onDragOut : function(e, id){
18089         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18090         if(this.beforeDragOut(target, e, id) !== false){
18091             if(target.isNotifyTarget){
18092                 target.notifyOut(this, e, this.dragData);
18093             }
18094             this.proxy.reset();
18095             if(this.afterDragOut){
18096                 /**
18097                  * An empty function by default, but provided so that you can perform a custom action
18098                  * after the dragged item is dragged out of the target without dropping.
18099                  * @param {Roo.dd.DragDrop} target The drop target
18100                  * @param {Event} e The event object
18101                  * @param {String} id The id of the dragged element
18102                  * @method afterDragOut
18103                  */
18104                 this.afterDragOut(target, e, id);
18105             }
18106         }
18107         this.cachedTarget = null;
18108     },
18109
18110     /**
18111      * An empty function by default, but provided so that you can perform a custom action before the dragged
18112      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18113      * @param {Roo.dd.DragDrop} target The drop target
18114      * @param {Event} e The event object
18115      * @param {String} id The id of the dragged element
18116      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18117      */
18118     beforeDragOut : function(target, e, id){
18119         return true;
18120     },
18121     
18122     // private
18123     onDragDrop : function(e, id){
18124         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18125         if(this.beforeDragDrop(target, e, id) !== false){
18126             if(target.isNotifyTarget){
18127                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18128                     this.onValidDrop(target, e, id);
18129                 }else{
18130                     this.onInvalidDrop(target, e, id);
18131                 }
18132             }else{
18133                 this.onValidDrop(target, e, id);
18134             }
18135             
18136             if(this.afterDragDrop){
18137                 /**
18138                  * An empty function by default, but provided so that you can perform a custom action
18139                  * after a valid drag drop has occurred by providing an implementation.
18140                  * @param {Roo.dd.DragDrop} target The drop target
18141                  * @param {Event} e The event object
18142                  * @param {String} id The id of the dropped element
18143                  * @method afterDragDrop
18144                  */
18145                 this.afterDragDrop(target, e, id);
18146             }
18147         }
18148         delete this.cachedTarget;
18149     },
18150
18151     /**
18152      * An empty function by default, but provided so that you can perform a custom action before the dragged
18153      * item is dropped onto the target and optionally cancel the onDragDrop.
18154      * @param {Roo.dd.DragDrop} target The drop target
18155      * @param {Event} e The event object
18156      * @param {String} id The id of the dragged element
18157      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18158      */
18159     beforeDragDrop : function(target, e, id){
18160         return true;
18161     },
18162
18163     // private
18164     onValidDrop : function(target, e, id){
18165         this.hideProxy();
18166         if(this.afterValidDrop){
18167             /**
18168              * An empty function by default, but provided so that you can perform a custom action
18169              * after a valid drop has occurred by providing an implementation.
18170              * @param {Object} target The target DD 
18171              * @param {Event} e The event object
18172              * @param {String} id The id of the dropped element
18173              * @method afterInvalidDrop
18174              */
18175             this.afterValidDrop(target, e, id);
18176         }
18177     },
18178
18179     // private
18180     getRepairXY : function(e, data){
18181         return this.el.getXY();  
18182     },
18183
18184     // private
18185     onInvalidDrop : function(target, e, id){
18186         this.beforeInvalidDrop(target, e, id);
18187         if(this.cachedTarget){
18188             if(this.cachedTarget.isNotifyTarget){
18189                 this.cachedTarget.notifyOut(this, e, this.dragData);
18190             }
18191             this.cacheTarget = null;
18192         }
18193         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18194
18195         if(this.afterInvalidDrop){
18196             /**
18197              * An empty function by default, but provided so that you can perform a custom action
18198              * after an invalid drop has occurred by providing an implementation.
18199              * @param {Event} e The event object
18200              * @param {String} id The id of the dropped element
18201              * @method afterInvalidDrop
18202              */
18203             this.afterInvalidDrop(e, id);
18204         }
18205     },
18206
18207     // private
18208     afterRepair : function(){
18209         if(Roo.enableFx){
18210             this.el.highlight(this.hlColor || "c3daf9");
18211         }
18212         this.dragging = false;
18213     },
18214
18215     /**
18216      * An empty function by default, but provided so that you can perform a custom action after an invalid
18217      * drop has occurred.
18218      * @param {Roo.dd.DragDrop} target The drop target
18219      * @param {Event} e The event object
18220      * @param {String} id The id of the dragged element
18221      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18222      */
18223     beforeInvalidDrop : function(target, e, id){
18224         return true;
18225     },
18226
18227     // private
18228     handleMouseDown : function(e){
18229         if(this.dragging) {
18230             return;
18231         }
18232         var data = this.getDragData(e);
18233         if(data && this.onBeforeDrag(data, e) !== false){
18234             this.dragData = data;
18235             this.proxy.stop();
18236             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18237         } 
18238     },
18239
18240     /**
18241      * An empty function by default, but provided so that you can perform a custom action before the initial
18242      * drag event begins and optionally cancel it.
18243      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18244      * @param {Event} e The event object
18245      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18246      */
18247     onBeforeDrag : function(data, e){
18248         return true;
18249     },
18250
18251     /**
18252      * An empty function by default, but provided so that you can perform a custom action once the initial
18253      * drag event has begun.  The drag cannot be canceled from this function.
18254      * @param {Number} x The x position of the click on the dragged object
18255      * @param {Number} y The y position of the click on the dragged object
18256      */
18257     onStartDrag : Roo.emptyFn,
18258
18259     // private - YUI override
18260     startDrag : function(x, y){
18261         this.proxy.reset();
18262         this.dragging = true;
18263         this.proxy.update("");
18264         this.onInitDrag(x, y);
18265         this.proxy.show();
18266     },
18267
18268     // private
18269     onInitDrag : function(x, y){
18270         var clone = this.el.dom.cloneNode(true);
18271         clone.id = Roo.id(); // prevent duplicate ids
18272         this.proxy.update(clone);
18273         this.onStartDrag(x, y);
18274         return true;
18275     },
18276
18277     /**
18278      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18279      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18280      */
18281     getProxy : function(){
18282         return this.proxy;  
18283     },
18284
18285     /**
18286      * Hides the drag source's {@link Roo.dd.StatusProxy}
18287      */
18288     hideProxy : function(){
18289         this.proxy.hide();  
18290         this.proxy.reset(true);
18291         this.dragging = false;
18292     },
18293
18294     // private
18295     triggerCacheRefresh : function(){
18296         Roo.dd.DDM.refreshCache(this.groups);
18297     },
18298
18299     // private - override to prevent hiding
18300     b4EndDrag: function(e) {
18301     },
18302
18303     // private - override to prevent moving
18304     endDrag : function(e){
18305         this.onEndDrag(this.dragData, e);
18306     },
18307
18308     // private
18309     onEndDrag : function(data, e){
18310     },
18311     
18312     // private - pin to cursor
18313     autoOffset : function(x, y) {
18314         this.setDelta(-12, -20);
18315     }    
18316 });/*
18317  * Based on:
18318  * Ext JS Library 1.1.1
18319  * Copyright(c) 2006-2007, Ext JS, LLC.
18320  *
18321  * Originally Released Under LGPL - original licence link has changed is not relivant.
18322  *
18323  * Fork - LGPL
18324  * <script type="text/javascript">
18325  */
18326
18327
18328 /**
18329  * @class Roo.dd.DropTarget
18330  * @extends Roo.dd.DDTarget
18331  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18332  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18333  * @constructor
18334  * @param {String/HTMLElement/Element} el The container element
18335  * @param {Object} config
18336  */
18337 Roo.dd.DropTarget = function(el, config){
18338     this.el = Roo.get(el);
18339     
18340     var listeners = false; ;
18341     if (config && config.listeners) {
18342         listeners= config.listeners;
18343         delete config.listeners;
18344     }
18345     Roo.apply(this, config);
18346     
18347     if(this.containerScroll){
18348         Roo.dd.ScrollManager.register(this.el);
18349     }
18350     this.addEvents( {
18351          /**
18352          * @scope Roo.dd.DropTarget
18353          */
18354          
18355          /**
18356          * @event enter
18357          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18358          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18359          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18360          * 
18361          * IMPORTANT : it should set this.overClass and this.dropAllowed
18362          * 
18363          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18364          * @param {Event} e The event
18365          * @param {Object} data An object containing arbitrary data supplied by the drag source
18366          */
18367         "enter" : true,
18368         
18369          /**
18370          * @event over
18371          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18372          * This method will be called on every mouse movement while the drag source is over the drop target.
18373          * This default implementation simply returns the dropAllowed config value.
18374          * 
18375          * IMPORTANT : it should set this.dropAllowed
18376          * 
18377          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18378          * @param {Event} e The event
18379          * @param {Object} data An object containing arbitrary data supplied by the drag source
18380          
18381          */
18382         "over" : true,
18383         /**
18384          * @event out
18385          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18386          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18387          * overClass (if any) from the drop element.
18388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18389          * @param {Event} e The event
18390          * @param {Object} data An object containing arbitrary data supplied by the drag source
18391          */
18392          "out" : true,
18393          
18394         /**
18395          * @event drop
18396          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18397          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18398          * implementation that does something to process the drop event and returns true so that the drag source's
18399          * repair action does not run.
18400          * 
18401          * IMPORTANT : it should set this.success
18402          * 
18403          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18404          * @param {Event} e The event
18405          * @param {Object} data An object containing arbitrary data supplied by the drag source
18406         */
18407          "drop" : true
18408     });
18409             
18410      
18411     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18412         this.el.dom, 
18413         this.ddGroup || this.group,
18414         {
18415             isTarget: true,
18416             listeners : listeners || {} 
18417            
18418         
18419         }
18420     );
18421
18422 };
18423
18424 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18425     /**
18426      * @cfg {String} overClass
18427      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18428      */
18429      /**
18430      * @cfg {String} ddGroup
18431      * The drag drop group to handle drop events for
18432      */
18433      
18434     /**
18435      * @cfg {String} dropAllowed
18436      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18437      */
18438     dropAllowed : "x-dd-drop-ok",
18439     /**
18440      * @cfg {String} dropNotAllowed
18441      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18442      */
18443     dropNotAllowed : "x-dd-drop-nodrop",
18444     /**
18445      * @cfg {boolean} success
18446      * set this after drop listener.. 
18447      */
18448     success : false,
18449     /**
18450      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18451      * if the drop point is valid for over/enter..
18452      */
18453     valid : false,
18454     // private
18455     isTarget : true,
18456
18457     // private
18458     isNotifyTarget : true,
18459     
18460     /**
18461      * @hide
18462      */
18463     notifyEnter : function(dd, e, data)
18464     {
18465         this.valid = true;
18466         this.fireEvent('enter', dd, e, data);
18467         if(this.overClass){
18468             this.el.addClass(this.overClass);
18469         }
18470         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18471             this.valid ? this.dropAllowed : this.dropNotAllowed
18472         );
18473     },
18474
18475     /**
18476      * @hide
18477      */
18478     notifyOver : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('over', dd, e, data);
18482         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18483             this.valid ? this.dropAllowed : this.dropNotAllowed
18484         );
18485     },
18486
18487     /**
18488      * @hide
18489      */
18490     notifyOut : function(dd, e, data)
18491     {
18492         this.fireEvent('out', dd, e, data);
18493         if(this.overClass){
18494             this.el.removeClass(this.overClass);
18495         }
18496     },
18497
18498     /**
18499      * @hide
18500      */
18501     notifyDrop : function(dd, e, data)
18502     {
18503         this.success = false;
18504         this.fireEvent('drop', dd, e, data);
18505         return this.success;
18506     }
18507 });/*
18508  * Based on:
18509  * Ext JS Library 1.1.1
18510  * Copyright(c) 2006-2007, Ext JS, LLC.
18511  *
18512  * Originally Released Under LGPL - original licence link has changed is not relivant.
18513  *
18514  * Fork - LGPL
18515  * <script type="text/javascript">
18516  */
18517
18518
18519 /**
18520  * @class Roo.dd.DragZone
18521  * @extends Roo.dd.DragSource
18522  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18523  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18524  * @constructor
18525  * @param {String/HTMLElement/Element} el The container element
18526  * @param {Object} config
18527  */
18528 Roo.dd.DragZone = function(el, config){
18529     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18530     if(this.containerScroll){
18531         Roo.dd.ScrollManager.register(this.el);
18532     }
18533 };
18534
18535 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18536     /**
18537      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18538      * for auto scrolling during drag operations.
18539      */
18540     /**
18541      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18542      * method after a failed drop (defaults to "c3daf9" - light blue)
18543      */
18544
18545     /**
18546      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18547      * for a valid target to drag based on the mouse down. Override this method
18548      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18549      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18550      * @param {EventObject} e The mouse down event
18551      * @return {Object} The dragData
18552      */
18553     getDragData : function(e){
18554         return Roo.dd.Registry.getHandleFromEvent(e);
18555     },
18556     
18557     /**
18558      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18559      * this.dragData.ddel
18560      * @param {Number} x The x position of the click on the dragged object
18561      * @param {Number} y The y position of the click on the dragged object
18562      * @return {Boolean} true to continue the drag, false to cancel
18563      */
18564     onInitDrag : function(x, y){
18565         this.proxy.update(this.dragData.ddel.cloneNode(true));
18566         this.onStartDrag(x, y);
18567         return true;
18568     },
18569     
18570     /**
18571      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18572      */
18573     afterRepair : function(){
18574         if(Roo.enableFx){
18575             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18576         }
18577         this.dragging = false;
18578     },
18579
18580     /**
18581      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18582      * the XY of this.dragData.ddel
18583      * @param {EventObject} e The mouse up event
18584      * @return {Array} The xy location (e.g. [100, 200])
18585      */
18586     getRepairXY : function(e){
18587         return Roo.Element.fly(this.dragData.ddel).getXY();  
18588     }
18589 });/*
18590  * Based on:
18591  * Ext JS Library 1.1.1
18592  * Copyright(c) 2006-2007, Ext JS, LLC.
18593  *
18594  * Originally Released Under LGPL - original licence link has changed is not relivant.
18595  *
18596  * Fork - LGPL
18597  * <script type="text/javascript">
18598  */
18599 /**
18600  * @class Roo.dd.DropZone
18601  * @extends Roo.dd.DropTarget
18602  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18603  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18604  * @constructor
18605  * @param {String/HTMLElement/Element} el The container element
18606  * @param {Object} config
18607  */
18608 Roo.dd.DropZone = function(el, config){
18609     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18610 };
18611
18612 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18613     /**
18614      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18615      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18616      * provide your own custom lookup.
18617      * @param {Event} e The event
18618      * @return {Object} data The custom data
18619      */
18620     getTargetFromEvent : function(e){
18621         return Roo.dd.Registry.getTargetFromEvent(e);
18622     },
18623
18624     /**
18625      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18626      * that it has registered.  This method has no default implementation and should be overridden to provide
18627      * node-specific processing if necessary.
18628      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18629      * {@link #getTargetFromEvent} for this node)
18630      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18631      * @param {Event} e The event
18632      * @param {Object} data An object containing arbitrary data supplied by the drag source
18633      */
18634     onNodeEnter : function(n, dd, e, data){
18635         
18636     },
18637
18638     /**
18639      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18640      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18641      * overridden to provide the proper feedback.
18642      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18643      * {@link #getTargetFromEvent} for this node)
18644      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18645      * @param {Event} e The event
18646      * @param {Object} data An object containing arbitrary data supplied by the drag source
18647      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18648      * underlying {@link Roo.dd.StatusProxy} can be updated
18649      */
18650     onNodeOver : function(n, dd, e, data){
18651         return this.dropAllowed;
18652     },
18653
18654     /**
18655      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18656      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18657      * node-specific processing if necessary.
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      */
18664     onNodeOut : function(n, dd, e, data){
18665         
18666     },
18667
18668     /**
18669      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18670      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18671      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18672      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18673      * {@link #getTargetFromEvent} for this node)
18674      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18675      * @param {Event} e The event
18676      * @param {Object} data An object containing arbitrary data supplied by the drag source
18677      * @return {Boolean} True if the drop was valid, else false
18678      */
18679     onNodeDrop : function(n, dd, e, data){
18680         return false;
18681     },
18682
18683     /**
18684      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18685      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18686      * it should be overridden to provide the proper feedback if necessary.
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18691      * underlying {@link Roo.dd.StatusProxy} can be updated
18692      */
18693     onContainerOver : function(dd, e, data){
18694         return this.dropNotAllowed;
18695     },
18696
18697     /**
18698      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18699      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18700      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18701      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      * @return {Boolean} True if the drop was valid, else false
18706      */
18707     onContainerDrop : function(dd, e, data){
18708         return false;
18709     },
18710
18711     /**
18712      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18713      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18714      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18715      * you should override this method and provide a custom implementation.
18716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18717      * @param {Event} e The event
18718      * @param {Object} data An object containing arbitrary data supplied by the drag source
18719      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18720      * underlying {@link Roo.dd.StatusProxy} can be updated
18721      */
18722     notifyEnter : function(dd, e, data){
18723         return this.dropNotAllowed;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18728      * This method will be called on every mouse movement while the drag source is over the drop zone.
18729      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18730      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18731      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18732      * registered node, it will call {@link #onContainerOver}.
18733      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18734      * @param {Event} e The event
18735      * @param {Object} data An object containing arbitrary data supplied by the drag source
18736      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18737      * underlying {@link Roo.dd.StatusProxy} can be updated
18738      */
18739     notifyOver : function(dd, e, data){
18740         var n = this.getTargetFromEvent(e);
18741         if(!n){ // not over valid drop target
18742             if(this.lastOverNode){
18743                 this.onNodeOut(this.lastOverNode, dd, e, data);
18744                 this.lastOverNode = null;
18745             }
18746             return this.onContainerOver(dd, e, data);
18747         }
18748         if(this.lastOverNode != n){
18749             if(this.lastOverNode){
18750                 this.onNodeOut(this.lastOverNode, dd, e, data);
18751             }
18752             this.onNodeEnter(n, dd, e, data);
18753             this.lastOverNode = n;
18754         }
18755         return this.onNodeOver(n, dd, e, data);
18756     },
18757
18758     /**
18759      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18760      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18761      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18762      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18763      * @param {Event} e The event
18764      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18765      */
18766     notifyOut : function(dd, e, data){
18767         if(this.lastOverNode){
18768             this.onNodeOut(this.lastOverNode, dd, e, data);
18769             this.lastOverNode = null;
18770         }
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18775      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18776      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18777      * otherwise it will call {@link #onContainerDrop}.
18778      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18779      * @param {Event} e The event
18780      * @param {Object} data An object containing arbitrary data supplied by the drag source
18781      * @return {Boolean} True if the drop was valid, else false
18782      */
18783     notifyDrop : function(dd, e, data){
18784         if(this.lastOverNode){
18785             this.onNodeOut(this.lastOverNode, dd, e, data);
18786             this.lastOverNode = null;
18787         }
18788         var n = this.getTargetFromEvent(e);
18789         return n ?
18790             this.onNodeDrop(n, dd, e, data) :
18791             this.onContainerDrop(dd, e, data);
18792     },
18793
18794     // private
18795     triggerCacheRefresh : function(){
18796         Roo.dd.DDM.refreshCache(this.groups);
18797     }  
18798 });/*
18799  * Based on:
18800  * Ext JS Library 1.1.1
18801  * Copyright(c) 2006-2007, Ext JS, LLC.
18802  *
18803  * Originally Released Under LGPL - original licence link has changed is not relivant.
18804  *
18805  * Fork - LGPL
18806  * <script type="text/javascript">
18807  */
18808
18809
18810 /**
18811  * @class Roo.data.SortTypes
18812  * @singleton
18813  * Defines the default sorting (casting?) comparison functions used when sorting data.
18814  */
18815 Roo.data.SortTypes = {
18816     /**
18817      * Default sort that does nothing
18818      * @param {Mixed} s The value being converted
18819      * @return {Mixed} The comparison value
18820      */
18821     none : function(s){
18822         return s;
18823     },
18824     
18825     /**
18826      * The regular expression used to strip tags
18827      * @type {RegExp}
18828      * @property
18829      */
18830     stripTagsRE : /<\/?[^>]+>/gi,
18831     
18832     /**
18833      * Strips all HTML tags to sort on text only
18834      * @param {Mixed} s The value being converted
18835      * @return {String} The comparison value
18836      */
18837     asText : function(s){
18838         return String(s).replace(this.stripTagsRE, "");
18839     },
18840     
18841     /**
18842      * Strips all HTML tags to sort on text only - Case insensitive
18843      * @param {Mixed} s The value being converted
18844      * @return {String} The comparison value
18845      */
18846     asUCText : function(s){
18847         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18848     },
18849     
18850     /**
18851      * Case insensitive string
18852      * @param {Mixed} s The value being converted
18853      * @return {String} The comparison value
18854      */
18855     asUCString : function(s) {
18856         return String(s).toUpperCase();
18857     },
18858     
18859     /**
18860      * Date sorting
18861      * @param {Mixed} s The value being converted
18862      * @return {Number} The comparison value
18863      */
18864     asDate : function(s) {
18865         if(!s){
18866             return 0;
18867         }
18868         if(s instanceof Date){
18869             return s.getTime();
18870         }
18871         return Date.parse(String(s));
18872     },
18873     
18874     /**
18875      * Float sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Float} The comparison value
18878      */
18879     asFloat : function(s) {
18880         var val = parseFloat(String(s).replace(/,/g, ""));
18881         if(isNaN(val)) val = 0;
18882         return val;
18883     },
18884     
18885     /**
18886      * Integer sorting
18887      * @param {Mixed} s The value being converted
18888      * @return {Number} The comparison value
18889      */
18890     asInt : function(s) {
18891         var val = parseInt(String(s).replace(/,/g, ""));
18892         if(isNaN(val)) val = 0;
18893         return val;
18894     }
18895 };/*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905
18906 /**
18907 * @class Roo.data.Record
18908  * Instances of this class encapsulate both record <em>definition</em> information, and record
18909  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18910  * to access Records cached in an {@link Roo.data.Store} object.<br>
18911  * <p>
18912  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18913  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18914  * objects.<br>
18915  * <p>
18916  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18917  * @constructor
18918  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18919  * {@link #create}. The parameters are the same.
18920  * @param {Array} data An associative Array of data values keyed by the field name.
18921  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18922  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18923  * not specified an integer id is generated.
18924  */
18925 Roo.data.Record = function(data, id){
18926     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18927     this.data = data;
18928 };
18929
18930 /**
18931  * Generate a constructor for a specific record layout.
18932  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18933  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18934  * Each field definition object may contain the following properties: <ul>
18935  * <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,
18936  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18937  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18938  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18939  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18940  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18941  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18942  * this may be omitted.</p></li>
18943  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18944  * <ul><li>auto (Default, implies no conversion)</li>
18945  * <li>string</li>
18946  * <li>int</li>
18947  * <li>float</li>
18948  * <li>boolean</li>
18949  * <li>date</li></ul></p></li>
18950  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18951  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18952  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18953  * by the Reader into an object that will be stored in the Record. It is passed the
18954  * following parameters:<ul>
18955  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18956  * </ul></p></li>
18957  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18958  * </ul>
18959  * <br>usage:<br><pre><code>
18960 var TopicRecord = Roo.data.Record.create(
18961     {name: 'title', mapping: 'topic_title'},
18962     {name: 'author', mapping: 'username'},
18963     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18964     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18965     {name: 'lastPoster', mapping: 'user2'},
18966     {name: 'excerpt', mapping: 'post_text'}
18967 );
18968
18969 var myNewRecord = new TopicRecord({
18970     title: 'Do my job please',
18971     author: 'noobie',
18972     totalPosts: 1,
18973     lastPost: new Date(),
18974     lastPoster: 'Animal',
18975     excerpt: 'No way dude!'
18976 });
18977 myStore.add(myNewRecord);
18978 </code></pre>
18979  * @method create
18980  * @static
18981  */
18982 Roo.data.Record.create = function(o){
18983     var f = function(){
18984         f.superclass.constructor.apply(this, arguments);
18985     };
18986     Roo.extend(f, Roo.data.Record);
18987     var p = f.prototype;
18988     p.fields = new Roo.util.MixedCollection(false, function(field){
18989         return field.name;
18990     });
18991     for(var i = 0, len = o.length; i < len; i++){
18992         p.fields.add(new Roo.data.Field(o[i]));
18993     }
18994     f.getField = function(name){
18995         return p.fields.get(name);  
18996     };
18997     return f;
18998 };
18999
19000 Roo.data.Record.AUTO_ID = 1000;
19001 Roo.data.Record.EDIT = 'edit';
19002 Roo.data.Record.REJECT = 'reject';
19003 Roo.data.Record.COMMIT = 'commit';
19004
19005 Roo.data.Record.prototype = {
19006     /**
19007      * Readonly flag - true if this record has been modified.
19008      * @type Boolean
19009      */
19010     dirty : false,
19011     editing : false,
19012     error: null,
19013     modified: null,
19014
19015     // private
19016     join : function(store){
19017         this.store = store;
19018     },
19019
19020     /**
19021      * Set the named field to the specified value.
19022      * @param {String} name The name of the field to set.
19023      * @param {Object} value The value to set the field to.
19024      */
19025     set : function(name, value){
19026         if(this.data[name] == value){
19027             return;
19028         }
19029         this.dirty = true;
19030         if(!this.modified){
19031             this.modified = {};
19032         }
19033         if(typeof this.modified[name] == 'undefined'){
19034             this.modified[name] = this.data[name];
19035         }
19036         this.data[name] = value;
19037         if(!this.editing && this.store){
19038             this.store.afterEdit(this);
19039         }       
19040     },
19041
19042     /**
19043      * Get the value of the named field.
19044      * @param {String} name The name of the field to get the value of.
19045      * @return {Object} The value of the field.
19046      */
19047     get : function(name){
19048         return this.data[name]; 
19049     },
19050
19051     // private
19052     beginEdit : function(){
19053         this.editing = true;
19054         this.modified = {}; 
19055     },
19056
19057     // private
19058     cancelEdit : function(){
19059         this.editing = false;
19060         delete this.modified;
19061     },
19062
19063     // private
19064     endEdit : function(){
19065         this.editing = false;
19066         if(this.dirty && this.store){
19067             this.store.afterEdit(this);
19068         }
19069     },
19070
19071     /**
19072      * Usually called by the {@link Roo.data.Store} which owns the Record.
19073      * Rejects all changes made to the Record since either creation, or the last commit operation.
19074      * Modified fields are reverted to their original values.
19075      * <p>
19076      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19077      * of reject operations.
19078      */
19079     reject : function(){
19080         var m = this.modified;
19081         for(var n in m){
19082             if(typeof m[n] != "function"){
19083                 this.data[n] = m[n];
19084             }
19085         }
19086         this.dirty = false;
19087         delete this.modified;
19088         this.editing = false;
19089         if(this.store){
19090             this.store.afterReject(this);
19091         }
19092     },
19093
19094     /**
19095      * Usually called by the {@link Roo.data.Store} which owns the Record.
19096      * Commits all changes made to the Record since either creation, or the last commit operation.
19097      * <p>
19098      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19099      * of commit operations.
19100      */
19101     commit : function(){
19102         this.dirty = false;
19103         delete this.modified;
19104         this.editing = false;
19105         if(this.store){
19106             this.store.afterCommit(this);
19107         }
19108     },
19109
19110     // private
19111     hasError : function(){
19112         return this.error != null;
19113     },
19114
19115     // private
19116     clearError : function(){
19117         this.error = null;
19118     },
19119
19120     /**
19121      * Creates a copy of this record.
19122      * @param {String} id (optional) A new record id if you don't want to use this record's id
19123      * @return {Record}
19124      */
19125     copy : function(newId) {
19126         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19127     }
19128 };/*
19129  * Based on:
19130  * Ext JS Library 1.1.1
19131  * Copyright(c) 2006-2007, Ext JS, LLC.
19132  *
19133  * Originally Released Under LGPL - original licence link has changed is not relivant.
19134  *
19135  * Fork - LGPL
19136  * <script type="text/javascript">
19137  */
19138
19139
19140
19141 /**
19142  * @class Roo.data.Store
19143  * @extends Roo.util.Observable
19144  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19145  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19146  * <p>
19147  * 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
19148  * has no knowledge of the format of the data returned by the Proxy.<br>
19149  * <p>
19150  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19151  * instances from the data object. These records are cached and made available through accessor functions.
19152  * @constructor
19153  * Creates a new Store.
19154  * @param {Object} config A config object containing the objects needed for the Store to access data,
19155  * and read the data into Records.
19156  */
19157 Roo.data.Store = function(config){
19158     this.data = new Roo.util.MixedCollection(false);
19159     this.data.getKey = function(o){
19160         return o.id;
19161     };
19162     this.baseParams = {};
19163     // private
19164     this.paramNames = {
19165         "start" : "start",
19166         "limit" : "limit",
19167         "sort" : "sort",
19168         "dir" : "dir",
19169         "multisort" : "_multisort"
19170     };
19171
19172     if(config && config.data){
19173         this.inlineData = config.data;
19174         delete config.data;
19175     }
19176
19177     Roo.apply(this, config);
19178     
19179     if(this.reader){ // reader passed
19180         this.reader = Roo.factory(this.reader, Roo.data);
19181         this.reader.xmodule = this.xmodule || false;
19182         if(!this.recordType){
19183             this.recordType = this.reader.recordType;
19184         }
19185         if(this.reader.onMetaChange){
19186             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19187         }
19188     }
19189
19190     if(this.recordType){
19191         this.fields = this.recordType.prototype.fields;
19192     }
19193     this.modified = [];
19194
19195     this.addEvents({
19196         /**
19197          * @event datachanged
19198          * Fires when the data cache has changed, and a widget which is using this Store
19199          * as a Record cache should refresh its view.
19200          * @param {Store} this
19201          */
19202         datachanged : true,
19203         /**
19204          * @event metachange
19205          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19206          * @param {Store} this
19207          * @param {Object} meta The JSON metadata
19208          */
19209         metachange : true,
19210         /**
19211          * @event add
19212          * Fires when Records have been added to the Store
19213          * @param {Store} this
19214          * @param {Roo.data.Record[]} records The array of Records added
19215          * @param {Number} index The index at which the record(s) were added
19216          */
19217         add : true,
19218         /**
19219          * @event remove
19220          * Fires when a Record has been removed from the Store
19221          * @param {Store} this
19222          * @param {Roo.data.Record} record The Record that was removed
19223          * @param {Number} index The index at which the record was removed
19224          */
19225         remove : true,
19226         /**
19227          * @event update
19228          * Fires when a Record has been updated
19229          * @param {Store} this
19230          * @param {Roo.data.Record} record The Record that was updated
19231          * @param {String} operation The update operation being performed.  Value may be one of:
19232          * <pre><code>
19233  Roo.data.Record.EDIT
19234  Roo.data.Record.REJECT
19235  Roo.data.Record.COMMIT
19236          * </code></pre>
19237          */
19238         update : true,
19239         /**
19240          * @event clear
19241          * Fires when the data cache has been cleared.
19242          * @param {Store} this
19243          */
19244         clear : true,
19245         /**
19246          * @event beforeload
19247          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19248          * the load action will be canceled.
19249          * @param {Store} this
19250          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19251          */
19252         beforeload : true,
19253         /**
19254          * @event load
19255          * Fires after a new set of Records has been loaded.
19256          * @param {Store} this
19257          * @param {Roo.data.Record[]} records The Records that were loaded
19258          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19259          */
19260         load : true,
19261         /**
19262          * @event loadexception
19263          * Fires if an exception occurs in the Proxy during loading.
19264          * Called with the signature of the Proxy's "loadexception" event.
19265          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19266          * 
19267          * @param {Proxy} 
19268          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19269          * @param {Object} load options 
19270          * @param {Object} jsonData from your request (normally this contains the Exception)
19271          */
19272         loadexception : true
19273     });
19274     
19275     if(this.proxy){
19276         this.proxy = Roo.factory(this.proxy, Roo.data);
19277         this.proxy.xmodule = this.xmodule || false;
19278         this.relayEvents(this.proxy,  ["loadexception"]);
19279     }
19280     this.sortToggle = {};
19281     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19282
19283     Roo.data.Store.superclass.constructor.call(this);
19284
19285     if(this.inlineData){
19286         this.loadData(this.inlineData);
19287         delete this.inlineData;
19288     }
19289 };
19290 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19291      /**
19292     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19293     * without a remote query - used by combo/forms at present.
19294     */
19295     
19296     /**
19297     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19298     */
19299     /**
19300     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19301     */
19302     /**
19303     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19304     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19305     */
19306     /**
19307     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19308     * on any HTTP request
19309     */
19310     /**
19311     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19312     */
19313     /**
19314     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19315     */
19316     multiSort: false,
19317     /**
19318     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19319     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19320     */
19321     remoteSort : false,
19322
19323     /**
19324     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19325      * loaded or when a record is removed. (defaults to false).
19326     */
19327     pruneModifiedRecords : false,
19328
19329     // private
19330     lastOptions : null,
19331
19332     /**
19333      * Add Records to the Store and fires the add event.
19334      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19335      */
19336     add : function(records){
19337         records = [].concat(records);
19338         for(var i = 0, len = records.length; i < len; i++){
19339             records[i].join(this);
19340         }
19341         var index = this.data.length;
19342         this.data.addAll(records);
19343         this.fireEvent("add", this, records, index);
19344     },
19345
19346     /**
19347      * Remove a Record from the Store and fires the remove event.
19348      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19349      */
19350     remove : function(record){
19351         var index = this.data.indexOf(record);
19352         this.data.removeAt(index);
19353         if(this.pruneModifiedRecords){
19354             this.modified.remove(record);
19355         }
19356         this.fireEvent("remove", this, record, index);
19357     },
19358
19359     /**
19360      * Remove all Records from the Store and fires the clear event.
19361      */
19362     removeAll : function(){
19363         this.data.clear();
19364         if(this.pruneModifiedRecords){
19365             this.modified = [];
19366         }
19367         this.fireEvent("clear", this);
19368     },
19369
19370     /**
19371      * Inserts Records to the Store at the given index and fires the add event.
19372      * @param {Number} index The start index at which to insert the passed Records.
19373      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19374      */
19375     insert : function(index, records){
19376         records = [].concat(records);
19377         for(var i = 0, len = records.length; i < len; i++){
19378             this.data.insert(index, records[i]);
19379             records[i].join(this);
19380         }
19381         this.fireEvent("add", this, records, index);
19382     },
19383
19384     /**
19385      * Get the index within the cache of the passed Record.
19386      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19387      * @return {Number} The index of the passed Record. Returns -1 if not found.
19388      */
19389     indexOf : function(record){
19390         return this.data.indexOf(record);
19391     },
19392
19393     /**
19394      * Get the index within the cache of the Record with the passed id.
19395      * @param {String} id The id of the Record to find.
19396      * @return {Number} The index of the Record. Returns -1 if not found.
19397      */
19398     indexOfId : function(id){
19399         return this.data.indexOfKey(id);
19400     },
19401
19402     /**
19403      * Get the Record with the specified id.
19404      * @param {String} id The id of the Record to find.
19405      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19406      */
19407     getById : function(id){
19408         return this.data.key(id);
19409     },
19410
19411     /**
19412      * Get the Record at the specified index.
19413      * @param {Number} index The index of the Record to find.
19414      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19415      */
19416     getAt : function(index){
19417         return this.data.itemAt(index);
19418     },
19419
19420     /**
19421      * Returns a range of Records between specified indices.
19422      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19423      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19424      * @return {Roo.data.Record[]} An array of Records
19425      */
19426     getRange : function(start, end){
19427         return this.data.getRange(start, end);
19428     },
19429
19430     // private
19431     storeOptions : function(o){
19432         o = Roo.apply({}, o);
19433         delete o.callback;
19434         delete o.scope;
19435         this.lastOptions = o;
19436     },
19437
19438     /**
19439      * Loads the Record cache from the configured Proxy using the configured Reader.
19440      * <p>
19441      * If using remote paging, then the first load call must specify the <em>start</em>
19442      * and <em>limit</em> properties in the options.params property to establish the initial
19443      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19444      * <p>
19445      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19446      * and this call will return before the new data has been loaded. Perform any post-processing
19447      * in a callback function, or in a "load" event handler.</strong>
19448      * <p>
19449      * @param {Object} options An object containing properties which control loading options:<ul>
19450      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19451      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19452      * passed the following arguments:<ul>
19453      * <li>r : Roo.data.Record[]</li>
19454      * <li>options: Options object from the load call</li>
19455      * <li>success: Boolean success indicator</li></ul></li>
19456      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19457      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19458      * </ul>
19459      */
19460     load : function(options){
19461         options = options || {};
19462         if(this.fireEvent("beforeload", this, options) !== false){
19463             this.storeOptions(options);
19464             var p = Roo.apply(options.params || {}, this.baseParams);
19465             // if meta was not loaded from remote source.. try requesting it.
19466             if (!this.reader.metaFromRemote) {
19467                 p._requestMeta = 1;
19468             }
19469             if(this.sortInfo && this.remoteSort){
19470                 var pn = this.paramNames;
19471                 p[pn["sort"]] = this.sortInfo.field;
19472                 p[pn["dir"]] = this.sortInfo.direction;
19473             }
19474             if (this.multiSort) {
19475                 var pn = this.paramNames;
19476                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19477             }
19478             
19479             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19480         }
19481     },
19482
19483     /**
19484      * Reloads the Record cache from the configured Proxy using the configured Reader and
19485      * the options from the last load operation performed.
19486      * @param {Object} options (optional) An object containing properties which may override the options
19487      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19488      * the most recently used options are reused).
19489      */
19490     reload : function(options){
19491         this.load(Roo.applyIf(options||{}, this.lastOptions));
19492     },
19493
19494     // private
19495     // Called as a callback by the Reader during a load operation.
19496     loadRecords : function(o, options, success){
19497         if(!o || success === false){
19498             if(success !== false){
19499                 this.fireEvent("load", this, [], options);
19500             }
19501             if(options.callback){
19502                 options.callback.call(options.scope || this, [], options, false);
19503             }
19504             return;
19505         }
19506         // if data returned failure - throw an exception.
19507         if (o.success === false) {
19508             // show a message if no listener is registered.
19509             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19510                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19511             }
19512             // loadmask wil be hooked into this..
19513             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19514             return;
19515         }
19516         var r = o.records, t = o.totalRecords || r.length;
19517         if(!options || options.add !== true){
19518             if(this.pruneModifiedRecords){
19519                 this.modified = [];
19520             }
19521             for(var i = 0, len = r.length; i < len; i++){
19522                 r[i].join(this);
19523             }
19524             if(this.snapshot){
19525                 this.data = this.snapshot;
19526                 delete this.snapshot;
19527             }
19528             this.data.clear();
19529             this.data.addAll(r);
19530             this.totalLength = t;
19531             this.applySort();
19532             this.fireEvent("datachanged", this);
19533         }else{
19534             this.totalLength = Math.max(t, this.data.length+r.length);
19535             this.add(r);
19536         }
19537         this.fireEvent("load", this, r, options);
19538         if(options.callback){
19539             options.callback.call(options.scope || this, r, options, true);
19540         }
19541     },
19542
19543
19544     /**
19545      * Loads data from a passed data block. A Reader which understands the format of the data
19546      * must have been configured in the constructor.
19547      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19548      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19549      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19550      */
19551     loadData : function(o, append){
19552         var r = this.reader.readRecords(o);
19553         this.loadRecords(r, {add: append}, true);
19554     },
19555
19556     /**
19557      * Gets the number of cached records.
19558      * <p>
19559      * <em>If using paging, this may not be the total size of the dataset. If the data object
19560      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19561      * the data set size</em>
19562      */
19563     getCount : function(){
19564         return this.data.length || 0;
19565     },
19566
19567     /**
19568      * Gets the total number of records in the dataset as returned by the server.
19569      * <p>
19570      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19571      * the dataset size</em>
19572      */
19573     getTotalCount : function(){
19574         return this.totalLength || 0;
19575     },
19576
19577     /**
19578      * Returns the sort state of the Store as an object with two properties:
19579      * <pre><code>
19580  field {String} The name of the field by which the Records are sorted
19581  direction {String} The sort order, "ASC" or "DESC"
19582      * </code></pre>
19583      */
19584     getSortState : function(){
19585         return this.sortInfo;
19586     },
19587
19588     // private
19589     applySort : function(){
19590         if(this.sortInfo && !this.remoteSort){
19591             var s = this.sortInfo, f = s.field;
19592             var st = this.fields.get(f).sortType;
19593             var fn = function(r1, r2){
19594                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19595                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19596             };
19597             this.data.sort(s.direction, fn);
19598             if(this.snapshot && this.snapshot != this.data){
19599                 this.snapshot.sort(s.direction, fn);
19600             }
19601         }
19602     },
19603
19604     /**
19605      * Sets the default sort column and order to be used by the next load operation.
19606      * @param {String} fieldName The name of the field to sort by.
19607      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19608      */
19609     setDefaultSort : function(field, dir){
19610         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19611     },
19612
19613     /**
19614      * Sort the Records.
19615      * If remote sorting is used, the sort is performed on the server, and the cache is
19616      * reloaded. If local sorting is used, the cache is sorted internally.
19617      * @param {String} fieldName The name of the field to sort by.
19618      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19619      */
19620     sort : function(fieldName, dir){
19621         var f = this.fields.get(fieldName);
19622         if(!dir){
19623             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19624             
19625             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19626                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19627             }else{
19628                 dir = f.sortDir;
19629             }
19630         }
19631         this.sortToggle[f.name] = dir;
19632         this.sortInfo = {field: f.name, direction: dir};
19633         if(!this.remoteSort){
19634             this.applySort();
19635             this.fireEvent("datachanged", this);
19636         }else{
19637             this.load(this.lastOptions);
19638         }
19639     },
19640
19641     /**
19642      * Calls the specified function for each of the Records in the cache.
19643      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19644      * Returning <em>false</em> aborts and exits the iteration.
19645      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19646      */
19647     each : function(fn, scope){
19648         this.data.each(fn, scope);
19649     },
19650
19651     /**
19652      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19653      * (e.g., during paging).
19654      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19655      */
19656     getModifiedRecords : function(){
19657         return this.modified;
19658     },
19659
19660     // private
19661     createFilterFn : function(property, value, anyMatch){
19662         if(!value.exec){ // not a regex
19663             value = String(value);
19664             if(value.length == 0){
19665                 return false;
19666             }
19667             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19668         }
19669         return function(r){
19670             return value.test(r.data[property]);
19671         };
19672     },
19673
19674     /**
19675      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19676      * @param {String} property A field on your records
19677      * @param {Number} start The record index to start at (defaults to 0)
19678      * @param {Number} end The last record index to include (defaults to length - 1)
19679      * @return {Number} The sum
19680      */
19681     sum : function(property, start, end){
19682         var rs = this.data.items, v = 0;
19683         start = start || 0;
19684         end = (end || end === 0) ? end : rs.length-1;
19685
19686         for(var i = start; i <= end; i++){
19687             v += (rs[i].data[property] || 0);
19688         }
19689         return v;
19690     },
19691
19692     /**
19693      * Filter the records by a specified property.
19694      * @param {String} field A field on your records
19695      * @param {String/RegExp} value Either a string that the field
19696      * should start with or a RegExp to test against the field
19697      * @param {Boolean} anyMatch True to match any part not just the beginning
19698      */
19699     filter : function(property, value, anyMatch){
19700         var fn = this.createFilterFn(property, value, anyMatch);
19701         return fn ? this.filterBy(fn) : this.clearFilter();
19702     },
19703
19704     /**
19705      * Filter by a function. The specified function will be called with each
19706      * record in this data source. If the function returns true the record is included,
19707      * otherwise it is filtered.
19708      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19709      * @param {Object} scope (optional) The scope of the function (defaults to this)
19710      */
19711     filterBy : function(fn, scope){
19712         this.snapshot = this.snapshot || this.data;
19713         this.data = this.queryBy(fn, scope||this);
19714         this.fireEvent("datachanged", this);
19715     },
19716
19717     /**
19718      * Query the records by a specified property.
19719      * @param {String} field A field on your records
19720      * @param {String/RegExp} value Either a string that the field
19721      * should start with or a RegExp to test against the field
19722      * @param {Boolean} anyMatch True to match any part not just the beginning
19723      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19724      */
19725     query : function(property, value, anyMatch){
19726         var fn = this.createFilterFn(property, value, anyMatch);
19727         return fn ? this.queryBy(fn) : this.data.clone();
19728     },
19729
19730     /**
19731      * Query by a function. The specified function will be called with each
19732      * record in this data source. If the function returns true the record is included
19733      * in the results.
19734      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19735      * @param {Object} scope (optional) The scope of the function (defaults to this)
19736       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19737      **/
19738     queryBy : function(fn, scope){
19739         var data = this.snapshot || this.data;
19740         return data.filterBy(fn, scope||this);
19741     },
19742
19743     /**
19744      * Collects unique values for a particular dataIndex from this store.
19745      * @param {String} dataIndex The property to collect
19746      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19747      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19748      * @return {Array} An array of the unique values
19749      **/
19750     collect : function(dataIndex, allowNull, bypassFilter){
19751         var d = (bypassFilter === true && this.snapshot) ?
19752                 this.snapshot.items : this.data.items;
19753         var v, sv, r = [], l = {};
19754         for(var i = 0, len = d.length; i < len; i++){
19755             v = d[i].data[dataIndex];
19756             sv = String(v);
19757             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19758                 l[sv] = true;
19759                 r[r.length] = v;
19760             }
19761         }
19762         return r;
19763     },
19764
19765     /**
19766      * Revert to a view of the Record cache with no filtering applied.
19767      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19768      */
19769     clearFilter : function(suppressEvent){
19770         if(this.snapshot && this.snapshot != this.data){
19771             this.data = this.snapshot;
19772             delete this.snapshot;
19773             if(suppressEvent !== true){
19774                 this.fireEvent("datachanged", this);
19775             }
19776         }
19777     },
19778
19779     // private
19780     afterEdit : function(record){
19781         if(this.modified.indexOf(record) == -1){
19782             this.modified.push(record);
19783         }
19784         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19785     },
19786     
19787     // private
19788     afterReject : function(record){
19789         this.modified.remove(record);
19790         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19791     },
19792
19793     // private
19794     afterCommit : function(record){
19795         this.modified.remove(record);
19796         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19797     },
19798
19799     /**
19800      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19801      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19802      */
19803     commitChanges : function(){
19804         var m = this.modified.slice(0);
19805         this.modified = [];
19806         for(var i = 0, len = m.length; i < len; i++){
19807             m[i].commit();
19808         }
19809     },
19810
19811     /**
19812      * Cancel outstanding changes on all changed records.
19813      */
19814     rejectChanges : function(){
19815         var m = this.modified.slice(0);
19816         this.modified = [];
19817         for(var i = 0, len = m.length; i < len; i++){
19818             m[i].reject();
19819         }
19820     },
19821
19822     onMetaChange : function(meta, rtype, o){
19823         this.recordType = rtype;
19824         this.fields = rtype.prototype.fields;
19825         delete this.snapshot;
19826         this.sortInfo = meta.sortInfo || this.sortInfo;
19827         this.modified = [];
19828         this.fireEvent('metachange', this, this.reader.meta);
19829     }
19830 });/*
19831  * Based on:
19832  * Ext JS Library 1.1.1
19833  * Copyright(c) 2006-2007, Ext JS, LLC.
19834  *
19835  * Originally Released Under LGPL - original licence link has changed is not relivant.
19836  *
19837  * Fork - LGPL
19838  * <script type="text/javascript">
19839  */
19840
19841 /**
19842  * @class Roo.data.SimpleStore
19843  * @extends Roo.data.Store
19844  * Small helper class to make creating Stores from Array data easier.
19845  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19846  * @cfg {Array} fields An array of field definition objects, or field name strings.
19847  * @cfg {Array} data The multi-dimensional array of data
19848  * @constructor
19849  * @param {Object} config
19850  */
19851 Roo.data.SimpleStore = function(config){
19852     Roo.data.SimpleStore.superclass.constructor.call(this, {
19853         isLocal : true,
19854         reader: new Roo.data.ArrayReader({
19855                 id: config.id
19856             },
19857             Roo.data.Record.create(config.fields)
19858         ),
19859         proxy : new Roo.data.MemoryProxy(config.data)
19860     });
19861     this.load();
19862 };
19863 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19864  * Based on:
19865  * Ext JS Library 1.1.1
19866  * Copyright(c) 2006-2007, Ext JS, LLC.
19867  *
19868  * Originally Released Under LGPL - original licence link has changed is not relivant.
19869  *
19870  * Fork - LGPL
19871  * <script type="text/javascript">
19872  */
19873
19874 /**
19875 /**
19876  * @extends Roo.data.Store
19877  * @class Roo.data.JsonStore
19878  * Small helper class to make creating Stores for JSON data easier. <br/>
19879 <pre><code>
19880 var store = new Roo.data.JsonStore({
19881     url: 'get-images.php',
19882     root: 'images',
19883     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19884 });
19885 </code></pre>
19886  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19887  * JsonReader and HttpProxy (unless inline data is provided).</b>
19888  * @cfg {Array} fields An array of field definition objects, or field name strings.
19889  * @constructor
19890  * @param {Object} config
19891  */
19892 Roo.data.JsonStore = function(c){
19893     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19894         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19895         reader: new Roo.data.JsonReader(c, c.fields)
19896     }));
19897 };
19898 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19899  * Based on:
19900  * Ext JS Library 1.1.1
19901  * Copyright(c) 2006-2007, Ext JS, LLC.
19902  *
19903  * Originally Released Under LGPL - original licence link has changed is not relivant.
19904  *
19905  * Fork - LGPL
19906  * <script type="text/javascript">
19907  */
19908
19909  
19910 Roo.data.Field = function(config){
19911     if(typeof config == "string"){
19912         config = {name: config};
19913     }
19914     Roo.apply(this, config);
19915     
19916     if(!this.type){
19917         this.type = "auto";
19918     }
19919     
19920     var st = Roo.data.SortTypes;
19921     // named sortTypes are supported, here we look them up
19922     if(typeof this.sortType == "string"){
19923         this.sortType = st[this.sortType];
19924     }
19925     
19926     // set default sortType for strings and dates
19927     if(!this.sortType){
19928         switch(this.type){
19929             case "string":
19930                 this.sortType = st.asUCString;
19931                 break;
19932             case "date":
19933                 this.sortType = st.asDate;
19934                 break;
19935             default:
19936                 this.sortType = st.none;
19937         }
19938     }
19939
19940     // define once
19941     var stripRe = /[\$,%]/g;
19942
19943     // prebuilt conversion function for this field, instead of
19944     // switching every time we're reading a value
19945     if(!this.convert){
19946         var cv, dateFormat = this.dateFormat;
19947         switch(this.type){
19948             case "":
19949             case "auto":
19950             case undefined:
19951                 cv = function(v){ return v; };
19952                 break;
19953             case "string":
19954                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19955                 break;
19956             case "int":
19957                 cv = function(v){
19958                     return v !== undefined && v !== null && v !== '' ?
19959                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19960                     };
19961                 break;
19962             case "float":
19963                 cv = function(v){
19964                     return v !== undefined && v !== null && v !== '' ?
19965                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19966                     };
19967                 break;
19968             case "bool":
19969             case "boolean":
19970                 cv = function(v){ return v === true || v === "true" || v == 1; };
19971                 break;
19972             case "date":
19973                 cv = function(v){
19974                     if(!v){
19975                         return '';
19976                     }
19977                     if(v instanceof Date){
19978                         return v;
19979                     }
19980                     if(dateFormat){
19981                         if(dateFormat == "timestamp"){
19982                             return new Date(v*1000);
19983                         }
19984                         return Date.parseDate(v, dateFormat);
19985                     }
19986                     var parsed = Date.parse(v);
19987                     return parsed ? new Date(parsed) : null;
19988                 };
19989              break;
19990             
19991         }
19992         this.convert = cv;
19993     }
19994 };
19995
19996 Roo.data.Field.prototype = {
19997     dateFormat: null,
19998     defaultValue: "",
19999     mapping: null,
20000     sortType : null,
20001     sortDir : "ASC"
20002 };/*
20003  * Based on:
20004  * Ext JS Library 1.1.1
20005  * Copyright(c) 2006-2007, Ext JS, LLC.
20006  *
20007  * Originally Released Under LGPL - original licence link has changed is not relivant.
20008  *
20009  * Fork - LGPL
20010  * <script type="text/javascript">
20011  */
20012  
20013 // Base class for reading structured data from a data source.  This class is intended to be
20014 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20015
20016 /**
20017  * @class Roo.data.DataReader
20018  * Base class for reading structured data from a data source.  This class is intended to be
20019  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20020  */
20021
20022 Roo.data.DataReader = function(meta, recordType){
20023     
20024     this.meta = meta;
20025     
20026     this.recordType = recordType instanceof Array ? 
20027         Roo.data.Record.create(recordType) : recordType;
20028 };
20029
20030 Roo.data.DataReader.prototype = {
20031      /**
20032      * Create an empty record
20033      * @param {Object} data (optional) - overlay some values
20034      * @return {Roo.data.Record} record created.
20035      */
20036     newRow :  function(d) {
20037         var da =  {};
20038         this.recordType.prototype.fields.each(function(c) {
20039             switch( c.type) {
20040                 case 'int' : da[c.name] = 0; break;
20041                 case 'date' : da[c.name] = new Date(); break;
20042                 case 'float' : da[c.name] = 0.0; break;
20043                 case 'boolean' : da[c.name] = false; break;
20044                 default : da[c.name] = ""; break;
20045             }
20046             
20047         });
20048         return new this.recordType(Roo.apply(da, d));
20049     }
20050     
20051 };/*
20052  * Based on:
20053  * Ext JS Library 1.1.1
20054  * Copyright(c) 2006-2007, Ext JS, LLC.
20055  *
20056  * Originally Released Under LGPL - original licence link has changed is not relivant.
20057  *
20058  * Fork - LGPL
20059  * <script type="text/javascript">
20060  */
20061
20062 /**
20063  * @class Roo.data.DataProxy
20064  * @extends Roo.data.Observable
20065  * This class is an abstract base class for implementations which provide retrieval of
20066  * unformatted data objects.<br>
20067  * <p>
20068  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20069  * (of the appropriate type which knows how to parse the data object) to provide a block of
20070  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20071  * <p>
20072  * Custom implementations must implement the load method as described in
20073  * {@link Roo.data.HttpProxy#load}.
20074  */
20075 Roo.data.DataProxy = function(){
20076     this.addEvents({
20077         /**
20078          * @event beforeload
20079          * Fires before a network request is made to retrieve a data object.
20080          * @param {Object} This DataProxy object.
20081          * @param {Object} params The params parameter to the load function.
20082          */
20083         beforeload : true,
20084         /**
20085          * @event load
20086          * Fires before the load method's callback is called.
20087          * @param {Object} This DataProxy object.
20088          * @param {Object} o The data object.
20089          * @param {Object} arg The callback argument object passed to the load function.
20090          */
20091         load : true,
20092         /**
20093          * @event loadexception
20094          * Fires if an Exception occurs during data retrieval.
20095          * @param {Object} This DataProxy object.
20096          * @param {Object} o The data object.
20097          * @param {Object} arg The callback argument object passed to the load function.
20098          * @param {Object} e The Exception.
20099          */
20100         loadexception : true
20101     });
20102     Roo.data.DataProxy.superclass.constructor.call(this);
20103 };
20104
20105 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20106
20107     /**
20108      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20109      */
20110 /*
20111  * Based on:
20112  * Ext JS Library 1.1.1
20113  * Copyright(c) 2006-2007, Ext JS, LLC.
20114  *
20115  * Originally Released Under LGPL - original licence link has changed is not relivant.
20116  *
20117  * Fork - LGPL
20118  * <script type="text/javascript">
20119  */
20120 /**
20121  * @class Roo.data.MemoryProxy
20122  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20123  * to the Reader when its load method is called.
20124  * @constructor
20125  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20126  */
20127 Roo.data.MemoryProxy = function(data){
20128     if (data.data) {
20129         data = data.data;
20130     }
20131     Roo.data.MemoryProxy.superclass.constructor.call(this);
20132     this.data = data;
20133 };
20134
20135 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20136     /**
20137      * Load data from the requested source (in this case an in-memory
20138      * data object passed to the constructor), read the data object into
20139      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20140      * process that block using the passed callback.
20141      * @param {Object} params This parameter is not used by the MemoryProxy class.
20142      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20143      * object into a block of Roo.data.Records.
20144      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20145      * The function must be passed <ul>
20146      * <li>The Record block object</li>
20147      * <li>The "arg" argument from the load function</li>
20148      * <li>A boolean success indicator</li>
20149      * </ul>
20150      * @param {Object} scope The scope in which to call the callback
20151      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20152      */
20153     load : function(params, reader, callback, scope, arg){
20154         params = params || {};
20155         var result;
20156         try {
20157             result = reader.readRecords(this.data);
20158         }catch(e){
20159             this.fireEvent("loadexception", this, arg, null, e);
20160             callback.call(scope, null, arg, false);
20161             return;
20162         }
20163         callback.call(scope, result, arg, true);
20164     },
20165     
20166     // private
20167     update : function(params, records){
20168         
20169     }
20170 });/*
20171  * Based on:
20172  * Ext JS Library 1.1.1
20173  * Copyright(c) 2006-2007, Ext JS, LLC.
20174  *
20175  * Originally Released Under LGPL - original licence link has changed is not relivant.
20176  *
20177  * Fork - LGPL
20178  * <script type="text/javascript">
20179  */
20180 /**
20181  * @class Roo.data.HttpProxy
20182  * @extends Roo.data.DataProxy
20183  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20184  * configured to reference a certain URL.<br><br>
20185  * <p>
20186  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20187  * from which the running page was served.<br><br>
20188  * <p>
20189  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20190  * <p>
20191  * Be aware that to enable the browser to parse an XML document, the server must set
20192  * the Content-Type header in the HTTP response to "text/xml".
20193  * @constructor
20194  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20195  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20196  * will be used to make the request.
20197  */
20198 Roo.data.HttpProxy = function(conn){
20199     Roo.data.HttpProxy.superclass.constructor.call(this);
20200     // is conn a conn config or a real conn?
20201     this.conn = conn;
20202     this.useAjax = !conn || !conn.events;
20203   
20204 };
20205
20206 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20207     // thse are take from connection...
20208     
20209     /**
20210      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20211      */
20212     /**
20213      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20214      * extra parameters to each request made by this object. (defaults to undefined)
20215      */
20216     /**
20217      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20218      *  to each request made by this object. (defaults to undefined)
20219      */
20220     /**
20221      * @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)
20222      */
20223     /**
20224      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20225      */
20226      /**
20227      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20228      * @type Boolean
20229      */
20230   
20231
20232     /**
20233      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20234      * @type Boolean
20235      */
20236     /**
20237      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20238      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20239      * a finer-grained basis than the DataProxy events.
20240      */
20241     getConnection : function(){
20242         return this.useAjax ? Roo.Ajax : this.conn;
20243     },
20244
20245     /**
20246      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20247      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20248      * process that block using the passed callback.
20249      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20250      * for the request to the remote server.
20251      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20252      * object into a block of Roo.data.Records.
20253      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20254      * The function must be passed <ul>
20255      * <li>The Record block object</li>
20256      * <li>The "arg" argument from the load function</li>
20257      * <li>A boolean success indicator</li>
20258      * </ul>
20259      * @param {Object} scope The scope in which to call the callback
20260      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20261      */
20262     load : function(params, reader, callback, scope, arg){
20263         if(this.fireEvent("beforeload", this, params) !== false){
20264             var  o = {
20265                 params : params || {},
20266                 request: {
20267                     callback : callback,
20268                     scope : scope,
20269                     arg : arg
20270                 },
20271                 reader: reader,
20272                 callback : this.loadResponse,
20273                 scope: this
20274             };
20275             if(this.useAjax){
20276                 Roo.applyIf(o, this.conn);
20277                 if(this.activeRequest){
20278                     Roo.Ajax.abort(this.activeRequest);
20279                 }
20280                 this.activeRequest = Roo.Ajax.request(o);
20281             }else{
20282                 this.conn.request(o);
20283             }
20284         }else{
20285             callback.call(scope||this, null, arg, false);
20286         }
20287     },
20288
20289     // private
20290     loadResponse : function(o, success, response){
20291         delete this.activeRequest;
20292         if(!success){
20293             this.fireEvent("loadexception", this, o, response);
20294             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20295             return;
20296         }
20297         var result;
20298         try {
20299             result = o.reader.read(response);
20300         }catch(e){
20301             this.fireEvent("loadexception", this, o, response, e);
20302             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20303             return;
20304         }
20305         
20306         this.fireEvent("load", this, o, o.request.arg);
20307         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20308     },
20309
20310     // private
20311     update : function(dataSet){
20312
20313     },
20314
20315     // private
20316     updateResponse : function(dataSet){
20317
20318     }
20319 });/*
20320  * Based on:
20321  * Ext JS Library 1.1.1
20322  * Copyright(c) 2006-2007, Ext JS, LLC.
20323  *
20324  * Originally Released Under LGPL - original licence link has changed is not relivant.
20325  *
20326  * Fork - LGPL
20327  * <script type="text/javascript">
20328  */
20329
20330 /**
20331  * @class Roo.data.ScriptTagProxy
20332  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20333  * other than the originating domain of the running page.<br><br>
20334  * <p>
20335  * <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
20336  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20337  * <p>
20338  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20339  * source code that is used as the source inside a &lt;script> tag.<br><br>
20340  * <p>
20341  * In order for the browser to process the returned data, the server must wrap the data object
20342  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20343  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20344  * depending on whether the callback name was passed:
20345  * <p>
20346  * <pre><code>
20347 boolean scriptTag = false;
20348 String cb = request.getParameter("callback");
20349 if (cb != null) {
20350     scriptTag = true;
20351     response.setContentType("text/javascript");
20352 } else {
20353     response.setContentType("application/x-json");
20354 }
20355 Writer out = response.getWriter();
20356 if (scriptTag) {
20357     out.write(cb + "(");
20358 }
20359 out.print(dataBlock.toJsonString());
20360 if (scriptTag) {
20361     out.write(");");
20362 }
20363 </pre></code>
20364  *
20365  * @constructor
20366  * @param {Object} config A configuration object.
20367  */
20368 Roo.data.ScriptTagProxy = function(config){
20369     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20370     Roo.apply(this, config);
20371     this.head = document.getElementsByTagName("head")[0];
20372 };
20373
20374 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20375
20376 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20377     /**
20378      * @cfg {String} url The URL from which to request the data object.
20379      */
20380     /**
20381      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20382      */
20383     timeout : 30000,
20384     /**
20385      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20386      * the server the name of the callback function set up by the load call to process the returned data object.
20387      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20388      * javascript output which calls this named function passing the data object as its only parameter.
20389      */
20390     callbackParam : "callback",
20391     /**
20392      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20393      * name to the request.
20394      */
20395     nocache : true,
20396
20397     /**
20398      * Load data from the configured URL, read the data object into
20399      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20400      * process that block using the passed callback.
20401      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20402      * for the request to the remote server.
20403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20404      * object into a block of Roo.data.Records.
20405      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20406      * The function must be passed <ul>
20407      * <li>The Record block object</li>
20408      * <li>The "arg" argument from the load function</li>
20409      * <li>A boolean success indicator</li>
20410      * </ul>
20411      * @param {Object} scope The scope in which to call the callback
20412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20413      */
20414     load : function(params, reader, callback, scope, arg){
20415         if(this.fireEvent("beforeload", this, params) !== false){
20416
20417             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20418
20419             var url = this.url;
20420             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20421             if(this.nocache){
20422                 url += "&_dc=" + (new Date().getTime());
20423             }
20424             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20425             var trans = {
20426                 id : transId,
20427                 cb : "stcCallback"+transId,
20428                 scriptId : "stcScript"+transId,
20429                 params : params,
20430                 arg : arg,
20431                 url : url,
20432                 callback : callback,
20433                 scope : scope,
20434                 reader : reader
20435             };
20436             var conn = this;
20437
20438             window[trans.cb] = function(o){
20439                 conn.handleResponse(o, trans);
20440             };
20441
20442             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20443
20444             if(this.autoAbort !== false){
20445                 this.abort();
20446             }
20447
20448             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20449
20450             var script = document.createElement("script");
20451             script.setAttribute("src", url);
20452             script.setAttribute("type", "text/javascript");
20453             script.setAttribute("id", trans.scriptId);
20454             this.head.appendChild(script);
20455
20456             this.trans = trans;
20457         }else{
20458             callback.call(scope||this, null, arg, false);
20459         }
20460     },
20461
20462     // private
20463     isLoading : function(){
20464         return this.trans ? true : false;
20465     },
20466
20467     /**
20468      * Abort the current server request.
20469      */
20470     abort : function(){
20471         if(this.isLoading()){
20472             this.destroyTrans(this.trans);
20473         }
20474     },
20475
20476     // private
20477     destroyTrans : function(trans, isLoaded){
20478         this.head.removeChild(document.getElementById(trans.scriptId));
20479         clearTimeout(trans.timeoutId);
20480         if(isLoaded){
20481             window[trans.cb] = undefined;
20482             try{
20483                 delete window[trans.cb];
20484             }catch(e){}
20485         }else{
20486             // if hasn't been loaded, wait for load to remove it to prevent script error
20487             window[trans.cb] = function(){
20488                 window[trans.cb] = undefined;
20489                 try{
20490                     delete window[trans.cb];
20491                 }catch(e){}
20492             };
20493         }
20494     },
20495
20496     // private
20497     handleResponse : function(o, trans){
20498         this.trans = false;
20499         this.destroyTrans(trans, true);
20500         var result;
20501         try {
20502             result = trans.reader.readRecords(o);
20503         }catch(e){
20504             this.fireEvent("loadexception", this, o, trans.arg, e);
20505             trans.callback.call(trans.scope||window, null, trans.arg, false);
20506             return;
20507         }
20508         this.fireEvent("load", this, o, trans.arg);
20509         trans.callback.call(trans.scope||window, result, trans.arg, true);
20510     },
20511
20512     // private
20513     handleFailure : function(trans){
20514         this.trans = false;
20515         this.destroyTrans(trans, false);
20516         this.fireEvent("loadexception", this, null, trans.arg);
20517         trans.callback.call(trans.scope||window, null, trans.arg, false);
20518     }
20519 });/*
20520  * Based on:
20521  * Ext JS Library 1.1.1
20522  * Copyright(c) 2006-2007, Ext JS, LLC.
20523  *
20524  * Originally Released Under LGPL - original licence link has changed is not relivant.
20525  *
20526  * Fork - LGPL
20527  * <script type="text/javascript">
20528  */
20529
20530 /**
20531  * @class Roo.data.JsonReader
20532  * @extends Roo.data.DataReader
20533  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20534  * based on mappings in a provided Roo.data.Record constructor.
20535  * 
20536  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20537  * in the reply previously. 
20538  * 
20539  * <p>
20540  * Example code:
20541  * <pre><code>
20542 var RecordDef = Roo.data.Record.create([
20543     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20544     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20545 ]);
20546 var myReader = new Roo.data.JsonReader({
20547     totalProperty: "results",    // The property which contains the total dataset size (optional)
20548     root: "rows",                // The property which contains an Array of row objects
20549     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20550 }, RecordDef);
20551 </code></pre>
20552  * <p>
20553  * This would consume a JSON file like this:
20554  * <pre><code>
20555 { 'results': 2, 'rows': [
20556     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20557     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20558 }
20559 </code></pre>
20560  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20561  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20562  * paged from the remote server.
20563  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20564  * @cfg {String} root name of the property which contains the Array of row objects.
20565  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20566  * @constructor
20567  * Create a new JsonReader
20568  * @param {Object} meta Metadata configuration options
20569  * @param {Object} recordType Either an Array of field definition objects,
20570  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20571  */
20572 Roo.data.JsonReader = function(meta, recordType){
20573     
20574     meta = meta || {};
20575     // set some defaults:
20576     Roo.applyIf(meta, {
20577         totalProperty: 'total',
20578         successProperty : 'success',
20579         root : 'data',
20580         id : 'id'
20581     });
20582     
20583     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20584 };
20585 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20586     
20587     /**
20588      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20589      * Used by Store query builder to append _requestMeta to params.
20590      * 
20591      */
20592     metaFromRemote : false,
20593     /**
20594      * This method is only used by a DataProxy which has retrieved data from a remote server.
20595      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20596      * @return {Object} data A data block which is used by an Roo.data.Store object as
20597      * a cache of Roo.data.Records.
20598      */
20599     read : function(response){
20600         var json = response.responseText;
20601        
20602         var o = /* eval:var:o */ eval("("+json+")");
20603         if(!o) {
20604             throw {message: "JsonReader.read: Json object not found"};
20605         }
20606         
20607         if(o.metaData){
20608             
20609             delete this.ef;
20610             this.metaFromRemote = true;
20611             this.meta = o.metaData;
20612             this.recordType = Roo.data.Record.create(o.metaData.fields);
20613             this.onMetaChange(this.meta, this.recordType, o);
20614         }
20615         return this.readRecords(o);
20616     },
20617
20618     // private function a store will implement
20619     onMetaChange : function(meta, recordType, o){
20620
20621     },
20622
20623     /**
20624          * @ignore
20625          */
20626     simpleAccess: function(obj, subsc) {
20627         return obj[subsc];
20628     },
20629
20630         /**
20631          * @ignore
20632          */
20633     getJsonAccessor: function(){
20634         var re = /[\[\.]/;
20635         return function(expr) {
20636             try {
20637                 return(re.test(expr))
20638                     ? new Function("obj", "return obj." + expr)
20639                     : function(obj){
20640                         return obj[expr];
20641                     };
20642             } catch(e){}
20643             return Roo.emptyFn;
20644         };
20645     }(),
20646
20647     /**
20648      * Create a data block containing Roo.data.Records from an XML document.
20649      * @param {Object} o An object which contains an Array of row objects in the property specified
20650      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20651      * which contains the total size of the dataset.
20652      * @return {Object} data A data block which is used by an Roo.data.Store object as
20653      * a cache of Roo.data.Records.
20654      */
20655     readRecords : function(o){
20656         /**
20657          * After any data loads, the raw JSON data is available for further custom processing.
20658          * @type Object
20659          */
20660         this.jsonData = o;
20661         var s = this.meta, Record = this.recordType,
20662             f = Record.prototype.fields, fi = f.items, fl = f.length;
20663
20664 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20665         if (!this.ef) {
20666             if(s.totalProperty) {
20667                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20668                 }
20669                 if(s.successProperty) {
20670                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20671                 }
20672                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20673                 if (s.id) {
20674                         var g = this.getJsonAccessor(s.id);
20675                         this.getId = function(rec) {
20676                                 var r = g(rec);
20677                                 return (r === undefined || r === "") ? null : r;
20678                         };
20679                 } else {
20680                         this.getId = function(){return null;};
20681                 }
20682             this.ef = [];
20683             for(var jj = 0; jj < fl; jj++){
20684                 f = fi[jj];
20685                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20686                 this.ef[jj] = this.getJsonAccessor(map);
20687             }
20688         }
20689
20690         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20691         if(s.totalProperty){
20692             var vt = parseInt(this.getTotal(o), 10);
20693             if(!isNaN(vt)){
20694                 totalRecords = vt;
20695             }
20696         }
20697         if(s.successProperty){
20698             var vs = this.getSuccess(o);
20699             if(vs === false || vs === 'false'){
20700                 success = false;
20701             }
20702         }
20703         var records = [];
20704             for(var i = 0; i < c; i++){
20705                     var n = root[i];
20706                 var values = {};
20707                 var id = this.getId(n);
20708                 for(var j = 0; j < fl; j++){
20709                     f = fi[j];
20710                 var v = this.ef[j](n);
20711                 if (!f.convert) {
20712                     Roo.log('missing convert for ' + f.name);
20713                     Roo.log(f);
20714                     continue;
20715                 }
20716                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20717                 }
20718                 var record = new Record(values, id);
20719                 record.json = n;
20720                 records[i] = record;
20721             }
20722             return {
20723                 success : success,
20724                 records : records,
20725                 totalRecords : totalRecords
20726             };
20727     }
20728 });/*
20729  * Based on:
20730  * Ext JS Library 1.1.1
20731  * Copyright(c) 2006-2007, Ext JS, LLC.
20732  *
20733  * Originally Released Under LGPL - original licence link has changed is not relivant.
20734  *
20735  * Fork - LGPL
20736  * <script type="text/javascript">
20737  */
20738
20739 /**
20740  * @class Roo.data.XmlReader
20741  * @extends Roo.data.DataReader
20742  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20743  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20744  * <p>
20745  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20746  * header in the HTTP response must be set to "text/xml".</em>
20747  * <p>
20748  * Example code:
20749  * <pre><code>
20750 var RecordDef = Roo.data.Record.create([
20751    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20752    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20753 ]);
20754 var myReader = new Roo.data.XmlReader({
20755    totalRecords: "results", // The element which contains the total dataset size (optional)
20756    record: "row",           // The repeated element which contains row information
20757    id: "id"                 // The element within the row that provides an ID for the record (optional)
20758 }, RecordDef);
20759 </code></pre>
20760  * <p>
20761  * This would consume an XML file like this:
20762  * <pre><code>
20763 &lt;?xml?>
20764 &lt;dataset>
20765  &lt;results>2&lt;/results>
20766  &lt;row>
20767    &lt;id>1&lt;/id>
20768    &lt;name>Bill&lt;/name>
20769    &lt;occupation>Gardener&lt;/occupation>
20770  &lt;/row>
20771  &lt;row>
20772    &lt;id>2&lt;/id>
20773    &lt;name>Ben&lt;/name>
20774    &lt;occupation>Horticulturalist&lt;/occupation>
20775  &lt;/row>
20776 &lt;/dataset>
20777 </code></pre>
20778  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20779  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20780  * paged from the remote server.
20781  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20782  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20783  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20784  * a record identifier value.
20785  * @constructor
20786  * Create a new XmlReader
20787  * @param {Object} meta Metadata configuration options
20788  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20789  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20790  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20791  */
20792 Roo.data.XmlReader = function(meta, recordType){
20793     meta = meta || {};
20794     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20795 };
20796 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20797     /**
20798      * This method is only used by a DataProxy which has retrieved data from a remote server.
20799          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20800          * to contain a method called 'responseXML' that returns an XML document object.
20801      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20802      * a cache of Roo.data.Records.
20803      */
20804     read : function(response){
20805         var doc = response.responseXML;
20806         if(!doc) {
20807             throw {message: "XmlReader.read: XML Document not available"};
20808         }
20809         return this.readRecords(doc);
20810     },
20811
20812     /**
20813      * Create a data block containing Roo.data.Records from an XML document.
20814          * @param {Object} doc A parsed XML document.
20815      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20816      * a cache of Roo.data.Records.
20817      */
20818     readRecords : function(doc){
20819         /**
20820          * After any data loads/reads, the raw XML Document is available for further custom processing.
20821          * @type XMLDocument
20822          */
20823         this.xmlData = doc;
20824         var root = doc.documentElement || doc;
20825         var q = Roo.DomQuery;
20826         var recordType = this.recordType, fields = recordType.prototype.fields;
20827         var sid = this.meta.id;
20828         var totalRecords = 0, success = true;
20829         if(this.meta.totalRecords){
20830             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20831         }
20832         
20833         if(this.meta.success){
20834             var sv = q.selectValue(this.meta.success, root, true);
20835             success = sv !== false && sv !== 'false';
20836         }
20837         var records = [];
20838         var ns = q.select(this.meta.record, root);
20839         for(var i = 0, len = ns.length; i < len; i++) {
20840                 var n = ns[i];
20841                 var values = {};
20842                 var id = sid ? q.selectValue(sid, n) : undefined;
20843                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20844                     var f = fields.items[j];
20845                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20846                     v = f.convert(v);
20847                     values[f.name] = v;
20848                 }
20849                 var record = new recordType(values, id);
20850                 record.node = n;
20851                 records[records.length] = record;
20852             }
20853
20854             return {
20855                 success : success,
20856                 records : records,
20857                 totalRecords : totalRecords || records.length
20858             };
20859     }
20860 });/*
20861  * Based on:
20862  * Ext JS Library 1.1.1
20863  * Copyright(c) 2006-2007, Ext JS, LLC.
20864  *
20865  * Originally Released Under LGPL - original licence link has changed is not relivant.
20866  *
20867  * Fork - LGPL
20868  * <script type="text/javascript">
20869  */
20870
20871 /**
20872  * @class Roo.data.ArrayReader
20873  * @extends Roo.data.DataReader
20874  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20875  * Each element of that Array represents a row of data fields. The
20876  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20877  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20878  * <p>
20879  * Example code:.
20880  * <pre><code>
20881 var RecordDef = Roo.data.Record.create([
20882     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20883     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20884 ]);
20885 var myReader = new Roo.data.ArrayReader({
20886     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20887 }, RecordDef);
20888 </code></pre>
20889  * <p>
20890  * This would consume an Array like this:
20891  * <pre><code>
20892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20893   </code></pre>
20894  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20895  * @constructor
20896  * Create a new JsonReader
20897  * @param {Object} meta Metadata configuration options.
20898  * @param {Object} recordType Either an Array of field definition objects
20899  * as specified to {@link Roo.data.Record#create},
20900  * or an {@link Roo.data.Record} object
20901  * created using {@link Roo.data.Record#create}.
20902  */
20903 Roo.data.ArrayReader = function(meta, recordType){
20904     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20905 };
20906
20907 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20908     /**
20909      * Create a data block containing Roo.data.Records from an XML document.
20910      * @param {Object} o An Array of row objects which represents the dataset.
20911      * @return {Object} data A data block which is used by an Roo.data.Store object as
20912      * a cache of Roo.data.Records.
20913      */
20914     readRecords : function(o){
20915         var sid = this.meta ? this.meta.id : null;
20916         var recordType = this.recordType, fields = recordType.prototype.fields;
20917         var records = [];
20918         var root = o;
20919             for(var i = 0; i < root.length; i++){
20920                     var n = root[i];
20921                 var values = {};
20922                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20923                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20924                 var f = fields.items[j];
20925                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20926                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20927                 v = f.convert(v);
20928                 values[f.name] = v;
20929             }
20930                 var record = new recordType(values, id);
20931                 record.json = n;
20932                 records[records.length] = record;
20933             }
20934             return {
20935                 records : records,
20936                 totalRecords : records.length
20937             };
20938     }
20939 });/*
20940  * Based on:
20941  * Ext JS Library 1.1.1
20942  * Copyright(c) 2006-2007, Ext JS, LLC.
20943  *
20944  * Originally Released Under LGPL - original licence link has changed is not relivant.
20945  *
20946  * Fork - LGPL
20947  * <script type="text/javascript">
20948  */
20949
20950
20951 /**
20952  * @class Roo.data.Tree
20953  * @extends Roo.util.Observable
20954  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20955  * in the tree have most standard DOM functionality.
20956  * @constructor
20957  * @param {Node} root (optional) The root node
20958  */
20959 Roo.data.Tree = function(root){
20960    this.nodeHash = {};
20961    /**
20962     * The root node for this tree
20963     * @type Node
20964     */
20965    this.root = null;
20966    if(root){
20967        this.setRootNode(root);
20968    }
20969    this.addEvents({
20970        /**
20971         * @event append
20972         * Fires when a new child node is appended to a node in this tree.
20973         * @param {Tree} tree The owner tree
20974         * @param {Node} parent The parent node
20975         * @param {Node} node The newly appended node
20976         * @param {Number} index The index of the newly appended node
20977         */
20978        "append" : true,
20979        /**
20980         * @event remove
20981         * Fires when a child node is removed from a node in this tree.
20982         * @param {Tree} tree The owner tree
20983         * @param {Node} parent The parent node
20984         * @param {Node} node The child node removed
20985         */
20986        "remove" : true,
20987        /**
20988         * @event move
20989         * Fires when a node is moved to a new location in the tree
20990         * @param {Tree} tree The owner tree
20991         * @param {Node} node The node moved
20992         * @param {Node} oldParent The old parent of this node
20993         * @param {Node} newParent The new parent of this node
20994         * @param {Number} index The index it was moved to
20995         */
20996        "move" : true,
20997        /**
20998         * @event insert
20999         * Fires when a new child node is inserted in a node in this tree.
21000         * @param {Tree} tree The owner tree
21001         * @param {Node} parent The parent node
21002         * @param {Node} node The child node inserted
21003         * @param {Node} refNode The child node the node was inserted before
21004         */
21005        "insert" : true,
21006        /**
21007         * @event beforeappend
21008         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The child node to be appended
21012         */
21013        "beforeappend" : true,
21014        /**
21015         * @event beforeremove
21016         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21017         * @param {Tree} tree The owner tree
21018         * @param {Node} parent The parent node
21019         * @param {Node} node The child node to be removed
21020         */
21021        "beforeremove" : true,
21022        /**
21023         * @event beforemove
21024         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21025         * @param {Tree} tree The owner tree
21026         * @param {Node} node The node being moved
21027         * @param {Node} oldParent The parent of the node
21028         * @param {Node} newParent The new parent the node is moving to
21029         * @param {Number} index The index it is being moved to
21030         */
21031        "beforemove" : true,
21032        /**
21033         * @event beforeinsert
21034         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21035         * @param {Tree} tree The owner tree
21036         * @param {Node} parent The parent node
21037         * @param {Node} node The child node to be inserted
21038         * @param {Node} refNode The child node the node is being inserted before
21039         */
21040        "beforeinsert" : true
21041    });
21042
21043     Roo.data.Tree.superclass.constructor.call(this);
21044 };
21045
21046 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21047     pathSeparator: "/",
21048
21049     proxyNodeEvent : function(){
21050         return this.fireEvent.apply(this, arguments);
21051     },
21052
21053     /**
21054      * Returns the root node for this tree.
21055      * @return {Node}
21056      */
21057     getRootNode : function(){
21058         return this.root;
21059     },
21060
21061     /**
21062      * Sets the root node for this tree.
21063      * @param {Node} node
21064      * @return {Node}
21065      */
21066     setRootNode : function(node){
21067         this.root = node;
21068         node.ownerTree = this;
21069         node.isRoot = true;
21070         this.registerNode(node);
21071         return node;
21072     },
21073
21074     /**
21075      * Gets a node in this tree by its id.
21076      * @param {String} id
21077      * @return {Node}
21078      */
21079     getNodeById : function(id){
21080         return this.nodeHash[id];
21081     },
21082
21083     registerNode : function(node){
21084         this.nodeHash[node.id] = node;
21085     },
21086
21087     unregisterNode : function(node){
21088         delete this.nodeHash[node.id];
21089     },
21090
21091     toString : function(){
21092         return "[Tree"+(this.id?" "+this.id:"")+"]";
21093     }
21094 });
21095
21096 /**
21097  * @class Roo.data.Node
21098  * @extends Roo.util.Observable
21099  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21100  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21101  * @constructor
21102  * @param {Object} attributes The attributes/config for the node
21103  */
21104 Roo.data.Node = function(attributes){
21105     /**
21106      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21107      * @type {Object}
21108      */
21109     this.attributes = attributes || {};
21110     this.leaf = this.attributes.leaf;
21111     /**
21112      * The node id. @type String
21113      */
21114     this.id = this.attributes.id;
21115     if(!this.id){
21116         this.id = Roo.id(null, "ynode-");
21117         this.attributes.id = this.id;
21118     }
21119     /**
21120      * All child nodes of this node. @type Array
21121      */
21122     this.childNodes = [];
21123     if(!this.childNodes.indexOf){ // indexOf is a must
21124         this.childNodes.indexOf = function(o){
21125             for(var i = 0, len = this.length; i < len; i++){
21126                 if(this[i] == o) {
21127                     return i;
21128                 }
21129             }
21130             return -1;
21131         };
21132     }
21133     /**
21134      * The parent node for this node. @type Node
21135      */
21136     this.parentNode = null;
21137     /**
21138      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21139      */
21140     this.firstChild = null;
21141     /**
21142      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21143      */
21144     this.lastChild = null;
21145     /**
21146      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21147      */
21148     this.previousSibling = null;
21149     /**
21150      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21151      */
21152     this.nextSibling = null;
21153
21154     this.addEvents({
21155        /**
21156         * @event append
21157         * Fires when a new child node is appended
21158         * @param {Tree} tree The owner tree
21159         * @param {Node} this This node
21160         * @param {Node} node The newly appended node
21161         * @param {Number} index The index of the newly appended node
21162         */
21163        "append" : true,
21164        /**
21165         * @event remove
21166         * Fires when a child node is removed
21167         * @param {Tree} tree The owner tree
21168         * @param {Node} this This node
21169         * @param {Node} node The removed node
21170         */
21171        "remove" : true,
21172        /**
21173         * @event move
21174         * Fires when this node is moved to a new location in the tree
21175         * @param {Tree} tree The owner tree
21176         * @param {Node} this This node
21177         * @param {Node} oldParent The old parent of this node
21178         * @param {Node} newParent The new parent of this node
21179         * @param {Number} index The index it was moved to
21180         */
21181        "move" : true,
21182        /**
21183         * @event insert
21184         * Fires when a new child node is inserted.
21185         * @param {Tree} tree The owner tree
21186         * @param {Node} this This node
21187         * @param {Node} node The child node inserted
21188         * @param {Node} refNode The child node the node was inserted before
21189         */
21190        "insert" : true,
21191        /**
21192         * @event beforeappend
21193         * Fires before a new child is appended, return false to cancel the append.
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The child node to be appended
21197         */
21198        "beforeappend" : true,
21199        /**
21200         * @event beforeremove
21201         * Fires before a child is removed, return false to cancel the remove.
21202         * @param {Tree} tree The owner tree
21203         * @param {Node} this This node
21204         * @param {Node} node The child node to be removed
21205         */
21206        "beforeremove" : true,
21207        /**
21208         * @event beforemove
21209         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21210         * @param {Tree} tree The owner tree
21211         * @param {Node} this This node
21212         * @param {Node} oldParent The parent of this node
21213         * @param {Node} newParent The new parent this node is moving to
21214         * @param {Number} index The index it is being moved to
21215         */
21216        "beforemove" : true,
21217        /**
21218         * @event beforeinsert
21219         * Fires before a new child is inserted, return false to cancel the insert.
21220         * @param {Tree} tree The owner tree
21221         * @param {Node} this This node
21222         * @param {Node} node The child node to be inserted
21223         * @param {Node} refNode The child node the node is being inserted before
21224         */
21225        "beforeinsert" : true
21226    });
21227     this.listeners = this.attributes.listeners;
21228     Roo.data.Node.superclass.constructor.call(this);
21229 };
21230
21231 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21232     fireEvent : function(evtName){
21233         // first do standard event for this node
21234         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21235             return false;
21236         }
21237         // then bubble it up to the tree if the event wasn't cancelled
21238         var ot = this.getOwnerTree();
21239         if(ot){
21240             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21241                 return false;
21242             }
21243         }
21244         return true;
21245     },
21246
21247     /**
21248      * Returns true if this node is a leaf
21249      * @return {Boolean}
21250      */
21251     isLeaf : function(){
21252         return this.leaf === true;
21253     },
21254
21255     // private
21256     setFirstChild : function(node){
21257         this.firstChild = node;
21258     },
21259
21260     //private
21261     setLastChild : function(node){
21262         this.lastChild = node;
21263     },
21264
21265
21266     /**
21267      * Returns true if this node is the last child of its parent
21268      * @return {Boolean}
21269      */
21270     isLast : function(){
21271        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21272     },
21273
21274     /**
21275      * Returns true if this node is the first child of its parent
21276      * @return {Boolean}
21277      */
21278     isFirst : function(){
21279        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21280     },
21281
21282     hasChildNodes : function(){
21283         return !this.isLeaf() && this.childNodes.length > 0;
21284     },
21285
21286     /**
21287      * Insert node(s) as the last child node of this node.
21288      * @param {Node/Array} node The node or Array of nodes to append
21289      * @return {Node} The appended node if single append, or null if an array was passed
21290      */
21291     appendChild : function(node){
21292         var multi = false;
21293         if(node instanceof Array){
21294             multi = node;
21295         }else if(arguments.length > 1){
21296             multi = arguments;
21297         }
21298         // if passed an array or multiple args do them one by one
21299         if(multi){
21300             for(var i = 0, len = multi.length; i < len; i++) {
21301                 this.appendChild(multi[i]);
21302             }
21303         }else{
21304             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21305                 return false;
21306             }
21307             var index = this.childNodes.length;
21308             var oldParent = node.parentNode;
21309             // it's a move, make sure we move it cleanly
21310             if(oldParent){
21311                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21312                     return false;
21313                 }
21314                 oldParent.removeChild(node);
21315             }
21316             index = this.childNodes.length;
21317             if(index == 0){
21318                 this.setFirstChild(node);
21319             }
21320             this.childNodes.push(node);
21321             node.parentNode = this;
21322             var ps = this.childNodes[index-1];
21323             if(ps){
21324                 node.previousSibling = ps;
21325                 ps.nextSibling = node;
21326             }else{
21327                 node.previousSibling = null;
21328             }
21329             node.nextSibling = null;
21330             this.setLastChild(node);
21331             node.setOwnerTree(this.getOwnerTree());
21332             this.fireEvent("append", this.ownerTree, this, node, index);
21333             if(oldParent){
21334                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21335             }
21336             return node;
21337         }
21338     },
21339
21340     /**
21341      * Removes a child node from this node.
21342      * @param {Node} node The node to remove
21343      * @return {Node} The removed node
21344      */
21345     removeChild : function(node){
21346         var index = this.childNodes.indexOf(node);
21347         if(index == -1){
21348             return false;
21349         }
21350         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21351             return false;
21352         }
21353
21354         // remove it from childNodes collection
21355         this.childNodes.splice(index, 1);
21356
21357         // update siblings
21358         if(node.previousSibling){
21359             node.previousSibling.nextSibling = node.nextSibling;
21360         }
21361         if(node.nextSibling){
21362             node.nextSibling.previousSibling = node.previousSibling;
21363         }
21364
21365         // update child refs
21366         if(this.firstChild == node){
21367             this.setFirstChild(node.nextSibling);
21368         }
21369         if(this.lastChild == node){
21370             this.setLastChild(node.previousSibling);
21371         }
21372
21373         node.setOwnerTree(null);
21374         // clear any references from the node
21375         node.parentNode = null;
21376         node.previousSibling = null;
21377         node.nextSibling = null;
21378         this.fireEvent("remove", this.ownerTree, this, node);
21379         return node;
21380     },
21381
21382     /**
21383      * Inserts the first node before the second node in this nodes childNodes collection.
21384      * @param {Node} node The node to insert
21385      * @param {Node} refNode The node to insert before (if null the node is appended)
21386      * @return {Node} The inserted node
21387      */
21388     insertBefore : function(node, refNode){
21389         if(!refNode){ // like standard Dom, refNode can be null for append
21390             return this.appendChild(node);
21391         }
21392         // nothing to do
21393         if(node == refNode){
21394             return false;
21395         }
21396
21397         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21398             return false;
21399         }
21400         var index = this.childNodes.indexOf(refNode);
21401         var oldParent = node.parentNode;
21402         var refIndex = index;
21403
21404         // when moving internally, indexes will change after remove
21405         if(oldParent == this && this.childNodes.indexOf(node) < index){
21406             refIndex--;
21407         }
21408
21409         // it's a move, make sure we move it cleanly
21410         if(oldParent){
21411             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21412                 return false;
21413             }
21414             oldParent.removeChild(node);
21415         }
21416         if(refIndex == 0){
21417             this.setFirstChild(node);
21418         }
21419         this.childNodes.splice(refIndex, 0, node);
21420         node.parentNode = this;
21421         var ps = this.childNodes[refIndex-1];
21422         if(ps){
21423             node.previousSibling = ps;
21424             ps.nextSibling = node;
21425         }else{
21426             node.previousSibling = null;
21427         }
21428         node.nextSibling = refNode;
21429         refNode.previousSibling = node;
21430         node.setOwnerTree(this.getOwnerTree());
21431         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21432         if(oldParent){
21433             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21434         }
21435         return node;
21436     },
21437
21438     /**
21439      * Returns the child node at the specified index.
21440      * @param {Number} index
21441      * @return {Node}
21442      */
21443     item : function(index){
21444         return this.childNodes[index];
21445     },
21446
21447     /**
21448      * Replaces one child node in this node with another.
21449      * @param {Node} newChild The replacement node
21450      * @param {Node} oldChild The node to replace
21451      * @return {Node} The replaced node
21452      */
21453     replaceChild : function(newChild, oldChild){
21454         this.insertBefore(newChild, oldChild);
21455         this.removeChild(oldChild);
21456         return oldChild;
21457     },
21458
21459     /**
21460      * Returns the index of a child node
21461      * @param {Node} node
21462      * @return {Number} The index of the node or -1 if it was not found
21463      */
21464     indexOf : function(child){
21465         return this.childNodes.indexOf(child);
21466     },
21467
21468     /**
21469      * Returns the tree this node is in.
21470      * @return {Tree}
21471      */
21472     getOwnerTree : function(){
21473         // if it doesn't have one, look for one
21474         if(!this.ownerTree){
21475             var p = this;
21476             while(p){
21477                 if(p.ownerTree){
21478                     this.ownerTree = p.ownerTree;
21479                     break;
21480                 }
21481                 p = p.parentNode;
21482             }
21483         }
21484         return this.ownerTree;
21485     },
21486
21487     /**
21488      * Returns depth of this node (the root node has a depth of 0)
21489      * @return {Number}
21490      */
21491     getDepth : function(){
21492         var depth = 0;
21493         var p = this;
21494         while(p.parentNode){
21495             ++depth;
21496             p = p.parentNode;
21497         }
21498         return depth;
21499     },
21500
21501     // private
21502     setOwnerTree : function(tree){
21503         // if it's move, we need to update everyone
21504         if(tree != this.ownerTree){
21505             if(this.ownerTree){
21506                 this.ownerTree.unregisterNode(this);
21507             }
21508             this.ownerTree = tree;
21509             var cs = this.childNodes;
21510             for(var i = 0, len = cs.length; i < len; i++) {
21511                 cs[i].setOwnerTree(tree);
21512             }
21513             if(tree){
21514                 tree.registerNode(this);
21515             }
21516         }
21517     },
21518
21519     /**
21520      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21521      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21522      * @return {String} The path
21523      */
21524     getPath : function(attr){
21525         attr = attr || "id";
21526         var p = this.parentNode;
21527         var b = [this.attributes[attr]];
21528         while(p){
21529             b.unshift(p.attributes[attr]);
21530             p = p.parentNode;
21531         }
21532         var sep = this.getOwnerTree().pathSeparator;
21533         return sep + b.join(sep);
21534     },
21535
21536     /**
21537      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21538      * function call will be the scope provided or the current node. The arguments to the function
21539      * will be the args provided or the current node. If the function returns false at any point,
21540      * the bubble is stopped.
21541      * @param {Function} fn The function to call
21542      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21543      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21544      */
21545     bubble : function(fn, scope, args){
21546         var p = this;
21547         while(p){
21548             if(fn.call(scope || p, args || p) === false){
21549                 break;
21550             }
21551             p = p.parentNode;
21552         }
21553     },
21554
21555     /**
21556      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21557      * function call will be the scope provided or the current node. The arguments to the function
21558      * will be the args provided or the current node. If the function returns false at any point,
21559      * the cascade is stopped on that branch.
21560      * @param {Function} fn The function to call
21561      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21562      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21563      */
21564     cascade : function(fn, scope, args){
21565         if(fn.call(scope || this, args || this) !== false){
21566             var cs = this.childNodes;
21567             for(var i = 0, len = cs.length; i < len; i++) {
21568                 cs[i].cascade(fn, scope, args);
21569             }
21570         }
21571     },
21572
21573     /**
21574      * Interates the child nodes of 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 iteration stops.
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     eachChild : function(fn, scope, args){
21583         var cs = this.childNodes;
21584         for(var i = 0, len = cs.length; i < len; i++) {
21585                 if(fn.call(scope || this, args || cs[i]) === false){
21586                     break;
21587                 }
21588         }
21589     },
21590
21591     /**
21592      * Finds the first child that has the attribute with the specified value.
21593      * @param {String} attribute The attribute name
21594      * @param {Mixed} value The value to search for
21595      * @return {Node} The found child or null if none was found
21596      */
21597     findChild : function(attribute, value){
21598         var cs = this.childNodes;
21599         for(var i = 0, len = cs.length; i < len; i++) {
21600                 if(cs[i].attributes[attribute] == value){
21601                     return cs[i];
21602                 }
21603         }
21604         return null;
21605     },
21606
21607     /**
21608      * Finds the first child by a custom function. The child matches if the function passed
21609      * returns true.
21610      * @param {Function} fn
21611      * @param {Object} scope (optional)
21612      * @return {Node} The found child or null if none was found
21613      */
21614     findChildBy : function(fn, scope){
21615         var cs = this.childNodes;
21616         for(var i = 0, len = cs.length; i < len; i++) {
21617                 if(fn.call(scope||cs[i], cs[i]) === true){
21618                     return cs[i];
21619                 }
21620         }
21621         return null;
21622     },
21623
21624     /**
21625      * Sorts this nodes children using the supplied sort function
21626      * @param {Function} fn
21627      * @param {Object} scope (optional)
21628      */
21629     sort : function(fn, scope){
21630         var cs = this.childNodes;
21631         var len = cs.length;
21632         if(len > 0){
21633             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21634             cs.sort(sortFn);
21635             for(var i = 0; i < len; i++){
21636                 var n = cs[i];
21637                 n.previousSibling = cs[i-1];
21638                 n.nextSibling = cs[i+1];
21639                 if(i == 0){
21640                     this.setFirstChild(n);
21641                 }
21642                 if(i == len-1){
21643                     this.setLastChild(n);
21644                 }
21645             }
21646         }
21647     },
21648
21649     /**
21650      * Returns true if this node is an ancestor (at any point) of the passed node.
21651      * @param {Node} node
21652      * @return {Boolean}
21653      */
21654     contains : function(node){
21655         return node.isAncestor(this);
21656     },
21657
21658     /**
21659      * Returns true if the passed node is an ancestor (at any point) of this node.
21660      * @param {Node} node
21661      * @return {Boolean}
21662      */
21663     isAncestor : function(node){
21664         var p = this.parentNode;
21665         while(p){
21666             if(p == node){
21667                 return true;
21668             }
21669             p = p.parentNode;
21670         }
21671         return false;
21672     },
21673
21674     toString : function(){
21675         return "[Node"+(this.id?" "+this.id:"")+"]";
21676     }
21677 });/*
21678  * Based on:
21679  * Ext JS Library 1.1.1
21680  * Copyright(c) 2006-2007, Ext JS, LLC.
21681  *
21682  * Originally Released Under LGPL - original licence link has changed is not relivant.
21683  *
21684  * Fork - LGPL
21685  * <script type="text/javascript">
21686  */
21687  
21688
21689 /**
21690  * @class Roo.ComponentMgr
21691  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21692  * @singleton
21693  */
21694 Roo.ComponentMgr = function(){
21695     var all = new Roo.util.MixedCollection();
21696
21697     return {
21698         /**
21699          * Registers a component.
21700          * @param {Roo.Component} c The component
21701          */
21702         register : function(c){
21703             all.add(c);
21704         },
21705
21706         /**
21707          * Unregisters a component.
21708          * @param {Roo.Component} c The component
21709          */
21710         unregister : function(c){
21711             all.remove(c);
21712         },
21713
21714         /**
21715          * Returns a component by id
21716          * @param {String} id The component id
21717          */
21718         get : function(id){
21719             return all.get(id);
21720         },
21721
21722         /**
21723          * Registers a function that will be called when a specified component is added to ComponentMgr
21724          * @param {String} id The component id
21725          * @param {Funtction} fn The callback function
21726          * @param {Object} scope The scope of the callback
21727          */
21728         onAvailable : function(id, fn, scope){
21729             all.on("add", function(index, o){
21730                 if(o.id == id){
21731                     fn.call(scope || o, o);
21732                     all.un("add", fn, scope);
21733                 }
21734             });
21735         }
21736     };
21737 }();/*
21738  * Based on:
21739  * Ext JS Library 1.1.1
21740  * Copyright(c) 2006-2007, Ext JS, LLC.
21741  *
21742  * Originally Released Under LGPL - original licence link has changed is not relivant.
21743  *
21744  * Fork - LGPL
21745  * <script type="text/javascript">
21746  */
21747  
21748 /**
21749  * @class Roo.Component
21750  * @extends Roo.util.Observable
21751  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21752  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21753  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21754  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21755  * All visual components (widgets) that require rendering into a layout should subclass Component.
21756  * @constructor
21757  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21758  * 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
21759  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21760  */
21761 Roo.Component = function(config){
21762     config = config || {};
21763     if(config.tagName || config.dom || typeof config == "string"){ // element object
21764         config = {el: config, id: config.id || config};
21765     }
21766     this.initialConfig = config;
21767
21768     Roo.apply(this, config);
21769     this.addEvents({
21770         /**
21771          * @event disable
21772          * Fires after the component is disabled.
21773              * @param {Roo.Component} this
21774              */
21775         disable : true,
21776         /**
21777          * @event enable
21778          * Fires after the component is enabled.
21779              * @param {Roo.Component} this
21780              */
21781         enable : true,
21782         /**
21783          * @event beforeshow
21784          * Fires before the component is shown.  Return false to stop the show.
21785              * @param {Roo.Component} this
21786              */
21787         beforeshow : true,
21788         /**
21789          * @event show
21790          * Fires after the component is shown.
21791              * @param {Roo.Component} this
21792              */
21793         show : true,
21794         /**
21795          * @event beforehide
21796          * Fires before the component is hidden. Return false to stop the hide.
21797              * @param {Roo.Component} this
21798              */
21799         beforehide : true,
21800         /**
21801          * @event hide
21802          * Fires after the component is hidden.
21803              * @param {Roo.Component} this
21804              */
21805         hide : true,
21806         /**
21807          * @event beforerender
21808          * Fires before the component is rendered. Return false to stop the render.
21809              * @param {Roo.Component} this
21810              */
21811         beforerender : true,
21812         /**
21813          * @event render
21814          * Fires after the component is rendered.
21815              * @param {Roo.Component} this
21816              */
21817         render : true,
21818         /**
21819          * @event beforedestroy
21820          * Fires before the component is destroyed. Return false to stop the destroy.
21821              * @param {Roo.Component} this
21822              */
21823         beforedestroy : true,
21824         /**
21825          * @event destroy
21826          * Fires after the component is destroyed.
21827              * @param {Roo.Component} this
21828              */
21829         destroy : true
21830     });
21831     if(!this.id){
21832         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21833     }
21834     Roo.ComponentMgr.register(this);
21835     Roo.Component.superclass.constructor.call(this);
21836     this.initComponent();
21837     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21838         this.render(this.renderTo);
21839         delete this.renderTo;
21840     }
21841 };
21842
21843 /** @private */
21844 Roo.Component.AUTO_ID = 1000;
21845
21846 Roo.extend(Roo.Component, Roo.util.Observable, {
21847     /**
21848      * @scope Roo.Component.prototype
21849      * @type {Boolean}
21850      * true if this component is hidden. Read-only.
21851      */
21852     hidden : false,
21853     /**
21854      * @type {Boolean}
21855      * true if this component is disabled. Read-only.
21856      */
21857     disabled : false,
21858     /**
21859      * @type {Boolean}
21860      * true if this component has been rendered. Read-only.
21861      */
21862     rendered : false,
21863     
21864     /** @cfg {String} disableClass
21865      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21866      */
21867     disabledClass : "x-item-disabled",
21868         /** @cfg {Boolean} allowDomMove
21869          * Whether the component can move the Dom node when rendering (defaults to true).
21870          */
21871     allowDomMove : true,
21872     /** @cfg {String} hideMode
21873      * How this component should hidden. Supported values are
21874      * "visibility" (css visibility), "offsets" (negative offset position) and
21875      * "display" (css display) - defaults to "display".
21876      */
21877     hideMode: 'display',
21878
21879     /** @private */
21880     ctype : "Roo.Component",
21881
21882     /**
21883      * @cfg {String} actionMode 
21884      * which property holds the element that used for  hide() / show() / disable() / enable()
21885      * default is 'el' 
21886      */
21887     actionMode : "el",
21888
21889     /** @private */
21890     getActionEl : function(){
21891         return this[this.actionMode];
21892     },
21893
21894     initComponent : Roo.emptyFn,
21895     /**
21896      * If this is a lazy rendering component, render it to its container element.
21897      * @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.
21898      */
21899     render : function(container, position){
21900         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21901             if(!container && this.el){
21902                 this.el = Roo.get(this.el);
21903                 container = this.el.dom.parentNode;
21904                 this.allowDomMove = false;
21905             }
21906             this.container = Roo.get(container);
21907             this.rendered = true;
21908             if(position !== undefined){
21909                 if(typeof position == 'number'){
21910                     position = this.container.dom.childNodes[position];
21911                 }else{
21912                     position = Roo.getDom(position);
21913                 }
21914             }
21915             this.onRender(this.container, position || null);
21916             if(this.cls){
21917                 this.el.addClass(this.cls);
21918                 delete this.cls;
21919             }
21920             if(this.style){
21921                 this.el.applyStyles(this.style);
21922                 delete this.style;
21923             }
21924             this.fireEvent("render", this);
21925             this.afterRender(this.container);
21926             if(this.hidden){
21927                 this.hide();
21928             }
21929             if(this.disabled){
21930                 this.disable();
21931             }
21932         }
21933         return this;
21934     },
21935
21936     /** @private */
21937     // default function is not really useful
21938     onRender : function(ct, position){
21939         if(this.el){
21940             this.el = Roo.get(this.el);
21941             if(this.allowDomMove !== false){
21942                 ct.dom.insertBefore(this.el.dom, position);
21943             }
21944         }
21945     },
21946
21947     /** @private */
21948     getAutoCreate : function(){
21949         var cfg = typeof this.autoCreate == "object" ?
21950                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21951         if(this.id && !cfg.id){
21952             cfg.id = this.id;
21953         }
21954         return cfg;
21955     },
21956
21957     /** @private */
21958     afterRender : Roo.emptyFn,
21959
21960     /**
21961      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21962      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21963      */
21964     destroy : function(){
21965         if(this.fireEvent("beforedestroy", this) !== false){
21966             this.purgeListeners();
21967             this.beforeDestroy();
21968             if(this.rendered){
21969                 this.el.removeAllListeners();
21970                 this.el.remove();
21971                 if(this.actionMode == "container"){
21972                     this.container.remove();
21973                 }
21974             }
21975             this.onDestroy();
21976             Roo.ComponentMgr.unregister(this);
21977             this.fireEvent("destroy", this);
21978         }
21979     },
21980
21981         /** @private */
21982     beforeDestroy : function(){
21983
21984     },
21985
21986         /** @private */
21987         onDestroy : function(){
21988
21989     },
21990
21991     /**
21992      * Returns the underlying {@link Roo.Element}.
21993      * @return {Roo.Element} The element
21994      */
21995     getEl : function(){
21996         return this.el;
21997     },
21998
21999     /**
22000      * Returns the id of this component.
22001      * @return {String}
22002      */
22003     getId : function(){
22004         return this.id;
22005     },
22006
22007     /**
22008      * Try to focus this component.
22009      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22010      * @return {Roo.Component} this
22011      */
22012     focus : function(selectText){
22013         if(this.rendered){
22014             this.el.focus();
22015             if(selectText === true){
22016                 this.el.dom.select();
22017             }
22018         }
22019         return this;
22020     },
22021
22022     /** @private */
22023     blur : function(){
22024         if(this.rendered){
22025             this.el.blur();
22026         }
22027         return this;
22028     },
22029
22030     /**
22031      * Disable this component.
22032      * @return {Roo.Component} this
22033      */
22034     disable : function(){
22035         if(this.rendered){
22036             this.onDisable();
22037         }
22038         this.disabled = true;
22039         this.fireEvent("disable", this);
22040         return this;
22041     },
22042
22043         // private
22044     onDisable : function(){
22045         this.getActionEl().addClass(this.disabledClass);
22046         this.el.dom.disabled = true;
22047     },
22048
22049     /**
22050      * Enable this component.
22051      * @return {Roo.Component} this
22052      */
22053     enable : function(){
22054         if(this.rendered){
22055             this.onEnable();
22056         }
22057         this.disabled = false;
22058         this.fireEvent("enable", this);
22059         return this;
22060     },
22061
22062         // private
22063     onEnable : function(){
22064         this.getActionEl().removeClass(this.disabledClass);
22065         this.el.dom.disabled = false;
22066     },
22067
22068     /**
22069      * Convenience function for setting disabled/enabled by boolean.
22070      * @param {Boolean} disabled
22071      */
22072     setDisabled : function(disabled){
22073         this[disabled ? "disable" : "enable"]();
22074     },
22075
22076     /**
22077      * Show this component.
22078      * @return {Roo.Component} this
22079      */
22080     show: function(){
22081         if(this.fireEvent("beforeshow", this) !== false){
22082             this.hidden = false;
22083             if(this.rendered){
22084                 this.onShow();
22085             }
22086             this.fireEvent("show", this);
22087         }
22088         return this;
22089     },
22090
22091     // private
22092     onShow : function(){
22093         var ae = this.getActionEl();
22094         if(this.hideMode == 'visibility'){
22095             ae.dom.style.visibility = "visible";
22096         }else if(this.hideMode == 'offsets'){
22097             ae.removeClass('x-hidden');
22098         }else{
22099             ae.dom.style.display = "";
22100         }
22101     },
22102
22103     /**
22104      * Hide this component.
22105      * @return {Roo.Component} this
22106      */
22107     hide: function(){
22108         if(this.fireEvent("beforehide", this) !== false){
22109             this.hidden = true;
22110             if(this.rendered){
22111                 this.onHide();
22112             }
22113             this.fireEvent("hide", this);
22114         }
22115         return this;
22116     },
22117
22118     // private
22119     onHide : function(){
22120         var ae = this.getActionEl();
22121         if(this.hideMode == 'visibility'){
22122             ae.dom.style.visibility = "hidden";
22123         }else if(this.hideMode == 'offsets'){
22124             ae.addClass('x-hidden');
22125         }else{
22126             ae.dom.style.display = "none";
22127         }
22128     },
22129
22130     /**
22131      * Convenience function to hide or show this component by boolean.
22132      * @param {Boolean} visible True to show, false to hide
22133      * @return {Roo.Component} this
22134      */
22135     setVisible: function(visible){
22136         if(visible) {
22137             this.show();
22138         }else{
22139             this.hide();
22140         }
22141         return this;
22142     },
22143
22144     /**
22145      * Returns true if this component is visible.
22146      */
22147     isVisible : function(){
22148         return this.getActionEl().isVisible();
22149     },
22150
22151     cloneConfig : function(overrides){
22152         overrides = overrides || {};
22153         var id = overrides.id || Roo.id();
22154         var cfg = Roo.applyIf(overrides, this.initialConfig);
22155         cfg.id = id; // prevent dup id
22156         return new this.constructor(cfg);
22157     }
22158 });/*
22159  * Based on:
22160  * Ext JS Library 1.1.1
22161  * Copyright(c) 2006-2007, Ext JS, LLC.
22162  *
22163  * Originally Released Under LGPL - original licence link has changed is not relivant.
22164  *
22165  * Fork - LGPL
22166  * <script type="text/javascript">
22167  */
22168  (function(){ 
22169 /**
22170  * @class Roo.Layer
22171  * @extends Roo.Element
22172  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22173  * automatic maintaining of shadow/shim positions.
22174  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22175  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22176  * you can pass a string with a CSS class name. False turns off the shadow.
22177  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22178  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22179  * @cfg {String} cls CSS class to add to the element
22180  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22181  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22182  * @constructor
22183  * @param {Object} config An object with config options.
22184  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22185  */
22186
22187 Roo.Layer = function(config, existingEl){
22188     config = config || {};
22189     var dh = Roo.DomHelper;
22190     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22191     if(existingEl){
22192         this.dom = Roo.getDom(existingEl);
22193     }
22194     if(!this.dom){
22195         var o = config.dh || {tag: "div", cls: "x-layer"};
22196         this.dom = dh.append(pel, o);
22197     }
22198     if(config.cls){
22199         this.addClass(config.cls);
22200     }
22201     this.constrain = config.constrain !== false;
22202     this.visibilityMode = Roo.Element.VISIBILITY;
22203     if(config.id){
22204         this.id = this.dom.id = config.id;
22205     }else{
22206         this.id = Roo.id(this.dom);
22207     }
22208     this.zindex = config.zindex || this.getZIndex();
22209     this.position("absolute", this.zindex);
22210     if(config.shadow){
22211         this.shadowOffset = config.shadowOffset || 4;
22212         this.shadow = new Roo.Shadow({
22213             offset : this.shadowOffset,
22214             mode : config.shadow
22215         });
22216     }else{
22217         this.shadowOffset = 0;
22218     }
22219     this.useShim = config.shim !== false && Roo.useShims;
22220     this.useDisplay = config.useDisplay;
22221     this.hide();
22222 };
22223
22224 var supr = Roo.Element.prototype;
22225
22226 // shims are shared among layer to keep from having 100 iframes
22227 var shims = [];
22228
22229 Roo.extend(Roo.Layer, Roo.Element, {
22230
22231     getZIndex : function(){
22232         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22233     },
22234
22235     getShim : function(){
22236         if(!this.useShim){
22237             return null;
22238         }
22239         if(this.shim){
22240             return this.shim;
22241         }
22242         var shim = shims.shift();
22243         if(!shim){
22244             shim = this.createShim();
22245             shim.enableDisplayMode('block');
22246             shim.dom.style.display = 'none';
22247             shim.dom.style.visibility = 'visible';
22248         }
22249         var pn = this.dom.parentNode;
22250         if(shim.dom.parentNode != pn){
22251             pn.insertBefore(shim.dom, this.dom);
22252         }
22253         shim.setStyle('z-index', this.getZIndex()-2);
22254         this.shim = shim;
22255         return shim;
22256     },
22257
22258     hideShim : function(){
22259         if(this.shim){
22260             this.shim.setDisplayed(false);
22261             shims.push(this.shim);
22262             delete this.shim;
22263         }
22264     },
22265
22266     disableShadow : function(){
22267         if(this.shadow){
22268             this.shadowDisabled = true;
22269             this.shadow.hide();
22270             this.lastShadowOffset = this.shadowOffset;
22271             this.shadowOffset = 0;
22272         }
22273     },
22274
22275     enableShadow : function(show){
22276         if(this.shadow){
22277             this.shadowDisabled = false;
22278             this.shadowOffset = this.lastShadowOffset;
22279             delete this.lastShadowOffset;
22280             if(show){
22281                 this.sync(true);
22282             }
22283         }
22284     },
22285
22286     // private
22287     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22288     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22289     sync : function(doShow){
22290         var sw = this.shadow;
22291         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22292             var sh = this.getShim();
22293
22294             var w = this.getWidth(),
22295                 h = this.getHeight();
22296
22297             var l = this.getLeft(true),
22298                 t = this.getTop(true);
22299
22300             if(sw && !this.shadowDisabled){
22301                 if(doShow && !sw.isVisible()){
22302                     sw.show(this);
22303                 }else{
22304                     sw.realign(l, t, w, h);
22305                 }
22306                 if(sh){
22307                     if(doShow){
22308                        sh.show();
22309                     }
22310                     // fit the shim behind the shadow, so it is shimmed too
22311                     var a = sw.adjusts, s = sh.dom.style;
22312                     s.left = (Math.min(l, l+a.l))+"px";
22313                     s.top = (Math.min(t, t+a.t))+"px";
22314                     s.width = (w+a.w)+"px";
22315                     s.height = (h+a.h)+"px";
22316                 }
22317             }else if(sh){
22318                 if(doShow){
22319                    sh.show();
22320                 }
22321                 sh.setSize(w, h);
22322                 sh.setLeftTop(l, t);
22323             }
22324             
22325         }
22326     },
22327
22328     // private
22329     destroy : function(){
22330         this.hideShim();
22331         if(this.shadow){
22332             this.shadow.hide();
22333         }
22334         this.removeAllListeners();
22335         var pn = this.dom.parentNode;
22336         if(pn){
22337             pn.removeChild(this.dom);
22338         }
22339         Roo.Element.uncache(this.id);
22340     },
22341
22342     remove : function(){
22343         this.destroy();
22344     },
22345
22346     // private
22347     beginUpdate : function(){
22348         this.updating = true;
22349     },
22350
22351     // private
22352     endUpdate : function(){
22353         this.updating = false;
22354         this.sync(true);
22355     },
22356
22357     // private
22358     hideUnders : function(negOffset){
22359         if(this.shadow){
22360             this.shadow.hide();
22361         }
22362         this.hideShim();
22363     },
22364
22365     // private
22366     constrainXY : function(){
22367         if(this.constrain){
22368             var vw = Roo.lib.Dom.getViewWidth(),
22369                 vh = Roo.lib.Dom.getViewHeight();
22370             var s = Roo.get(document).getScroll();
22371
22372             var xy = this.getXY();
22373             var x = xy[0], y = xy[1];   
22374             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22375             // only move it if it needs it
22376             var moved = false;
22377             // first validate right/bottom
22378             if((x + w) > vw+s.left){
22379                 x = vw - w - this.shadowOffset;
22380                 moved = true;
22381             }
22382             if((y + h) > vh+s.top){
22383                 y = vh - h - this.shadowOffset;
22384                 moved = true;
22385             }
22386             // then make sure top/left isn't negative
22387             if(x < s.left){
22388                 x = s.left;
22389                 moved = true;
22390             }
22391             if(y < s.top){
22392                 y = s.top;
22393                 moved = true;
22394             }
22395             if(moved){
22396                 if(this.avoidY){
22397                     var ay = this.avoidY;
22398                     if(y <= ay && (y+h) >= ay){
22399                         y = ay-h-5;   
22400                     }
22401                 }
22402                 xy = [x, y];
22403                 this.storeXY(xy);
22404                 supr.setXY.call(this, xy);
22405                 this.sync();
22406             }
22407         }
22408     },
22409
22410     isVisible : function(){
22411         return this.visible;    
22412     },
22413
22414     // private
22415     showAction : function(){
22416         this.visible = true; // track visibility to prevent getStyle calls
22417         if(this.useDisplay === true){
22418             this.setDisplayed("");
22419         }else if(this.lastXY){
22420             supr.setXY.call(this, this.lastXY);
22421         }else if(this.lastLT){
22422             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22423         }
22424     },
22425
22426     // private
22427     hideAction : function(){
22428         this.visible = false;
22429         if(this.useDisplay === true){
22430             this.setDisplayed(false);
22431         }else{
22432             this.setLeftTop(-10000,-10000);
22433         }
22434     },
22435
22436     // overridden Element method
22437     setVisible : function(v, a, d, c, e){
22438         if(v){
22439             this.showAction();
22440         }
22441         if(a && v){
22442             var cb = function(){
22443                 this.sync(true);
22444                 if(c){
22445                     c();
22446                 }
22447             }.createDelegate(this);
22448             supr.setVisible.call(this, true, true, d, cb, e);
22449         }else{
22450             if(!v){
22451                 this.hideUnders(true);
22452             }
22453             var cb = c;
22454             if(a){
22455                 cb = function(){
22456                     this.hideAction();
22457                     if(c){
22458                         c();
22459                     }
22460                 }.createDelegate(this);
22461             }
22462             supr.setVisible.call(this, v, a, d, cb, e);
22463             if(v){
22464                 this.sync(true);
22465             }else if(!a){
22466                 this.hideAction();
22467             }
22468         }
22469     },
22470
22471     storeXY : function(xy){
22472         delete this.lastLT;
22473         this.lastXY = xy;
22474     },
22475
22476     storeLeftTop : function(left, top){
22477         delete this.lastXY;
22478         this.lastLT = [left, top];
22479     },
22480
22481     // private
22482     beforeFx : function(){
22483         this.beforeAction();
22484         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22485     },
22486
22487     // private
22488     afterFx : function(){
22489         Roo.Layer.superclass.afterFx.apply(this, arguments);
22490         this.sync(this.isVisible());
22491     },
22492
22493     // private
22494     beforeAction : function(){
22495         if(!this.updating && this.shadow){
22496             this.shadow.hide();
22497         }
22498     },
22499
22500     // overridden Element method
22501     setLeft : function(left){
22502         this.storeLeftTop(left, this.getTop(true));
22503         supr.setLeft.apply(this, arguments);
22504         this.sync();
22505     },
22506
22507     setTop : function(top){
22508         this.storeLeftTop(this.getLeft(true), top);
22509         supr.setTop.apply(this, arguments);
22510         this.sync();
22511     },
22512
22513     setLeftTop : function(left, top){
22514         this.storeLeftTop(left, top);
22515         supr.setLeftTop.apply(this, arguments);
22516         this.sync();
22517     },
22518
22519     setXY : function(xy, a, d, c, e){
22520         this.fixDisplay();
22521         this.beforeAction();
22522         this.storeXY(xy);
22523         var cb = this.createCB(c);
22524         supr.setXY.call(this, xy, a, d, cb, e);
22525         if(!a){
22526             cb();
22527         }
22528     },
22529
22530     // private
22531     createCB : function(c){
22532         var el = this;
22533         return function(){
22534             el.constrainXY();
22535             el.sync(true);
22536             if(c){
22537                 c();
22538             }
22539         };
22540     },
22541
22542     // overridden Element method
22543     setX : function(x, a, d, c, e){
22544         this.setXY([x, this.getY()], a, d, c, e);
22545     },
22546
22547     // overridden Element method
22548     setY : function(y, a, d, c, e){
22549         this.setXY([this.getX(), y], a, d, c, e);
22550     },
22551
22552     // overridden Element method
22553     setSize : function(w, h, a, d, c, e){
22554         this.beforeAction();
22555         var cb = this.createCB(c);
22556         supr.setSize.call(this, w, h, a, d, cb, e);
22557         if(!a){
22558             cb();
22559         }
22560     },
22561
22562     // overridden Element method
22563     setWidth : function(w, a, d, c, e){
22564         this.beforeAction();
22565         var cb = this.createCB(c);
22566         supr.setWidth.call(this, w, a, d, cb, e);
22567         if(!a){
22568             cb();
22569         }
22570     },
22571
22572     // overridden Element method
22573     setHeight : function(h, a, d, c, e){
22574         this.beforeAction();
22575         var cb = this.createCB(c);
22576         supr.setHeight.call(this, h, a, d, cb, e);
22577         if(!a){
22578             cb();
22579         }
22580     },
22581
22582     // overridden Element method
22583     setBounds : function(x, y, w, h, a, d, c, e){
22584         this.beforeAction();
22585         var cb = this.createCB(c);
22586         if(!a){
22587             this.storeXY([x, y]);
22588             supr.setXY.call(this, [x, y]);
22589             supr.setSize.call(this, w, h, a, d, cb, e);
22590             cb();
22591         }else{
22592             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22593         }
22594         return this;
22595     },
22596     
22597     /**
22598      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22599      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22600      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22601      * @param {Number} zindex The new z-index to set
22602      * @return {this} The Layer
22603      */
22604     setZIndex : function(zindex){
22605         this.zindex = zindex;
22606         this.setStyle("z-index", zindex + 2);
22607         if(this.shadow){
22608             this.shadow.setZIndex(zindex + 1);
22609         }
22610         if(this.shim){
22611             this.shim.setStyle("z-index", zindex);
22612         }
22613     }
22614 });
22615 })();/*
22616  * Based on:
22617  * Ext JS Library 1.1.1
22618  * Copyright(c) 2006-2007, Ext JS, LLC.
22619  *
22620  * Originally Released Under LGPL - original licence link has changed is not relivant.
22621  *
22622  * Fork - LGPL
22623  * <script type="text/javascript">
22624  */
22625
22626
22627 /**
22628  * @class Roo.Shadow
22629  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22630  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22631  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22632  * @constructor
22633  * Create a new Shadow
22634  * @param {Object} config The config object
22635  */
22636 Roo.Shadow = function(config){
22637     Roo.apply(this, config);
22638     if(typeof this.mode != "string"){
22639         this.mode = this.defaultMode;
22640     }
22641     var o = this.offset, a = {h: 0};
22642     var rad = Math.floor(this.offset/2);
22643     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22644         case "drop":
22645             a.w = 0;
22646             a.l = a.t = o;
22647             a.t -= 1;
22648             if(Roo.isIE){
22649                 a.l -= this.offset + rad;
22650                 a.t -= this.offset + rad;
22651                 a.w -= rad;
22652                 a.h -= rad;
22653                 a.t += 1;
22654             }
22655         break;
22656         case "sides":
22657             a.w = (o*2);
22658             a.l = -o;
22659             a.t = o-1;
22660             if(Roo.isIE){
22661                 a.l -= (this.offset - rad);
22662                 a.t -= this.offset + rad;
22663                 a.l += 1;
22664                 a.w -= (this.offset - rad)*2;
22665                 a.w -= rad + 1;
22666                 a.h -= 1;
22667             }
22668         break;
22669         case "frame":
22670             a.w = a.h = (o*2);
22671             a.l = a.t = -o;
22672             a.t += 1;
22673             a.h -= 2;
22674             if(Roo.isIE){
22675                 a.l -= (this.offset - rad);
22676                 a.t -= (this.offset - rad);
22677                 a.l += 1;
22678                 a.w -= (this.offset + rad + 1);
22679                 a.h -= (this.offset + rad);
22680                 a.h += 1;
22681             }
22682         break;
22683     };
22684
22685     this.adjusts = a;
22686 };
22687
22688 Roo.Shadow.prototype = {
22689     /**
22690      * @cfg {String} mode
22691      * The shadow display mode.  Supports the following options:<br />
22692      * sides: Shadow displays on both sides and bottom only<br />
22693      * frame: Shadow displays equally on all four sides<br />
22694      * drop: Traditional bottom-right drop shadow (default)
22695      */
22696     /**
22697      * @cfg {String} offset
22698      * The number of pixels to offset the shadow from the element (defaults to 4)
22699      */
22700     offset: 4,
22701
22702     // private
22703     defaultMode: "drop",
22704
22705     /**
22706      * Displays the shadow under the target element
22707      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22708      */
22709     show : function(target){
22710         target = Roo.get(target);
22711         if(!this.el){
22712             this.el = Roo.Shadow.Pool.pull();
22713             if(this.el.dom.nextSibling != target.dom){
22714                 this.el.insertBefore(target);
22715             }
22716         }
22717         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22718         if(Roo.isIE){
22719             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22720         }
22721         this.realign(
22722             target.getLeft(true),
22723             target.getTop(true),
22724             target.getWidth(),
22725             target.getHeight()
22726         );
22727         this.el.dom.style.display = "block";
22728     },
22729
22730     /**
22731      * Returns true if the shadow is visible, else false
22732      */
22733     isVisible : function(){
22734         return this.el ? true : false;  
22735     },
22736
22737     /**
22738      * Direct alignment when values are already available. Show must be called at least once before
22739      * calling this method to ensure it is initialized.
22740      * @param {Number} left The target element left position
22741      * @param {Number} top The target element top position
22742      * @param {Number} width The target element width
22743      * @param {Number} height The target element height
22744      */
22745     realign : function(l, t, w, h){
22746         if(!this.el){
22747             return;
22748         }
22749         var a = this.adjusts, d = this.el.dom, s = d.style;
22750         var iea = 0;
22751         s.left = (l+a.l)+"px";
22752         s.top = (t+a.t)+"px";
22753         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22754  
22755         if(s.width != sws || s.height != shs){
22756             s.width = sws;
22757             s.height = shs;
22758             if(!Roo.isIE){
22759                 var cn = d.childNodes;
22760                 var sww = Math.max(0, (sw-12))+"px";
22761                 cn[0].childNodes[1].style.width = sww;
22762                 cn[1].childNodes[1].style.width = sww;
22763                 cn[2].childNodes[1].style.width = sww;
22764                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22765             }
22766         }
22767     },
22768
22769     /**
22770      * Hides this shadow
22771      */
22772     hide : function(){
22773         if(this.el){
22774             this.el.dom.style.display = "none";
22775             Roo.Shadow.Pool.push(this.el);
22776             delete this.el;
22777         }
22778     },
22779
22780     /**
22781      * Adjust the z-index of this shadow
22782      * @param {Number} zindex The new z-index
22783      */
22784     setZIndex : function(z){
22785         this.zIndex = z;
22786         if(this.el){
22787             this.el.setStyle("z-index", z);
22788         }
22789     }
22790 };
22791
22792 // Private utility class that manages the internal Shadow cache
22793 Roo.Shadow.Pool = function(){
22794     var p = [];
22795     var markup = Roo.isIE ?
22796                  '<div class="x-ie-shadow"></div>' :
22797                  '<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>';
22798     return {
22799         pull : function(){
22800             var sh = p.shift();
22801             if(!sh){
22802                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22803                 sh.autoBoxAdjust = false;
22804             }
22805             return sh;
22806         },
22807
22808         push : function(sh){
22809             p.push(sh);
22810         }
22811     };
22812 }();/*
22813  * Based on:
22814  * Ext JS Library 1.1.1
22815  * Copyright(c) 2006-2007, Ext JS, LLC.
22816  *
22817  * Originally Released Under LGPL - original licence link has changed is not relivant.
22818  *
22819  * Fork - LGPL
22820  * <script type="text/javascript">
22821  */
22822
22823 /**
22824  * @class Roo.BoxComponent
22825  * @extends Roo.Component
22826  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22827  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22828  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22829  * layout containers.
22830  * @constructor
22831  * @param {Roo.Element/String/Object} config The configuration options.
22832  */
22833 Roo.BoxComponent = function(config){
22834     Roo.Component.call(this, config);
22835     this.addEvents({
22836         /**
22837          * @event resize
22838          * Fires after the component is resized.
22839              * @param {Roo.Component} this
22840              * @param {Number} adjWidth The box-adjusted width that was set
22841              * @param {Number} adjHeight The box-adjusted height that was set
22842              * @param {Number} rawWidth The width that was originally specified
22843              * @param {Number} rawHeight The height that was originally specified
22844              */
22845         resize : true,
22846         /**
22847          * @event move
22848          * Fires after the component is moved.
22849              * @param {Roo.Component} this
22850              * @param {Number} x The new x position
22851              * @param {Number} y The new y position
22852              */
22853         move : true
22854     });
22855 };
22856
22857 Roo.extend(Roo.BoxComponent, Roo.Component, {
22858     // private, set in afterRender to signify that the component has been rendered
22859     boxReady : false,
22860     // private, used to defer height settings to subclasses
22861     deferHeight: false,
22862     /** @cfg {Number} width
22863      * width (optional) size of component
22864      */
22865      /** @cfg {Number} height
22866      * height (optional) size of component
22867      */
22868      
22869     /**
22870      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22871      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22872      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22873      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22874      * @return {Roo.BoxComponent} this
22875      */
22876     setSize : function(w, h){
22877         // support for standard size objects
22878         if(typeof w == 'object'){
22879             h = w.height;
22880             w = w.width;
22881         }
22882         // not rendered
22883         if(!this.boxReady){
22884             this.width = w;
22885             this.height = h;
22886             return this;
22887         }
22888
22889         // prevent recalcs when not needed
22890         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22891             return this;
22892         }
22893         this.lastSize = {width: w, height: h};
22894
22895         var adj = this.adjustSize(w, h);
22896         var aw = adj.width, ah = adj.height;
22897         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22898             var rz = this.getResizeEl();
22899             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22900                 rz.setSize(aw, ah);
22901             }else if(!this.deferHeight && ah !== undefined){
22902                 rz.setHeight(ah);
22903             }else if(aw !== undefined){
22904                 rz.setWidth(aw);
22905             }
22906             this.onResize(aw, ah, w, h);
22907             this.fireEvent('resize', this, aw, ah, w, h);
22908         }
22909         return this;
22910     },
22911
22912     /**
22913      * Gets the current size of the component's underlying element.
22914      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22915      */
22916     getSize : function(){
22917         return this.el.getSize();
22918     },
22919
22920     /**
22921      * Gets the current XY position of the component's underlying element.
22922      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22923      * @return {Array} The XY position of the element (e.g., [100, 200])
22924      */
22925     getPosition : function(local){
22926         if(local === true){
22927             return [this.el.getLeft(true), this.el.getTop(true)];
22928         }
22929         return this.xy || this.el.getXY();
22930     },
22931
22932     /**
22933      * Gets the current box measurements of the component's underlying element.
22934      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22935      * @returns {Object} box An object in the format {x, y, width, height}
22936      */
22937     getBox : function(local){
22938         var s = this.el.getSize();
22939         if(local){
22940             s.x = this.el.getLeft(true);
22941             s.y = this.el.getTop(true);
22942         }else{
22943             var xy = this.xy || this.el.getXY();
22944             s.x = xy[0];
22945             s.y = xy[1];
22946         }
22947         return s;
22948     },
22949
22950     /**
22951      * Sets the current box measurements of the component's underlying element.
22952      * @param {Object} box An object in the format {x, y, width, height}
22953      * @returns {Roo.BoxComponent} this
22954      */
22955     updateBox : function(box){
22956         this.setSize(box.width, box.height);
22957         this.setPagePosition(box.x, box.y);
22958         return this;
22959     },
22960
22961     // protected
22962     getResizeEl : function(){
22963         return this.resizeEl || this.el;
22964     },
22965
22966     // protected
22967     getPositionEl : function(){
22968         return this.positionEl || this.el;
22969     },
22970
22971     /**
22972      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22973      * This method fires the move event.
22974      * @param {Number} left The new left
22975      * @param {Number} top The new top
22976      * @returns {Roo.BoxComponent} this
22977      */
22978     setPosition : function(x, y){
22979         this.x = x;
22980         this.y = y;
22981         if(!this.boxReady){
22982             return this;
22983         }
22984         var adj = this.adjustPosition(x, y);
22985         var ax = adj.x, ay = adj.y;
22986
22987         var el = this.getPositionEl();
22988         if(ax !== undefined || ay !== undefined){
22989             if(ax !== undefined && ay !== undefined){
22990                 el.setLeftTop(ax, ay);
22991             }else if(ax !== undefined){
22992                 el.setLeft(ax);
22993             }else if(ay !== undefined){
22994                 el.setTop(ay);
22995             }
22996             this.onPosition(ax, ay);
22997             this.fireEvent('move', this, ax, ay);
22998         }
22999         return this;
23000     },
23001
23002     /**
23003      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23004      * This method fires the move event.
23005      * @param {Number} x The new x position
23006      * @param {Number} y The new y position
23007      * @returns {Roo.BoxComponent} this
23008      */
23009     setPagePosition : function(x, y){
23010         this.pageX = x;
23011         this.pageY = y;
23012         if(!this.boxReady){
23013             return;
23014         }
23015         if(x === undefined || y === undefined){ // cannot translate undefined points
23016             return;
23017         }
23018         var p = this.el.translatePoints(x, y);
23019         this.setPosition(p.left, p.top);
23020         return this;
23021     },
23022
23023     // private
23024     onRender : function(ct, position){
23025         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23026         if(this.resizeEl){
23027             this.resizeEl = Roo.get(this.resizeEl);
23028         }
23029         if(this.positionEl){
23030             this.positionEl = Roo.get(this.positionEl);
23031         }
23032     },
23033
23034     // private
23035     afterRender : function(){
23036         Roo.BoxComponent.superclass.afterRender.call(this);
23037         this.boxReady = true;
23038         this.setSize(this.width, this.height);
23039         if(this.x || this.y){
23040             this.setPosition(this.x, this.y);
23041         }
23042         if(this.pageX || this.pageY){
23043             this.setPagePosition(this.pageX, this.pageY);
23044         }
23045     },
23046
23047     /**
23048      * Force the component's size to recalculate based on the underlying element's current height and width.
23049      * @returns {Roo.BoxComponent} this
23050      */
23051     syncSize : function(){
23052         delete this.lastSize;
23053         this.setSize(this.el.getWidth(), this.el.getHeight());
23054         return this;
23055     },
23056
23057     /**
23058      * Called after the component is resized, this method is empty by default but can be implemented by any
23059      * subclass that needs to perform custom logic after a resize occurs.
23060      * @param {Number} adjWidth The box-adjusted width that was set
23061      * @param {Number} adjHeight The box-adjusted height that was set
23062      * @param {Number} rawWidth The width that was originally specified
23063      * @param {Number} rawHeight The height that was originally specified
23064      */
23065     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23066
23067     },
23068
23069     /**
23070      * Called after the component is moved, this method is empty by default but can be implemented by any
23071      * subclass that needs to perform custom logic after a move occurs.
23072      * @param {Number} x The new x position
23073      * @param {Number} y The new y position
23074      */
23075     onPosition : function(x, y){
23076
23077     },
23078
23079     // private
23080     adjustSize : function(w, h){
23081         if(this.autoWidth){
23082             w = 'auto';
23083         }
23084         if(this.autoHeight){
23085             h = 'auto';
23086         }
23087         return {width : w, height: h};
23088     },
23089
23090     // private
23091     adjustPosition : function(x, y){
23092         return {x : x, y: y};
23093     }
23094 });/*
23095  * Based on:
23096  * Ext JS Library 1.1.1
23097  * Copyright(c) 2006-2007, Ext JS, LLC.
23098  *
23099  * Originally Released Under LGPL - original licence link has changed is not relivant.
23100  *
23101  * Fork - LGPL
23102  * <script type="text/javascript">
23103  */
23104
23105
23106 /**
23107  * @class Roo.SplitBar
23108  * @extends Roo.util.Observable
23109  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23110  * <br><br>
23111  * Usage:
23112  * <pre><code>
23113 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23114                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23115 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23116 split.minSize = 100;
23117 split.maxSize = 600;
23118 split.animate = true;
23119 split.on('moved', splitterMoved);
23120 </code></pre>
23121  * @constructor
23122  * Create a new SplitBar
23123  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23124  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23125  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23126  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23127                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23128                         position of the SplitBar).
23129  */
23130 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23131     
23132     /** @private */
23133     this.el = Roo.get(dragElement, true);
23134     this.el.dom.unselectable = "on";
23135     /** @private */
23136     this.resizingEl = Roo.get(resizingElement, true);
23137
23138     /**
23139      * @private
23140      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23141      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23142      * @type Number
23143      */
23144     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23145     
23146     /**
23147      * The minimum size of the resizing element. (Defaults to 0)
23148      * @type Number
23149      */
23150     this.minSize = 0;
23151     
23152     /**
23153      * The maximum size of the resizing element. (Defaults to 2000)
23154      * @type Number
23155      */
23156     this.maxSize = 2000;
23157     
23158     /**
23159      * Whether to animate the transition to the new size
23160      * @type Boolean
23161      */
23162     this.animate = false;
23163     
23164     /**
23165      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23166      * @type Boolean
23167      */
23168     this.useShim = false;
23169     
23170     /** @private */
23171     this.shim = null;
23172     
23173     if(!existingProxy){
23174         /** @private */
23175         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23176     }else{
23177         this.proxy = Roo.get(existingProxy).dom;
23178     }
23179     /** @private */
23180     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23181     
23182     /** @private */
23183     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23184     
23185     /** @private */
23186     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23187     
23188     /** @private */
23189     this.dragSpecs = {};
23190     
23191     /**
23192      * @private The adapter to use to positon and resize elements
23193      */
23194     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23195     this.adapter.init(this);
23196     
23197     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23198         /** @private */
23199         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23200         this.el.addClass("x-splitbar-h");
23201     }else{
23202         /** @private */
23203         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23204         this.el.addClass("x-splitbar-v");
23205     }
23206     
23207     this.addEvents({
23208         /**
23209          * @event resize
23210          * Fires when the splitter is moved (alias for {@link #event-moved})
23211          * @param {Roo.SplitBar} this
23212          * @param {Number} newSize the new width or height
23213          */
23214         "resize" : true,
23215         /**
23216          * @event moved
23217          * Fires when the splitter is moved
23218          * @param {Roo.SplitBar} this
23219          * @param {Number} newSize the new width or height
23220          */
23221         "moved" : true,
23222         /**
23223          * @event beforeresize
23224          * Fires before the splitter is dragged
23225          * @param {Roo.SplitBar} this
23226          */
23227         "beforeresize" : true,
23228
23229         "beforeapply" : true
23230     });
23231
23232     Roo.util.Observable.call(this);
23233 };
23234
23235 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23236     onStartProxyDrag : function(x, y){
23237         this.fireEvent("beforeresize", this);
23238         if(!this.overlay){
23239             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23240             o.unselectable();
23241             o.enableDisplayMode("block");
23242             // all splitbars share the same overlay
23243             Roo.SplitBar.prototype.overlay = o;
23244         }
23245         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23246         this.overlay.show();
23247         Roo.get(this.proxy).setDisplayed("block");
23248         var size = this.adapter.getElementSize(this);
23249         this.activeMinSize = this.getMinimumSize();;
23250         this.activeMaxSize = this.getMaximumSize();;
23251         var c1 = size - this.activeMinSize;
23252         var c2 = Math.max(this.activeMaxSize - size, 0);
23253         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23254             this.dd.resetConstraints();
23255             this.dd.setXConstraint(
23256                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23257                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23258             );
23259             this.dd.setYConstraint(0, 0);
23260         }else{
23261             this.dd.resetConstraints();
23262             this.dd.setXConstraint(0, 0);
23263             this.dd.setYConstraint(
23264                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23265                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23266             );
23267          }
23268         this.dragSpecs.startSize = size;
23269         this.dragSpecs.startPoint = [x, y];
23270         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23271     },
23272     
23273     /** 
23274      * @private Called after the drag operation by the DDProxy
23275      */
23276     onEndProxyDrag : function(e){
23277         Roo.get(this.proxy).setDisplayed(false);
23278         var endPoint = Roo.lib.Event.getXY(e);
23279         if(this.overlay){
23280             this.overlay.hide();
23281         }
23282         var newSize;
23283         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23284             newSize = this.dragSpecs.startSize + 
23285                 (this.placement == Roo.SplitBar.LEFT ?
23286                     endPoint[0] - this.dragSpecs.startPoint[0] :
23287                     this.dragSpecs.startPoint[0] - endPoint[0]
23288                 );
23289         }else{
23290             newSize = this.dragSpecs.startSize + 
23291                 (this.placement == Roo.SplitBar.TOP ?
23292                     endPoint[1] - this.dragSpecs.startPoint[1] :
23293                     this.dragSpecs.startPoint[1] - endPoint[1]
23294                 );
23295         }
23296         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23297         if(newSize != this.dragSpecs.startSize){
23298             if(this.fireEvent('beforeapply', this, newSize) !== false){
23299                 this.adapter.setElementSize(this, newSize);
23300                 this.fireEvent("moved", this, newSize);
23301                 this.fireEvent("resize", this, newSize);
23302             }
23303         }
23304     },
23305     
23306     /**
23307      * Get the adapter this SplitBar uses
23308      * @return The adapter object
23309      */
23310     getAdapter : function(){
23311         return this.adapter;
23312     },
23313     
23314     /**
23315      * Set the adapter this SplitBar uses
23316      * @param {Object} adapter A SplitBar adapter object
23317      */
23318     setAdapter : function(adapter){
23319         this.adapter = adapter;
23320         this.adapter.init(this);
23321     },
23322     
23323     /**
23324      * Gets the minimum size for the resizing element
23325      * @return {Number} The minimum size
23326      */
23327     getMinimumSize : function(){
23328         return this.minSize;
23329     },
23330     
23331     /**
23332      * Sets the minimum size for the resizing element
23333      * @param {Number} minSize The minimum size
23334      */
23335     setMinimumSize : function(minSize){
23336         this.minSize = minSize;
23337     },
23338     
23339     /**
23340      * Gets the maximum size for the resizing element
23341      * @return {Number} The maximum size
23342      */
23343     getMaximumSize : function(){
23344         return this.maxSize;
23345     },
23346     
23347     /**
23348      * Sets the maximum size for the resizing element
23349      * @param {Number} maxSize The maximum size
23350      */
23351     setMaximumSize : function(maxSize){
23352         this.maxSize = maxSize;
23353     },
23354     
23355     /**
23356      * Sets the initialize size for the resizing element
23357      * @param {Number} size The initial size
23358      */
23359     setCurrentSize : function(size){
23360         var oldAnimate = this.animate;
23361         this.animate = false;
23362         this.adapter.setElementSize(this, size);
23363         this.animate = oldAnimate;
23364     },
23365     
23366     /**
23367      * Destroy this splitbar. 
23368      * @param {Boolean} removeEl True to remove the element
23369      */
23370     destroy : function(removeEl){
23371         if(this.shim){
23372             this.shim.remove();
23373         }
23374         this.dd.unreg();
23375         this.proxy.parentNode.removeChild(this.proxy);
23376         if(removeEl){
23377             this.el.remove();
23378         }
23379     }
23380 });
23381
23382 /**
23383  * @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.
23384  */
23385 Roo.SplitBar.createProxy = function(dir){
23386     var proxy = new Roo.Element(document.createElement("div"));
23387     proxy.unselectable();
23388     var cls = 'x-splitbar-proxy';
23389     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23390     document.body.appendChild(proxy.dom);
23391     return proxy.dom;
23392 };
23393
23394 /** 
23395  * @class Roo.SplitBar.BasicLayoutAdapter
23396  * Default Adapter. It assumes the splitter and resizing element are not positioned
23397  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23398  */
23399 Roo.SplitBar.BasicLayoutAdapter = function(){
23400 };
23401
23402 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23403     // do nothing for now
23404     init : function(s){
23405     
23406     },
23407     /**
23408      * Called before drag operations to get the current size of the resizing element. 
23409      * @param {Roo.SplitBar} s The SplitBar using this adapter
23410      */
23411      getElementSize : function(s){
23412         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23413             return s.resizingEl.getWidth();
23414         }else{
23415             return s.resizingEl.getHeight();
23416         }
23417     },
23418     
23419     /**
23420      * Called after drag operations to set the size of the resizing element.
23421      * @param {Roo.SplitBar} s The SplitBar using this adapter
23422      * @param {Number} newSize The new size to set
23423      * @param {Function} onComplete A function to be invoked when resizing is complete
23424      */
23425     setElementSize : function(s, newSize, onComplete){
23426         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23427             if(!s.animate){
23428                 s.resizingEl.setWidth(newSize);
23429                 if(onComplete){
23430                     onComplete(s, newSize);
23431                 }
23432             }else{
23433                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23434             }
23435         }else{
23436             
23437             if(!s.animate){
23438                 s.resizingEl.setHeight(newSize);
23439                 if(onComplete){
23440                     onComplete(s, newSize);
23441                 }
23442             }else{
23443                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23444             }
23445         }
23446     }
23447 };
23448
23449 /** 
23450  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23451  * @extends Roo.SplitBar.BasicLayoutAdapter
23452  * Adapter that  moves the splitter element to align with the resized sizing element. 
23453  * Used with an absolute positioned SplitBar.
23454  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23455  * document.body, make sure you assign an id to the body element.
23456  */
23457 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23458     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23459     this.container = Roo.get(container);
23460 };
23461
23462 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23463     init : function(s){
23464         this.basic.init(s);
23465     },
23466     
23467     getElementSize : function(s){
23468         return this.basic.getElementSize(s);
23469     },
23470     
23471     setElementSize : function(s, newSize, onComplete){
23472         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23473     },
23474     
23475     moveSplitter : function(s){
23476         var yes = Roo.SplitBar;
23477         switch(s.placement){
23478             case yes.LEFT:
23479                 s.el.setX(s.resizingEl.getRight());
23480                 break;
23481             case yes.RIGHT:
23482                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23483                 break;
23484             case yes.TOP:
23485                 s.el.setY(s.resizingEl.getBottom());
23486                 break;
23487             case yes.BOTTOM:
23488                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23489                 break;
23490         }
23491     }
23492 };
23493
23494 /**
23495  * Orientation constant - Create a vertical SplitBar
23496  * @static
23497  * @type Number
23498  */
23499 Roo.SplitBar.VERTICAL = 1;
23500
23501 /**
23502  * Orientation constant - Create a horizontal SplitBar
23503  * @static
23504  * @type Number
23505  */
23506 Roo.SplitBar.HORIZONTAL = 2;
23507
23508 /**
23509  * Placement constant - The resizing element is to the left of the splitter element
23510  * @static
23511  * @type Number
23512  */
23513 Roo.SplitBar.LEFT = 1;
23514
23515 /**
23516  * Placement constant - The resizing element is to the right of the splitter element
23517  * @static
23518  * @type Number
23519  */
23520 Roo.SplitBar.RIGHT = 2;
23521
23522 /**
23523  * Placement constant - The resizing element is positioned above the splitter element
23524  * @static
23525  * @type Number
23526  */
23527 Roo.SplitBar.TOP = 3;
23528
23529 /**
23530  * Placement constant - The resizing element is positioned under splitter element
23531  * @static
23532  * @type Number
23533  */
23534 Roo.SplitBar.BOTTOM = 4;
23535 /*
23536  * Based on:
23537  * Ext JS Library 1.1.1
23538  * Copyright(c) 2006-2007, Ext JS, LLC.
23539  *
23540  * Originally Released Under LGPL - original licence link has changed is not relivant.
23541  *
23542  * Fork - LGPL
23543  * <script type="text/javascript">
23544  */
23545
23546 /**
23547  * @class Roo.View
23548  * @extends Roo.util.Observable
23549  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23550  * This class also supports single and multi selection modes. <br>
23551  * Create a data model bound view:
23552  <pre><code>
23553  var store = new Roo.data.Store(...);
23554
23555  var view = new Roo.View({
23556     el : "my-element",
23557     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23558  
23559     singleSelect: true,
23560     selectedClass: "ydataview-selected",
23561     store: store
23562  });
23563
23564  // listen for node click?
23565  view.on("click", function(vw, index, node, e){
23566  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23567  });
23568
23569  // load XML data
23570  dataModel.load("foobar.xml");
23571  </code></pre>
23572  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23573  * <br><br>
23574  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23575  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23576  * 
23577  * Note: old style constructor is still suported (container, template, config)
23578  * 
23579  * @constructor
23580  * Create a new View
23581  * @param {Object} config The config object
23582  * 
23583  */
23584 Roo.View = function(config, depreciated_tpl, depreciated_config){
23585     
23586     if (typeof(depreciated_tpl) == 'undefined') {
23587         // new way.. - universal constructor.
23588         Roo.apply(this, config);
23589         this.el  = Roo.get(this.el);
23590     } else {
23591         // old format..
23592         this.el  = Roo.get(config);
23593         this.tpl = depreciated_tpl;
23594         Roo.apply(this, depreciated_config);
23595     }
23596      
23597     
23598     if(typeof(this.tpl) == "string"){
23599         this.tpl = new Roo.Template(this.tpl);
23600     } else {
23601         // support xtype ctors..
23602         this.tpl = new Roo.factory(this.tpl, Roo);
23603     }
23604     
23605     
23606     this.tpl.compile();
23607    
23608
23609      
23610     /** @private */
23611     this.addEvents({
23612         /**
23613          * @event beforeclick
23614          * Fires before a click is processed. Returns false to cancel the default action.
23615          * @param {Roo.View} this
23616          * @param {Number} index The index of the target node
23617          * @param {HTMLElement} node The target node
23618          * @param {Roo.EventObject} e The raw event object
23619          */
23620             "beforeclick" : true,
23621         /**
23622          * @event click
23623          * Fires when a template node is clicked.
23624          * @param {Roo.View} this
23625          * @param {Number} index The index of the target node
23626          * @param {HTMLElement} node The target node
23627          * @param {Roo.EventObject} e The raw event object
23628          */
23629             "click" : true,
23630         /**
23631          * @event dblclick
23632          * Fires when a template node is double clicked.
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             "dblclick" : true,
23639         /**
23640          * @event contextmenu
23641          * Fires when a template node is right 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             "contextmenu" : true,
23648         /**
23649          * @event selectionchange
23650          * Fires when the selected nodes change.
23651          * @param {Roo.View} this
23652          * @param {Array} selections Array of the selected nodes
23653          */
23654             "selectionchange" : true,
23655     
23656         /**
23657          * @event beforeselect
23658          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23659          * @param {Roo.View} this
23660          * @param {HTMLElement} node The node to be selected
23661          * @param {Array} selections Array of currently selected nodes
23662          */
23663             "beforeselect" : true,
23664         /**
23665          * @event preparedata
23666          * Fires on every row to render, to allow you to change the data.
23667          * @param {Roo.View} this
23668          * @param {Object} data to be rendered (change this)
23669          */
23670           "preparedata" : true
23671         });
23672
23673     this.el.on({
23674         "click": this.onClick,
23675         "dblclick": this.onDblClick,
23676         "contextmenu": this.onContextMenu,
23677         scope:this
23678     });
23679
23680     this.selections = [];
23681     this.nodes = [];
23682     this.cmp = new Roo.CompositeElementLite([]);
23683     if(this.store){
23684         this.store = Roo.factory(this.store, Roo.data);
23685         this.setStore(this.store, true);
23686     }
23687     Roo.View.superclass.constructor.call(this);
23688 };
23689
23690 Roo.extend(Roo.View, Roo.util.Observable, {
23691     
23692      /**
23693      * @cfg {Roo.data.Store} store Data store to load data from.
23694      */
23695     store : false,
23696     
23697     /**
23698      * @cfg {String|Roo.Element} el The container element.
23699      */
23700     el : '',
23701     
23702     /**
23703      * @cfg {String|Roo.Template} tpl The template used by this View 
23704      */
23705     tpl : false,
23706     
23707     /**
23708      * @cfg {String} selectedClass The css class to add to selected nodes
23709      */
23710     selectedClass : "x-view-selected",
23711      /**
23712      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23713      */
23714     emptyText : "",
23715     /**
23716      * @cfg {Boolean} multiSelect Allow multiple selection
23717      */
23718     multiSelect : false,
23719     /**
23720      * @cfg {Boolean} singleSelect Allow single selection
23721      */
23722     singleSelect:  false,
23723     
23724     /**
23725      * @cfg {Boolean} toggleSelect - selecting 
23726      */
23727     toggleSelect : false,
23728     
23729     /**
23730      * Returns the element this view is bound to.
23731      * @return {Roo.Element}
23732      */
23733     getEl : function(){
23734         return this.el;
23735     },
23736
23737     /**
23738      * Refreshes the view.
23739      */
23740     refresh : function(){
23741         var t = this.tpl;
23742         this.clearSelections();
23743         this.el.update("");
23744         var html = [];
23745         var records = this.store.getRange();
23746         if(records.length < 1){
23747             this.el.update(this.emptyText);
23748             return;
23749         }
23750         for(var i = 0, len = records.length; i < len; i++){
23751             var data = this.prepareData(records[i].data, i, records[i]);
23752             this.fireEvent("preparedata", this, data, i, records[i]);
23753             html[html.length] = t.apply(data);
23754         }
23755         this.el.update(html.join(""));
23756         this.nodes = this.el.dom.childNodes;
23757         this.updateIndexes(0);
23758     },
23759
23760     /**
23761      * Function to override to reformat the data that is sent to
23762      * the template for each node.
23763      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23764      * a JSON object for an UpdateManager bound view).
23765      */
23766     prepareData : function(data){
23767         return data;
23768     },
23769
23770     onUpdate : function(ds, record){
23771         this.clearSelections();
23772         var index = this.store.indexOf(record);
23773         var n = this.nodes[index];
23774         this.tpl.insertBefore(n, this.prepareData(record.data));
23775         n.parentNode.removeChild(n);
23776         this.updateIndexes(index, index);
23777     },
23778
23779     onAdd : function(ds, records, index){
23780         this.clearSelections();
23781         if(this.nodes.length == 0){
23782             this.refresh();
23783             return;
23784         }
23785         var n = this.nodes[index];
23786         for(var i = 0, len = records.length; i < len; i++){
23787             var d = this.prepareData(records[i].data);
23788             if(n){
23789                 this.tpl.insertBefore(n, d);
23790             }else{
23791                 this.tpl.append(this.el, d);
23792             }
23793         }
23794         this.updateIndexes(index);
23795     },
23796
23797     onRemove : function(ds, record, index){
23798         this.clearSelections();
23799         this.el.dom.removeChild(this.nodes[index]);
23800         this.updateIndexes(index);
23801     },
23802
23803     /**
23804      * Refresh an individual node.
23805      * @param {Number} index
23806      */
23807     refreshNode : function(index){
23808         this.onUpdate(this.store, this.store.getAt(index));
23809     },
23810
23811     updateIndexes : function(startIndex, endIndex){
23812         var ns = this.nodes;
23813         startIndex = startIndex || 0;
23814         endIndex = endIndex || ns.length - 1;
23815         for(var i = startIndex; i <= endIndex; i++){
23816             ns[i].nodeIndex = i;
23817         }
23818     },
23819
23820     /**
23821      * Changes the data store this view uses and refresh the view.
23822      * @param {Store} store
23823      */
23824     setStore : function(store, initial){
23825         if(!initial && this.store){
23826             this.store.un("datachanged", this.refresh);
23827             this.store.un("add", this.onAdd);
23828             this.store.un("remove", this.onRemove);
23829             this.store.un("update", this.onUpdate);
23830             this.store.un("clear", this.refresh);
23831         }
23832         if(store){
23833           
23834             store.on("datachanged", this.refresh, this);
23835             store.on("add", this.onAdd, this);
23836             store.on("remove", this.onRemove, this);
23837             store.on("update", this.onUpdate, this);
23838             store.on("clear", this.refresh, this);
23839         }
23840         
23841         if(store){
23842             this.refresh();
23843         }
23844     },
23845
23846     /**
23847      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23848      * @param {HTMLElement} node
23849      * @return {HTMLElement} The template node
23850      */
23851     findItemFromChild : function(node){
23852         var el = this.el.dom;
23853         if(!node || node.parentNode == el){
23854                     return node;
23855             }
23856             var p = node.parentNode;
23857             while(p && p != el){
23858             if(p.parentNode == el){
23859                 return p;
23860             }
23861             p = p.parentNode;
23862         }
23863             return null;
23864     },
23865
23866     /** @ignore */
23867     onClick : function(e){
23868         var item = this.findItemFromChild(e.getTarget());
23869         if(item){
23870             var index = this.indexOf(item);
23871             if(this.onItemClick(item, index, e) !== false){
23872                 this.fireEvent("click", this, index, item, e);
23873             }
23874         }else{
23875             this.clearSelections();
23876         }
23877     },
23878
23879     /** @ignore */
23880     onContextMenu : function(e){
23881         var item = this.findItemFromChild(e.getTarget());
23882         if(item){
23883             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23884         }
23885     },
23886
23887     /** @ignore */
23888     onDblClick : function(e){
23889         var item = this.findItemFromChild(e.getTarget());
23890         if(item){
23891             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23892         }
23893     },
23894
23895     onItemClick : function(item, index, e)
23896     {
23897         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23898             return false;
23899         }
23900         if (this.toggleSelect) {
23901             var m = this.isSelected(item) ? 'unselect' : 'select';
23902             Roo.log(m);
23903             var _t = this;
23904             _t[m](item, true, false);
23905             return true;
23906         }
23907         if(this.multiSelect || this.singleSelect){
23908             if(this.multiSelect && e.shiftKey && this.lastSelection){
23909                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23910             }else{
23911                 this.select(item, this.multiSelect && e.ctrlKey);
23912                 this.lastSelection = item;
23913             }
23914             e.preventDefault();
23915         }
23916         return true;
23917     },
23918
23919     /**
23920      * Get the number of selected nodes.
23921      * @return {Number}
23922      */
23923     getSelectionCount : function(){
23924         return this.selections.length;
23925     },
23926
23927     /**
23928      * Get the currently selected nodes.
23929      * @return {Array} An array of HTMLElements
23930      */
23931     getSelectedNodes : function(){
23932         return this.selections;
23933     },
23934
23935     /**
23936      * Get the indexes of the selected nodes.
23937      * @return {Array}
23938      */
23939     getSelectedIndexes : function(){
23940         var indexes = [], s = this.selections;
23941         for(var i = 0, len = s.length; i < len; i++){
23942             indexes.push(s[i].nodeIndex);
23943         }
23944         return indexes;
23945     },
23946
23947     /**
23948      * Clear all selections
23949      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23950      */
23951     clearSelections : function(suppressEvent){
23952         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23953             this.cmp.elements = this.selections;
23954             this.cmp.removeClass(this.selectedClass);
23955             this.selections = [];
23956             if(!suppressEvent){
23957                 this.fireEvent("selectionchange", this, this.selections);
23958             }
23959         }
23960     },
23961
23962     /**
23963      * Returns true if the passed node is selected
23964      * @param {HTMLElement/Number} node The node or node index
23965      * @return {Boolean}
23966      */
23967     isSelected : function(node){
23968         var s = this.selections;
23969         if(s.length < 1){
23970             return false;
23971         }
23972         node = this.getNode(node);
23973         return s.indexOf(node) !== -1;
23974     },
23975
23976     /**
23977      * Selects nodes.
23978      * @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
23979      * @param {Boolean} keepExisting (optional) true to keep existing selections
23980      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23981      */
23982     select : function(nodeInfo, keepExisting, suppressEvent){
23983         if(nodeInfo instanceof Array){
23984             if(!keepExisting){
23985                 this.clearSelections(true);
23986             }
23987             for(var i = 0, len = nodeInfo.length; i < len; i++){
23988                 this.select(nodeInfo[i], true, true);
23989             }
23990             return;
23991         } 
23992         var node = this.getNode(nodeInfo);
23993         if(!node || this.isSelected(node)){
23994             return; // already selected.
23995         }
23996         if(!keepExisting){
23997             this.clearSelections(true);
23998         }
23999         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24000             Roo.fly(node).addClass(this.selectedClass);
24001             this.selections.push(node);
24002             if(!suppressEvent){
24003                 this.fireEvent("selectionchange", this, this.selections);
24004             }
24005         }
24006         
24007         
24008     },
24009       /**
24010      * Unselects nodes.
24011      * @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
24012      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24013      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24014      */
24015     unselect : function(nodeInfo, keepExisting, suppressEvent)
24016     {
24017         if(nodeInfo instanceof Array){
24018             Roo.each(this.selections, function(s) {
24019                 this.unselect(s, nodeInfo);
24020             }, this);
24021             return;
24022         }
24023         var node = this.getNode(nodeInfo);
24024         if(!node || !this.isSelected(node)){
24025             Roo.log("not selected");
24026             return; // not selected.
24027         }
24028         // fireevent???
24029         var ns = [];
24030         Roo.each(this.selections, function(s) {
24031             if (s == node ) {
24032                 Roo.fly(node).removeClass(this.selectedClass);
24033
24034                 return;
24035             }
24036             ns.push(s);
24037         },this);
24038         
24039         this.selections= ns;
24040         this.fireEvent("selectionchange", this, this.selections);
24041     },
24042
24043     /**
24044      * Gets a template node.
24045      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24046      * @return {HTMLElement} The node or null if it wasn't found
24047      */
24048     getNode : function(nodeInfo){
24049         if(typeof nodeInfo == "string"){
24050             return document.getElementById(nodeInfo);
24051         }else if(typeof nodeInfo == "number"){
24052             return this.nodes[nodeInfo];
24053         }
24054         return nodeInfo;
24055     },
24056
24057     /**
24058      * Gets a range template nodes.
24059      * @param {Number} startIndex
24060      * @param {Number} endIndex
24061      * @return {Array} An array of nodes
24062      */
24063     getNodes : function(start, end){
24064         var ns = this.nodes;
24065         start = start || 0;
24066         end = typeof end == "undefined" ? ns.length - 1 : end;
24067         var nodes = [];
24068         if(start <= end){
24069             for(var i = start; i <= end; i++){
24070                 nodes.push(ns[i]);
24071             }
24072         } else{
24073             for(var i = start; i >= end; i--){
24074                 nodes.push(ns[i]);
24075             }
24076         }
24077         return nodes;
24078     },
24079
24080     /**
24081      * Finds the index of the passed node
24082      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24083      * @return {Number} The index of the node or -1
24084      */
24085     indexOf : function(node){
24086         node = this.getNode(node);
24087         if(typeof node.nodeIndex == "number"){
24088             return node.nodeIndex;
24089         }
24090         var ns = this.nodes;
24091         for(var i = 0, len = ns.length; i < len; i++){
24092             if(ns[i] == node){
24093                 return i;
24094             }
24095         }
24096         return -1;
24097     }
24098 });
24099 /*
24100  * Based on:
24101  * Ext JS Library 1.1.1
24102  * Copyright(c) 2006-2007, Ext JS, LLC.
24103  *
24104  * Originally Released Under LGPL - original licence link has changed is not relivant.
24105  *
24106  * Fork - LGPL
24107  * <script type="text/javascript">
24108  */
24109
24110 /**
24111  * @class Roo.JsonView
24112  * @extends Roo.View
24113  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24114 <pre><code>
24115 var view = new Roo.JsonView({
24116     container: "my-element",
24117     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24118     multiSelect: true, 
24119     jsonRoot: "data" 
24120 });
24121
24122 // listen for node click?
24123 view.on("click", function(vw, index, node, e){
24124     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24125 });
24126
24127 // direct load of JSON data
24128 view.load("foobar.php");
24129
24130 // Example from my blog list
24131 var tpl = new Roo.Template(
24132     '&lt;div class="entry"&gt;' +
24133     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24134     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24135     "&lt;/div&gt;&lt;hr /&gt;"
24136 );
24137
24138 var moreView = new Roo.JsonView({
24139     container :  "entry-list", 
24140     template : tpl,
24141     jsonRoot: "posts"
24142 });
24143 moreView.on("beforerender", this.sortEntries, this);
24144 moreView.load({
24145     url: "/blog/get-posts.php",
24146     params: "allposts=true",
24147     text: "Loading Blog Entries..."
24148 });
24149 </code></pre>
24150
24151 * Note: old code is supported with arguments : (container, template, config)
24152
24153
24154  * @constructor
24155  * Create a new JsonView
24156  * 
24157  * @param {Object} config The config object
24158  * 
24159  */
24160 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24161     
24162     
24163     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24164
24165     var um = this.el.getUpdateManager();
24166     um.setRenderer(this);
24167     um.on("update", this.onLoad, this);
24168     um.on("failure", this.onLoadException, this);
24169
24170     /**
24171      * @event beforerender
24172      * Fires before rendering of the downloaded JSON data.
24173      * @param {Roo.JsonView} this
24174      * @param {Object} data The JSON data loaded
24175      */
24176     /**
24177      * @event load
24178      * Fires when data is loaded.
24179      * @param {Roo.JsonView} this
24180      * @param {Object} data The JSON data loaded
24181      * @param {Object} response The raw Connect response object
24182      */
24183     /**
24184      * @event loadexception
24185      * Fires when loading fails.
24186      * @param {Roo.JsonView} this
24187      * @param {Object} response The raw Connect response object
24188      */
24189     this.addEvents({
24190         'beforerender' : true,
24191         'load' : true,
24192         'loadexception' : true
24193     });
24194 };
24195 Roo.extend(Roo.JsonView, Roo.View, {
24196     /**
24197      * @type {String} The root property in the loaded JSON object that contains the data
24198      */
24199     jsonRoot : "",
24200
24201     /**
24202      * Refreshes the view.
24203      */
24204     refresh : function(){
24205         this.clearSelections();
24206         this.el.update("");
24207         var html = [];
24208         var o = this.jsonData;
24209         if(o && o.length > 0){
24210             for(var i = 0, len = o.length; i < len; i++){
24211                 var data = this.prepareData(o[i], i, o);
24212                 html[html.length] = this.tpl.apply(data);
24213             }
24214         }else{
24215             html.push(this.emptyText);
24216         }
24217         this.el.update(html.join(""));
24218         this.nodes = this.el.dom.childNodes;
24219         this.updateIndexes(0);
24220     },
24221
24222     /**
24223      * 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.
24224      * @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:
24225      <pre><code>
24226      view.load({
24227          url: "your-url.php",
24228          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24229          callback: yourFunction,
24230          scope: yourObject, //(optional scope)
24231          discardUrl: false,
24232          nocache: false,
24233          text: "Loading...",
24234          timeout: 30,
24235          scripts: false
24236      });
24237      </code></pre>
24238      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24239      * 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.
24240      * @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}
24241      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24242      * @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.
24243      */
24244     load : function(){
24245         var um = this.el.getUpdateManager();
24246         um.update.apply(um, arguments);
24247     },
24248
24249     render : function(el, response){
24250         this.clearSelections();
24251         this.el.update("");
24252         var o;
24253         try{
24254             o = Roo.util.JSON.decode(response.responseText);
24255             if(this.jsonRoot){
24256                 
24257                 o = o[this.jsonRoot];
24258             }
24259         } catch(e){
24260         }
24261         /**
24262          * The current JSON data or null
24263          */
24264         this.jsonData = o;
24265         this.beforeRender();
24266         this.refresh();
24267     },
24268
24269 /**
24270  * Get the number of records in the current JSON dataset
24271  * @return {Number}
24272  */
24273     getCount : function(){
24274         return this.jsonData ? this.jsonData.length : 0;
24275     },
24276
24277 /**
24278  * Returns the JSON object for the specified node(s)
24279  * @param {HTMLElement/Array} node The node or an array of nodes
24280  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24281  * you get the JSON object for the node
24282  */
24283     getNodeData : function(node){
24284         if(node instanceof Array){
24285             var data = [];
24286             for(var i = 0, len = node.length; i < len; i++){
24287                 data.push(this.getNodeData(node[i]));
24288             }
24289             return data;
24290         }
24291         return this.jsonData[this.indexOf(node)] || null;
24292     },
24293
24294     beforeRender : function(){
24295         this.snapshot = this.jsonData;
24296         if(this.sortInfo){
24297             this.sort.apply(this, this.sortInfo);
24298         }
24299         this.fireEvent("beforerender", this, this.jsonData);
24300     },
24301
24302     onLoad : function(el, o){
24303         this.fireEvent("load", this, this.jsonData, o);
24304     },
24305
24306     onLoadException : function(el, o){
24307         this.fireEvent("loadexception", this, o);
24308     },
24309
24310 /**
24311  * Filter the data by a specific property.
24312  * @param {String} property A property on your JSON objects
24313  * @param {String/RegExp} value Either string that the property values
24314  * should start with, or a RegExp to test against the property
24315  */
24316     filter : function(property, value){
24317         if(this.jsonData){
24318             var data = [];
24319             var ss = this.snapshot;
24320             if(typeof value == "string"){
24321                 var vlen = value.length;
24322                 if(vlen == 0){
24323                     this.clearFilter();
24324                     return;
24325                 }
24326                 value = value.toLowerCase();
24327                 for(var i = 0, len = ss.length; i < len; i++){
24328                     var o = ss[i];
24329                     if(o[property].substr(0, vlen).toLowerCase() == value){
24330                         data.push(o);
24331                     }
24332                 }
24333             } else if(value.exec){ // regex?
24334                 for(var i = 0, len = ss.length; i < len; i++){
24335                     var o = ss[i];
24336                     if(value.test(o[property])){
24337                         data.push(o);
24338                     }
24339                 }
24340             } else{
24341                 return;
24342             }
24343             this.jsonData = data;
24344             this.refresh();
24345         }
24346     },
24347
24348 /**
24349  * Filter by a function. The passed function will be called with each
24350  * object in the current dataset. If the function returns true the value is kept,
24351  * otherwise it is filtered.
24352  * @param {Function} fn
24353  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24354  */
24355     filterBy : function(fn, scope){
24356         if(this.jsonData){
24357             var data = [];
24358             var ss = this.snapshot;
24359             for(var i = 0, len = ss.length; i < len; i++){
24360                 var o = ss[i];
24361                 if(fn.call(scope || this, o)){
24362                     data.push(o);
24363                 }
24364             }
24365             this.jsonData = data;
24366             this.refresh();
24367         }
24368     },
24369
24370 /**
24371  * Clears the current filter.
24372  */
24373     clearFilter : function(){
24374         if(this.snapshot && this.jsonData != this.snapshot){
24375             this.jsonData = this.snapshot;
24376             this.refresh();
24377         }
24378     },
24379
24380
24381 /**
24382  * Sorts the data for this view and refreshes it.
24383  * @param {String} property A property on your JSON objects to sort on
24384  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24385  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24386  */
24387     sort : function(property, dir, sortType){
24388         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24389         if(this.jsonData){
24390             var p = property;
24391             var dsc = dir && dir.toLowerCase() == "desc";
24392             var f = function(o1, o2){
24393                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24394                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24395                 ;
24396                 if(v1 < v2){
24397                     return dsc ? +1 : -1;
24398                 } else if(v1 > v2){
24399                     return dsc ? -1 : +1;
24400                 } else{
24401                     return 0;
24402                 }
24403             };
24404             this.jsonData.sort(f);
24405             this.refresh();
24406             if(this.jsonData != this.snapshot){
24407                 this.snapshot.sort(f);
24408             }
24409         }
24410     }
24411 });/*
24412  * Based on:
24413  * Ext JS Library 1.1.1
24414  * Copyright(c) 2006-2007, Ext JS, LLC.
24415  *
24416  * Originally Released Under LGPL - original licence link has changed is not relivant.
24417  *
24418  * Fork - LGPL
24419  * <script type="text/javascript">
24420  */
24421  
24422
24423 /**
24424  * @class Roo.ColorPalette
24425  * @extends Roo.Component
24426  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24427  * Here's an example of typical usage:
24428  * <pre><code>
24429 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24430 cp.render('my-div');
24431
24432 cp.on('select', function(palette, selColor){
24433     // do something with selColor
24434 });
24435 </code></pre>
24436  * @constructor
24437  * Create a new ColorPalette
24438  * @param {Object} config The config object
24439  */
24440 Roo.ColorPalette = function(config){
24441     Roo.ColorPalette.superclass.constructor.call(this, config);
24442     this.addEvents({
24443         /**
24444              * @event select
24445              * Fires when a color is selected
24446              * @param {ColorPalette} this
24447              * @param {String} color The 6-digit color hex code (without the # symbol)
24448              */
24449         select: true
24450     });
24451
24452     if(this.handler){
24453         this.on("select", this.handler, this.scope, true);
24454     }
24455 };
24456 Roo.extend(Roo.ColorPalette, Roo.Component, {
24457     /**
24458      * @cfg {String} itemCls
24459      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24460      */
24461     itemCls : "x-color-palette",
24462     /**
24463      * @cfg {String} value
24464      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24465      * the hex codes are case-sensitive.
24466      */
24467     value : null,
24468     clickEvent:'click',
24469     // private
24470     ctype: "Roo.ColorPalette",
24471
24472     /**
24473      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24474      */
24475     allowReselect : false,
24476
24477     /**
24478      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24479      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24480      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24481      * of colors with the width setting until the box is symmetrical.</p>
24482      * <p>You can override individual colors if needed:</p>
24483      * <pre><code>
24484 var cp = new Roo.ColorPalette();
24485 cp.colors[0] = "FF0000";  // change the first box to red
24486 </code></pre>
24487
24488 Or you can provide a custom array of your own for complete control:
24489 <pre><code>
24490 var cp = new Roo.ColorPalette();
24491 cp.colors = ["000000", "993300", "333300"];
24492 </code></pre>
24493      * @type Array
24494      */
24495     colors : [
24496         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24497         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24498         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24499         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24500         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24501     ],
24502
24503     // private
24504     onRender : function(container, position){
24505         var t = new Roo.MasterTemplate(
24506             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24507         );
24508         var c = this.colors;
24509         for(var i = 0, len = c.length; i < len; i++){
24510             t.add([c[i]]);
24511         }
24512         var el = document.createElement("div");
24513         el.className = this.itemCls;
24514         t.overwrite(el);
24515         container.dom.insertBefore(el, position);
24516         this.el = Roo.get(el);
24517         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24518         if(this.clickEvent != 'click'){
24519             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24520         }
24521     },
24522
24523     // private
24524     afterRender : function(){
24525         Roo.ColorPalette.superclass.afterRender.call(this);
24526         if(this.value){
24527             var s = this.value;
24528             this.value = null;
24529             this.select(s);
24530         }
24531     },
24532
24533     // private
24534     handleClick : function(e, t){
24535         e.preventDefault();
24536         if(!this.disabled){
24537             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24538             this.select(c.toUpperCase());
24539         }
24540     },
24541
24542     /**
24543      * Selects the specified color in the palette (fires the select event)
24544      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24545      */
24546     select : function(color){
24547         color = color.replace("#", "");
24548         if(color != this.value || this.allowReselect){
24549             var el = this.el;
24550             if(this.value){
24551                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24552             }
24553             el.child("a.color-"+color).addClass("x-color-palette-sel");
24554             this.value = color;
24555             this.fireEvent("select", this, color);
24556         }
24557     }
24558 });/*
24559  * Based on:
24560  * Ext JS Library 1.1.1
24561  * Copyright(c) 2006-2007, Ext JS, LLC.
24562  *
24563  * Originally Released Under LGPL - original licence link has changed is not relivant.
24564  *
24565  * Fork - LGPL
24566  * <script type="text/javascript">
24567  */
24568  
24569 /**
24570  * @class Roo.DatePicker
24571  * @extends Roo.Component
24572  * Simple date picker class.
24573  * @constructor
24574  * Create a new DatePicker
24575  * @param {Object} config The config object
24576  */
24577 Roo.DatePicker = function(config){
24578     Roo.DatePicker.superclass.constructor.call(this, config);
24579
24580     this.value = config && config.value ?
24581                  config.value.clearTime() : new Date().clearTime();
24582
24583     this.addEvents({
24584         /**
24585              * @event select
24586              * Fires when a date is selected
24587              * @param {DatePicker} this
24588              * @param {Date} date The selected date
24589              */
24590         'select': true,
24591         /**
24592              * @event monthchange
24593              * Fires when the displayed month changes 
24594              * @param {DatePicker} this
24595              * @param {Date} date The selected month
24596              */
24597         'monthchange': true
24598     });
24599
24600     if(this.handler){
24601         this.on("select", this.handler,  this.scope || this);
24602     }
24603     // build the disabledDatesRE
24604     if(!this.disabledDatesRE && this.disabledDates){
24605         var dd = this.disabledDates;
24606         var re = "(?:";
24607         for(var i = 0; i < dd.length; i++){
24608             re += dd[i];
24609             if(i != dd.length-1) re += "|";
24610         }
24611         this.disabledDatesRE = new RegExp(re + ")");
24612     }
24613 };
24614
24615 Roo.extend(Roo.DatePicker, Roo.Component, {
24616     /**
24617      * @cfg {String} todayText
24618      * The text to display on the button that selects the current date (defaults to "Today")
24619      */
24620     todayText : "Today",
24621     /**
24622      * @cfg {String} okText
24623      * The text to display on the ok button
24624      */
24625     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24626     /**
24627      * @cfg {String} cancelText
24628      * The text to display on the cancel button
24629      */
24630     cancelText : "Cancel",
24631     /**
24632      * @cfg {String} todayTip
24633      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24634      */
24635     todayTip : "{0} (Spacebar)",
24636     /**
24637      * @cfg {Date} minDate
24638      * Minimum allowable date (JavaScript date object, defaults to null)
24639      */
24640     minDate : null,
24641     /**
24642      * @cfg {Date} maxDate
24643      * Maximum allowable date (JavaScript date object, defaults to null)
24644      */
24645     maxDate : null,
24646     /**
24647      * @cfg {String} minText
24648      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24649      */
24650     minText : "This date is before the minimum date",
24651     /**
24652      * @cfg {String} maxText
24653      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24654      */
24655     maxText : "This date is after the maximum date",
24656     /**
24657      * @cfg {String} format
24658      * The default date format string which can be overriden for localization support.  The format must be
24659      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24660      */
24661     format : "m/d/y",
24662     /**
24663      * @cfg {Array} disabledDays
24664      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24665      */
24666     disabledDays : null,
24667     /**
24668      * @cfg {String} disabledDaysText
24669      * The tooltip to display when the date falls on a disabled day (defaults to "")
24670      */
24671     disabledDaysText : "",
24672     /**
24673      * @cfg {RegExp} disabledDatesRE
24674      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24675      */
24676     disabledDatesRE : null,
24677     /**
24678      * @cfg {String} disabledDatesText
24679      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24680      */
24681     disabledDatesText : "",
24682     /**
24683      * @cfg {Boolean} constrainToViewport
24684      * True to constrain the date picker to the viewport (defaults to true)
24685      */
24686     constrainToViewport : true,
24687     /**
24688      * @cfg {Array} monthNames
24689      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24690      */
24691     monthNames : Date.monthNames,
24692     /**
24693      * @cfg {Array} dayNames
24694      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24695      */
24696     dayNames : Date.dayNames,
24697     /**
24698      * @cfg {String} nextText
24699      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24700      */
24701     nextText: 'Next Month (Control+Right)',
24702     /**
24703      * @cfg {String} prevText
24704      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24705      */
24706     prevText: 'Previous Month (Control+Left)',
24707     /**
24708      * @cfg {String} monthYearText
24709      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24710      */
24711     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24712     /**
24713      * @cfg {Number} startDay
24714      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24715      */
24716     startDay : 0,
24717     /**
24718      * @cfg {Bool} showClear
24719      * Show a clear button (usefull for date form elements that can be blank.)
24720      */
24721     
24722     showClear: false,
24723     
24724     /**
24725      * Sets the value of the date field
24726      * @param {Date} value The date to set
24727      */
24728     setValue : function(value){
24729         var old = this.value;
24730         this.value = value.clearTime(true);
24731         if(this.el){
24732             this.update(this.value);
24733         }
24734     },
24735
24736     /**
24737      * Gets the current selected value of the date field
24738      * @return {Date} The selected date
24739      */
24740     getValue : function(){
24741         return this.value;
24742     },
24743
24744     // private
24745     focus : function(){
24746         if(this.el){
24747             this.update(this.activeDate);
24748         }
24749     },
24750
24751     // private
24752     onRender : function(container, position){
24753         var m = [
24754              '<table cellspacing="0">',
24755                 '<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>',
24756                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24757         var dn = this.dayNames;
24758         for(var i = 0; i < 7; i++){
24759             var d = this.startDay+i;
24760             if(d > 6){
24761                 d = d-7;
24762             }
24763             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24764         }
24765         m[m.length] = "</tr></thead><tbody><tr>";
24766         for(var i = 0; i < 42; i++) {
24767             if(i % 7 == 0 && i != 0){
24768                 m[m.length] = "</tr><tr>";
24769             }
24770             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24771         }
24772         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24773             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24774
24775         var el = document.createElement("div");
24776         el.className = "x-date-picker";
24777         el.innerHTML = m.join("");
24778
24779         container.dom.insertBefore(el, position);
24780
24781         this.el = Roo.get(el);
24782         this.eventEl = Roo.get(el.firstChild);
24783
24784         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24785             handler: this.showPrevMonth,
24786             scope: this,
24787             preventDefault:true,
24788             stopDefault:true
24789         });
24790
24791         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24792             handler: this.showNextMonth,
24793             scope: this,
24794             preventDefault:true,
24795             stopDefault:true
24796         });
24797
24798         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24799
24800         this.monthPicker = this.el.down('div.x-date-mp');
24801         this.monthPicker.enableDisplayMode('block');
24802         
24803         var kn = new Roo.KeyNav(this.eventEl, {
24804             "left" : function(e){
24805                 e.ctrlKey ?
24806                     this.showPrevMonth() :
24807                     this.update(this.activeDate.add("d", -1));
24808             },
24809
24810             "right" : function(e){
24811                 e.ctrlKey ?
24812                     this.showNextMonth() :
24813                     this.update(this.activeDate.add("d", 1));
24814             },
24815
24816             "up" : function(e){
24817                 e.ctrlKey ?
24818                     this.showNextYear() :
24819                     this.update(this.activeDate.add("d", -7));
24820             },
24821
24822             "down" : function(e){
24823                 e.ctrlKey ?
24824                     this.showPrevYear() :
24825                     this.update(this.activeDate.add("d", 7));
24826             },
24827
24828             "pageUp" : function(e){
24829                 this.showNextMonth();
24830             },
24831
24832             "pageDown" : function(e){
24833                 this.showPrevMonth();
24834             },
24835
24836             "enter" : function(e){
24837                 e.stopPropagation();
24838                 return true;
24839             },
24840
24841             scope : this
24842         });
24843
24844         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24845
24846         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24847
24848         this.el.unselectable();
24849         
24850         this.cells = this.el.select("table.x-date-inner tbody td");
24851         this.textNodes = this.el.query("table.x-date-inner tbody span");
24852
24853         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24854             text: "&#160;",
24855             tooltip: this.monthYearText
24856         });
24857
24858         this.mbtn.on('click', this.showMonthPicker, this);
24859         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24860
24861
24862         var today = (new Date()).dateFormat(this.format);
24863         
24864         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24865         if (this.showClear) {
24866             baseTb.add( new Roo.Toolbar.Fill());
24867         }
24868         baseTb.add({
24869             text: String.format(this.todayText, today),
24870             tooltip: String.format(this.todayTip, today),
24871             handler: this.selectToday,
24872             scope: this
24873         });
24874         
24875         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24876             
24877         //});
24878         if (this.showClear) {
24879             
24880             baseTb.add( new Roo.Toolbar.Fill());
24881             baseTb.add({
24882                 text: '&#160;',
24883                 cls: 'x-btn-icon x-btn-clear',
24884                 handler: function() {
24885                     //this.value = '';
24886                     this.fireEvent("select", this, '');
24887                 },
24888                 scope: this
24889             });
24890         }
24891         
24892         
24893         if(Roo.isIE){
24894             this.el.repaint();
24895         }
24896         this.update(this.value);
24897     },
24898
24899     createMonthPicker : function(){
24900         if(!this.monthPicker.dom.firstChild){
24901             var buf = ['<table border="0" cellspacing="0">'];
24902             for(var i = 0; i < 6; i++){
24903                 buf.push(
24904                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24905                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24906                     i == 0 ?
24907                     '<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>' :
24908                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24909                 );
24910             }
24911             buf.push(
24912                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24913                     this.okText,
24914                     '</button><button type="button" class="x-date-mp-cancel">',
24915                     this.cancelText,
24916                     '</button></td></tr>',
24917                 '</table>'
24918             );
24919             this.monthPicker.update(buf.join(''));
24920             this.monthPicker.on('click', this.onMonthClick, this);
24921             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24922
24923             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24924             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24925
24926             this.mpMonths.each(function(m, a, i){
24927                 i += 1;
24928                 if((i%2) == 0){
24929                     m.dom.xmonth = 5 + Math.round(i * .5);
24930                 }else{
24931                     m.dom.xmonth = Math.round((i-1) * .5);
24932                 }
24933             });
24934         }
24935     },
24936
24937     showMonthPicker : function(){
24938         this.createMonthPicker();
24939         var size = this.el.getSize();
24940         this.monthPicker.setSize(size);
24941         this.monthPicker.child('table').setSize(size);
24942
24943         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24944         this.updateMPMonth(this.mpSelMonth);
24945         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24946         this.updateMPYear(this.mpSelYear);
24947
24948         this.monthPicker.slideIn('t', {duration:.2});
24949     },
24950
24951     updateMPYear : function(y){
24952         this.mpyear = y;
24953         var ys = this.mpYears.elements;
24954         for(var i = 1; i <= 10; i++){
24955             var td = ys[i-1], y2;
24956             if((i%2) == 0){
24957                 y2 = y + Math.round(i * .5);
24958                 td.firstChild.innerHTML = y2;
24959                 td.xyear = y2;
24960             }else{
24961                 y2 = y - (5-Math.round(i * .5));
24962                 td.firstChild.innerHTML = y2;
24963                 td.xyear = y2;
24964             }
24965             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24966         }
24967     },
24968
24969     updateMPMonth : function(sm){
24970         this.mpMonths.each(function(m, a, i){
24971             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24972         });
24973     },
24974
24975     selectMPMonth: function(m){
24976         
24977     },
24978
24979     onMonthClick : function(e, t){
24980         e.stopEvent();
24981         var el = new Roo.Element(t), pn;
24982         if(el.is('button.x-date-mp-cancel')){
24983             this.hideMonthPicker();
24984         }
24985         else if(el.is('button.x-date-mp-ok')){
24986             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24987             this.hideMonthPicker();
24988         }
24989         else if(pn = el.up('td.x-date-mp-month', 2)){
24990             this.mpMonths.removeClass('x-date-mp-sel');
24991             pn.addClass('x-date-mp-sel');
24992             this.mpSelMonth = pn.dom.xmonth;
24993         }
24994         else if(pn = el.up('td.x-date-mp-year', 2)){
24995             this.mpYears.removeClass('x-date-mp-sel');
24996             pn.addClass('x-date-mp-sel');
24997             this.mpSelYear = pn.dom.xyear;
24998         }
24999         else if(el.is('a.x-date-mp-prev')){
25000             this.updateMPYear(this.mpyear-10);
25001         }
25002         else if(el.is('a.x-date-mp-next')){
25003             this.updateMPYear(this.mpyear+10);
25004         }
25005     },
25006
25007     onMonthDblClick : function(e, t){
25008         e.stopEvent();
25009         var el = new Roo.Element(t), pn;
25010         if(pn = el.up('td.x-date-mp-month', 2)){
25011             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25012             this.hideMonthPicker();
25013         }
25014         else if(pn = el.up('td.x-date-mp-year', 2)){
25015             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25016             this.hideMonthPicker();
25017         }
25018     },
25019
25020     hideMonthPicker : function(disableAnim){
25021         if(this.monthPicker){
25022             if(disableAnim === true){
25023                 this.monthPicker.hide();
25024             }else{
25025                 this.monthPicker.slideOut('t', {duration:.2});
25026             }
25027         }
25028     },
25029
25030     // private
25031     showPrevMonth : function(e){
25032         this.update(this.activeDate.add("mo", -1));
25033     },
25034
25035     // private
25036     showNextMonth : function(e){
25037         this.update(this.activeDate.add("mo", 1));
25038     },
25039
25040     // private
25041     showPrevYear : function(){
25042         this.update(this.activeDate.add("y", -1));
25043     },
25044
25045     // private
25046     showNextYear : function(){
25047         this.update(this.activeDate.add("y", 1));
25048     },
25049
25050     // private
25051     handleMouseWheel : function(e){
25052         var delta = e.getWheelDelta();
25053         if(delta > 0){
25054             this.showPrevMonth();
25055             e.stopEvent();
25056         } else if(delta < 0){
25057             this.showNextMonth();
25058             e.stopEvent();
25059         }
25060     },
25061
25062     // private
25063     handleDateClick : function(e, t){
25064         e.stopEvent();
25065         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25066             this.setValue(new Date(t.dateValue));
25067             this.fireEvent("select", this, this.value);
25068         }
25069     },
25070
25071     // private
25072     selectToday : function(){
25073         this.setValue(new Date().clearTime());
25074         this.fireEvent("select", this, this.value);
25075     },
25076
25077     // private
25078     update : function(date)
25079     {
25080         var vd = this.activeDate;
25081         this.activeDate = date;
25082         if(vd && this.el){
25083             var t = date.getTime();
25084             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25085                 this.cells.removeClass("x-date-selected");
25086                 this.cells.each(function(c){
25087                    if(c.dom.firstChild.dateValue == t){
25088                        c.addClass("x-date-selected");
25089                        setTimeout(function(){
25090                             try{c.dom.firstChild.focus();}catch(e){}
25091                        }, 50);
25092                        return false;
25093                    }
25094                 });
25095                 return;
25096             }
25097         }
25098         
25099         var days = date.getDaysInMonth();
25100         var firstOfMonth = date.getFirstDateOfMonth();
25101         var startingPos = firstOfMonth.getDay()-this.startDay;
25102
25103         if(startingPos <= this.startDay){
25104             startingPos += 7;
25105         }
25106
25107         var pm = date.add("mo", -1);
25108         var prevStart = pm.getDaysInMonth()-startingPos;
25109
25110         var cells = this.cells.elements;
25111         var textEls = this.textNodes;
25112         days += startingPos;
25113
25114         // convert everything to numbers so it's fast
25115         var day = 86400000;
25116         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25117         var today = new Date().clearTime().getTime();
25118         var sel = date.clearTime().getTime();
25119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25121         var ddMatch = this.disabledDatesRE;
25122         var ddText = this.disabledDatesText;
25123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25124         var ddaysText = this.disabledDaysText;
25125         var format = this.format;
25126
25127         var setCellClass = function(cal, cell){
25128             cell.title = "";
25129             var t = d.getTime();
25130             cell.firstChild.dateValue = t;
25131             if(t == today){
25132                 cell.className += " x-date-today";
25133                 cell.title = cal.todayText;
25134             }
25135             if(t == sel){
25136                 cell.className += " x-date-selected";
25137                 setTimeout(function(){
25138                     try{cell.firstChild.focus();}catch(e){}
25139                 }, 50);
25140             }
25141             // disabling
25142             if(t < min) {
25143                 cell.className = " x-date-disabled";
25144                 cell.title = cal.minText;
25145                 return;
25146             }
25147             if(t > max) {
25148                 cell.className = " x-date-disabled";
25149                 cell.title = cal.maxText;
25150                 return;
25151             }
25152             if(ddays){
25153                 if(ddays.indexOf(d.getDay()) != -1){
25154                     cell.title = ddaysText;
25155                     cell.className = " x-date-disabled";
25156                 }
25157             }
25158             if(ddMatch && format){
25159                 var fvalue = d.dateFormat(format);
25160                 if(ddMatch.test(fvalue)){
25161                     cell.title = ddText.replace("%0", fvalue);
25162                     cell.className = " x-date-disabled";
25163                 }
25164             }
25165         };
25166
25167         var i = 0;
25168         for(; i < startingPos; i++) {
25169             textEls[i].innerHTML = (++prevStart);
25170             d.setDate(d.getDate()+1);
25171             cells[i].className = "x-date-prevday";
25172             setCellClass(this, cells[i]);
25173         }
25174         for(; i < days; i++){
25175             intDay = i - startingPos + 1;
25176             textEls[i].innerHTML = (intDay);
25177             d.setDate(d.getDate()+1);
25178             cells[i].className = "x-date-active";
25179             setCellClass(this, cells[i]);
25180         }
25181         var extraDays = 0;
25182         for(; i < 42; i++) {
25183              textEls[i].innerHTML = (++extraDays);
25184              d.setDate(d.getDate()+1);
25185              cells[i].className = "x-date-nextday";
25186              setCellClass(this, cells[i]);
25187         }
25188
25189         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25190         this.fireEvent('monthchange', this, date);
25191         
25192         if(!this.internalRender){
25193             var main = this.el.dom.firstChild;
25194             var w = main.offsetWidth;
25195             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25196             Roo.fly(main).setWidth(w);
25197             this.internalRender = true;
25198             // opera does not respect the auto grow header center column
25199             // then, after it gets a width opera refuses to recalculate
25200             // without a second pass
25201             if(Roo.isOpera && !this.secondPass){
25202                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25203                 this.secondPass = true;
25204                 this.update.defer(10, this, [date]);
25205             }
25206         }
25207         
25208         
25209     }
25210 });        /*
25211  * Based on:
25212  * Ext JS Library 1.1.1
25213  * Copyright(c) 2006-2007, Ext JS, LLC.
25214  *
25215  * Originally Released Under LGPL - original licence link has changed is not relivant.
25216  *
25217  * Fork - LGPL
25218  * <script type="text/javascript">
25219  */
25220 /**
25221  * @class Roo.TabPanel
25222  * @extends Roo.util.Observable
25223  * A lightweight tab container.
25224  * <br><br>
25225  * Usage:
25226  * <pre><code>
25227 // basic tabs 1, built from existing content
25228 var tabs = new Roo.TabPanel("tabs1");
25229 tabs.addTab("script", "View Script");
25230 tabs.addTab("markup", "View Markup");
25231 tabs.activate("script");
25232
25233 // more advanced tabs, built from javascript
25234 var jtabs = new Roo.TabPanel("jtabs");
25235 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25236
25237 // set up the UpdateManager
25238 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25239 var updater = tab2.getUpdateManager();
25240 updater.setDefaultUrl("ajax1.htm");
25241 tab2.on('activate', updater.refresh, updater, true);
25242
25243 // Use setUrl for Ajax loading
25244 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25245 tab3.setUrl("ajax2.htm", null, true);
25246
25247 // Disabled tab
25248 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25249 tab4.disable();
25250
25251 jtabs.activate("jtabs-1");
25252  * </code></pre>
25253  * @constructor
25254  * Create a new TabPanel.
25255  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25256  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25257  */
25258 Roo.TabPanel = function(container, config){
25259     /**
25260     * The container element for this TabPanel.
25261     * @type Roo.Element
25262     */
25263     this.el = Roo.get(container, true);
25264     if(config){
25265         if(typeof config == "boolean"){
25266             this.tabPosition = config ? "bottom" : "top";
25267         }else{
25268             Roo.apply(this, config);
25269         }
25270     }
25271     if(this.tabPosition == "bottom"){
25272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25273         this.el.addClass("x-tabs-bottom");
25274     }
25275     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25276     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25277     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25278     if(Roo.isIE){
25279         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25280     }
25281     if(this.tabPosition != "bottom"){
25282         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25283          * @type Roo.Element
25284          */
25285         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25286         this.el.addClass("x-tabs-top");
25287     }
25288     this.items = [];
25289
25290     this.bodyEl.setStyle("position", "relative");
25291
25292     this.active = null;
25293     this.activateDelegate = this.activate.createDelegate(this);
25294
25295     this.addEvents({
25296         /**
25297          * @event tabchange
25298          * Fires when the active tab changes
25299          * @param {Roo.TabPanel} this
25300          * @param {Roo.TabPanelItem} activePanel The new active tab
25301          */
25302         "tabchange": true,
25303         /**
25304          * @event beforetabchange
25305          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25306          * @param {Roo.TabPanel} this
25307          * @param {Object} e Set cancel to true on this object to cancel the tab change
25308          * @param {Roo.TabPanelItem} tab The tab being changed to
25309          */
25310         "beforetabchange" : true
25311     });
25312
25313     Roo.EventManager.onWindowResize(this.onResize, this);
25314     this.cpad = this.el.getPadding("lr");
25315     this.hiddenCount = 0;
25316
25317
25318     // toolbar on the tabbar support...
25319     if (this.toolbar) {
25320         var tcfg = this.toolbar;
25321         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25322         this.toolbar = new Roo.Toolbar(tcfg);
25323         if (Roo.isSafari) {
25324             var tbl = tcfg.container.child('table', true);
25325             tbl.setAttribute('width', '100%');
25326         }
25327         
25328     }
25329    
25330
25331
25332     Roo.TabPanel.superclass.constructor.call(this);
25333 };
25334
25335 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25336     /*
25337      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25338      */
25339     tabPosition : "top",
25340     /*
25341      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25342      */
25343     currentTabWidth : 0,
25344     /*
25345      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25346      */
25347     minTabWidth : 40,
25348     /*
25349      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25350      */
25351     maxTabWidth : 250,
25352     /*
25353      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25354      */
25355     preferredTabWidth : 175,
25356     /*
25357      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25358      */
25359     resizeTabs : false,
25360     /*
25361      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25362      */
25363     monitorResize : true,
25364     /*
25365      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25366      */
25367     toolbar : false,
25368
25369     /**
25370      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25371      * @param {String} id The id of the div to use <b>or create</b>
25372      * @param {String} text The text for the tab
25373      * @param {String} content (optional) Content to put in the TabPanelItem body
25374      * @param {Boolean} closable (optional) True to create a close icon on the tab
25375      * @return {Roo.TabPanelItem} The created TabPanelItem
25376      */
25377     addTab : function(id, text, content, closable){
25378         var item = new Roo.TabPanelItem(this, id, text, closable);
25379         this.addTabItem(item);
25380         if(content){
25381             item.setContent(content);
25382         }
25383         return item;
25384     },
25385
25386     /**
25387      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25388      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25389      * @return {Roo.TabPanelItem}
25390      */
25391     getTab : function(id){
25392         return this.items[id];
25393     },
25394
25395     /**
25396      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25397      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25398      */
25399     hideTab : function(id){
25400         var t = this.items[id];
25401         if(!t.isHidden()){
25402            t.setHidden(true);
25403            this.hiddenCount++;
25404            this.autoSizeTabs();
25405         }
25406     },
25407
25408     /**
25409      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25410      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25411      */
25412     unhideTab : function(id){
25413         var t = this.items[id];
25414         if(t.isHidden()){
25415            t.setHidden(false);
25416            this.hiddenCount--;
25417            this.autoSizeTabs();
25418         }
25419     },
25420
25421     /**
25422      * Adds an existing {@link Roo.TabPanelItem}.
25423      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25424      */
25425     addTabItem : function(item){
25426         this.items[item.id] = item;
25427         this.items.push(item);
25428         if(this.resizeTabs){
25429            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25430            this.autoSizeTabs();
25431         }else{
25432             item.autoSize();
25433         }
25434     },
25435
25436     /**
25437      * Removes a {@link Roo.TabPanelItem}.
25438      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25439      */
25440     removeTab : function(id){
25441         var items = this.items;
25442         var tab = items[id];
25443         if(!tab) { return; }
25444         var index = items.indexOf(tab);
25445         if(this.active == tab && items.length > 1){
25446             var newTab = this.getNextAvailable(index);
25447             if(newTab) {
25448                 newTab.activate();
25449             }
25450         }
25451         this.stripEl.dom.removeChild(tab.pnode.dom);
25452         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25453             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25454         }
25455         items.splice(index, 1);
25456         delete this.items[tab.id];
25457         tab.fireEvent("close", tab);
25458         tab.purgeListeners();
25459         this.autoSizeTabs();
25460     },
25461
25462     getNextAvailable : function(start){
25463         var items = this.items;
25464         var index = start;
25465         // look for a next tab that will slide over to
25466         // replace the one being removed
25467         while(index < items.length){
25468             var item = items[++index];
25469             if(item && !item.isHidden()){
25470                 return item;
25471             }
25472         }
25473         // if one isn't found select the previous tab (on the left)
25474         index = start;
25475         while(index >= 0){
25476             var item = items[--index];
25477             if(item && !item.isHidden()){
25478                 return item;
25479             }
25480         }
25481         return null;
25482     },
25483
25484     /**
25485      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25486      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25487      */
25488     disableTab : function(id){
25489         var tab = this.items[id];
25490         if(tab && this.active != tab){
25491             tab.disable();
25492         }
25493     },
25494
25495     /**
25496      * Enables a {@link Roo.TabPanelItem} that is disabled.
25497      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25498      */
25499     enableTab : function(id){
25500         var tab = this.items[id];
25501         tab.enable();
25502     },
25503
25504     /**
25505      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25506      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25507      * @return {Roo.TabPanelItem} The TabPanelItem.
25508      */
25509     activate : function(id){
25510         var tab = this.items[id];
25511         if(!tab){
25512             return null;
25513         }
25514         if(tab == this.active || tab.disabled){
25515             return tab;
25516         }
25517         var e = {};
25518         this.fireEvent("beforetabchange", this, e, tab);
25519         if(e.cancel !== true && !tab.disabled){
25520             if(this.active){
25521                 this.active.hide();
25522             }
25523             this.active = this.items[id];
25524             this.active.show();
25525             this.fireEvent("tabchange", this, this.active);
25526         }
25527         return tab;
25528     },
25529
25530     /**
25531      * Gets the active {@link Roo.TabPanelItem}.
25532      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25533      */
25534     getActiveTab : function(){
25535         return this.active;
25536     },
25537
25538     /**
25539      * Updates the tab body element to fit the height of the container element
25540      * for overflow scrolling
25541      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25542      */
25543     syncHeight : function(targetHeight){
25544         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25545         var bm = this.bodyEl.getMargins();
25546         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25547         this.bodyEl.setHeight(newHeight);
25548         return newHeight;
25549     },
25550
25551     onResize : function(){
25552         if(this.monitorResize){
25553             this.autoSizeTabs();
25554         }
25555     },
25556
25557     /**
25558      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25559      */
25560     beginUpdate : function(){
25561         this.updating = true;
25562     },
25563
25564     /**
25565      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25566      */
25567     endUpdate : function(){
25568         this.updating = false;
25569         this.autoSizeTabs();
25570     },
25571
25572     /**
25573      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25574      */
25575     autoSizeTabs : function(){
25576         var count = this.items.length;
25577         var vcount = count - this.hiddenCount;
25578         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25579         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25580         var availWidth = Math.floor(w / vcount);
25581         var b = this.stripBody;
25582         if(b.getWidth() > w){
25583             var tabs = this.items;
25584             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25585             if(availWidth < this.minTabWidth){
25586                 /*if(!this.sleft){    // incomplete scrolling code
25587                     this.createScrollButtons();
25588                 }
25589                 this.showScroll();
25590                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25591             }
25592         }else{
25593             if(this.currentTabWidth < this.preferredTabWidth){
25594                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25595             }
25596         }
25597     },
25598
25599     /**
25600      * Returns the number of tabs in this TabPanel.
25601      * @return {Number}
25602      */
25603      getCount : function(){
25604          return this.items.length;
25605      },
25606
25607     /**
25608      * Resizes all the tabs to the passed width
25609      * @param {Number} The new width
25610      */
25611     setTabWidth : function(width){
25612         this.currentTabWidth = width;
25613         for(var i = 0, len = this.items.length; i < len; i++) {
25614                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25615         }
25616     },
25617
25618     /**
25619      * Destroys this TabPanel
25620      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25621      */
25622     destroy : function(removeEl){
25623         Roo.EventManager.removeResizeListener(this.onResize, this);
25624         for(var i = 0, len = this.items.length; i < len; i++){
25625             this.items[i].purgeListeners();
25626         }
25627         if(removeEl === true){
25628             this.el.update("");
25629             this.el.remove();
25630         }
25631     }
25632 });
25633
25634 /**
25635  * @class Roo.TabPanelItem
25636  * @extends Roo.util.Observable
25637  * Represents an individual item (tab plus body) in a TabPanel.
25638  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25639  * @param {String} id The id of this TabPanelItem
25640  * @param {String} text The text for the tab of this TabPanelItem
25641  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25642  */
25643 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25644     /**
25645      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25646      * @type Roo.TabPanel
25647      */
25648     this.tabPanel = tabPanel;
25649     /**
25650      * The id for this TabPanelItem
25651      * @type String
25652      */
25653     this.id = id;
25654     /** @private */
25655     this.disabled = false;
25656     /** @private */
25657     this.text = text;
25658     /** @private */
25659     this.loaded = false;
25660     this.closable = closable;
25661
25662     /**
25663      * The body element for this TabPanelItem.
25664      * @type Roo.Element
25665      */
25666     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25667     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25668     this.bodyEl.setStyle("display", "block");
25669     this.bodyEl.setStyle("zoom", "1");
25670     this.hideAction();
25671
25672     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25673     /** @private */
25674     this.el = Roo.get(els.el, true);
25675     this.inner = Roo.get(els.inner, true);
25676     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25677     this.pnode = Roo.get(els.el.parentNode, true);
25678     this.el.on("mousedown", this.onTabMouseDown, this);
25679     this.el.on("click", this.onTabClick, this);
25680     /** @private */
25681     if(closable){
25682         var c = Roo.get(els.close, true);
25683         c.dom.title = this.closeText;
25684         c.addClassOnOver("close-over");
25685         c.on("click", this.closeClick, this);
25686      }
25687
25688     this.addEvents({
25689          /**
25690          * @event activate
25691          * Fires when this tab becomes the active tab.
25692          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25693          * @param {Roo.TabPanelItem} this
25694          */
25695         "activate": true,
25696         /**
25697          * @event beforeclose
25698          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25699          * @param {Roo.TabPanelItem} this
25700          * @param {Object} e Set cancel to true on this object to cancel the close.
25701          */
25702         "beforeclose": true,
25703         /**
25704          * @event close
25705          * Fires when this tab is closed.
25706          * @param {Roo.TabPanelItem} this
25707          */
25708          "close": true,
25709         /**
25710          * @event deactivate
25711          * Fires when this tab is no longer the active tab.
25712          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25713          * @param {Roo.TabPanelItem} this
25714          */
25715          "deactivate" : true
25716     });
25717     this.hidden = false;
25718
25719     Roo.TabPanelItem.superclass.constructor.call(this);
25720 };
25721
25722 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25723     purgeListeners : function(){
25724        Roo.util.Observable.prototype.purgeListeners.call(this);
25725        this.el.removeAllListeners();
25726     },
25727     /**
25728      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25729      */
25730     show : function(){
25731         this.pnode.addClass("on");
25732         this.showAction();
25733         if(Roo.isOpera){
25734             this.tabPanel.stripWrap.repaint();
25735         }
25736         this.fireEvent("activate", this.tabPanel, this);
25737     },
25738
25739     /**
25740      * Returns true if this tab is the active tab.
25741      * @return {Boolean}
25742      */
25743     isActive : function(){
25744         return this.tabPanel.getActiveTab() == this;
25745     },
25746
25747     /**
25748      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25749      */
25750     hide : function(){
25751         this.pnode.removeClass("on");
25752         this.hideAction();
25753         this.fireEvent("deactivate", this.tabPanel, this);
25754     },
25755
25756     hideAction : function(){
25757         this.bodyEl.hide();
25758         this.bodyEl.setStyle("position", "absolute");
25759         this.bodyEl.setLeft("-20000px");
25760         this.bodyEl.setTop("-20000px");
25761     },
25762
25763     showAction : function(){
25764         this.bodyEl.setStyle("position", "relative");
25765         this.bodyEl.setTop("");
25766         this.bodyEl.setLeft("");
25767         this.bodyEl.show();
25768     },
25769
25770     /**
25771      * Set the tooltip for the tab.
25772      * @param {String} tooltip The tab's tooltip
25773      */
25774     setTooltip : function(text){
25775         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25776             this.textEl.dom.qtip = text;
25777             this.textEl.dom.removeAttribute('title');
25778         }else{
25779             this.textEl.dom.title = text;
25780         }
25781     },
25782
25783     onTabClick : function(e){
25784         e.preventDefault();
25785         this.tabPanel.activate(this.id);
25786     },
25787
25788     onTabMouseDown : function(e){
25789         e.preventDefault();
25790         this.tabPanel.activate(this.id);
25791     },
25792
25793     getWidth : function(){
25794         return this.inner.getWidth();
25795     },
25796
25797     setWidth : function(width){
25798         var iwidth = width - this.pnode.getPadding("lr");
25799         this.inner.setWidth(iwidth);
25800         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25801         this.pnode.setWidth(width);
25802     },
25803
25804     /**
25805      * Show or hide the tab
25806      * @param {Boolean} hidden True to hide or false to show.
25807      */
25808     setHidden : function(hidden){
25809         this.hidden = hidden;
25810         this.pnode.setStyle("display", hidden ? "none" : "");
25811     },
25812
25813     /**
25814      * Returns true if this tab is "hidden"
25815      * @return {Boolean}
25816      */
25817     isHidden : function(){
25818         return this.hidden;
25819     },
25820
25821     /**
25822      * Returns the text for this tab
25823      * @return {String}
25824      */
25825     getText : function(){
25826         return this.text;
25827     },
25828
25829     autoSize : function(){
25830         //this.el.beginMeasure();
25831         this.textEl.setWidth(1);
25832         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25833         //this.el.endMeasure();
25834     },
25835
25836     /**
25837      * Sets the text for the tab (Note: this also sets the tooltip text)
25838      * @param {String} text The tab's text and tooltip
25839      */
25840     setText : function(text){
25841         this.text = text;
25842         this.textEl.update(text);
25843         this.setTooltip(text);
25844         if(!this.tabPanel.resizeTabs){
25845             this.autoSize();
25846         }
25847     },
25848     /**
25849      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25850      */
25851     activate : function(){
25852         this.tabPanel.activate(this.id);
25853     },
25854
25855     /**
25856      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25857      */
25858     disable : function(){
25859         if(this.tabPanel.active != this){
25860             this.disabled = true;
25861             this.pnode.addClass("disabled");
25862         }
25863     },
25864
25865     /**
25866      * Enables this TabPanelItem if it was previously disabled.
25867      */
25868     enable : function(){
25869         this.disabled = false;
25870         this.pnode.removeClass("disabled");
25871     },
25872
25873     /**
25874      * Sets the content for this TabPanelItem.
25875      * @param {String} content The content
25876      * @param {Boolean} loadScripts true to look for and load scripts
25877      */
25878     setContent : function(content, loadScripts){
25879         this.bodyEl.update(content, loadScripts);
25880     },
25881
25882     /**
25883      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25884      * @return {Roo.UpdateManager} The UpdateManager
25885      */
25886     getUpdateManager : function(){
25887         return this.bodyEl.getUpdateManager();
25888     },
25889
25890     /**
25891      * Set a URL to be used to load the content for this TabPanelItem.
25892      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25893      * @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)
25894      * @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)
25895      * @return {Roo.UpdateManager} The UpdateManager
25896      */
25897     setUrl : function(url, params, loadOnce){
25898         if(this.refreshDelegate){
25899             this.un('activate', this.refreshDelegate);
25900         }
25901         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25902         this.on("activate", this.refreshDelegate);
25903         return this.bodyEl.getUpdateManager();
25904     },
25905
25906     /** @private */
25907     _handleRefresh : function(url, params, loadOnce){
25908         if(!loadOnce || !this.loaded){
25909             var updater = this.bodyEl.getUpdateManager();
25910             updater.update(url, params, this._setLoaded.createDelegate(this));
25911         }
25912     },
25913
25914     /**
25915      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25916      *   Will fail silently if the setUrl method has not been called.
25917      *   This does not activate the panel, just updates its content.
25918      */
25919     refresh : function(){
25920         if(this.refreshDelegate){
25921            this.loaded = false;
25922            this.refreshDelegate();
25923         }
25924     },
25925
25926     /** @private */
25927     _setLoaded : function(){
25928         this.loaded = true;
25929     },
25930
25931     /** @private */
25932     closeClick : function(e){
25933         var o = {};
25934         e.stopEvent();
25935         this.fireEvent("beforeclose", this, o);
25936         if(o.cancel !== true){
25937             this.tabPanel.removeTab(this.id);
25938         }
25939     },
25940     /**
25941      * The text displayed in the tooltip for the close icon.
25942      * @type String
25943      */
25944     closeText : "Close this tab"
25945 });
25946
25947 /** @private */
25948 Roo.TabPanel.prototype.createStrip = function(container){
25949     var strip = document.createElement("div");
25950     strip.className = "x-tabs-wrap";
25951     container.appendChild(strip);
25952     return strip;
25953 };
25954 /** @private */
25955 Roo.TabPanel.prototype.createStripList = function(strip){
25956     // div wrapper for retard IE
25957     // returns the "tr" element.
25958     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25959         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25960         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25961     return strip.firstChild.firstChild.firstChild.firstChild;
25962 };
25963 /** @private */
25964 Roo.TabPanel.prototype.createBody = function(container){
25965     var body = document.createElement("div");
25966     Roo.id(body, "tab-body");
25967     Roo.fly(body).addClass("x-tabs-body");
25968     container.appendChild(body);
25969     return body;
25970 };
25971 /** @private */
25972 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25973     var body = Roo.getDom(id);
25974     if(!body){
25975         body = document.createElement("div");
25976         body.id = id;
25977     }
25978     Roo.fly(body).addClass("x-tabs-item-body");
25979     bodyEl.insertBefore(body, bodyEl.firstChild);
25980     return body;
25981 };
25982 /** @private */
25983 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25984     var td = document.createElement("td");
25985     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
25986     //stripEl.appendChild(td);
25987     if(closable){
25988         td.className = "x-tabs-closable";
25989         if(!this.closeTpl){
25990             this.closeTpl = new Roo.Template(
25991                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25992                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25993                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25994             );
25995         }
25996         var el = this.closeTpl.overwrite(td, {"text": text});
25997         var close = el.getElementsByTagName("div")[0];
25998         var inner = el.getElementsByTagName("em")[0];
25999         return {"el": el, "close": close, "inner": inner};
26000     } else {
26001         if(!this.tabTpl){
26002             this.tabTpl = new Roo.Template(
26003                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26004                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26005             );
26006         }
26007         var el = this.tabTpl.overwrite(td, {"text": text});
26008         var inner = el.getElementsByTagName("em")[0];
26009         return {"el": el, "inner": inner};
26010     }
26011 };/*
26012  * Based on:
26013  * Ext JS Library 1.1.1
26014  * Copyright(c) 2006-2007, Ext JS, LLC.
26015  *
26016  * Originally Released Under LGPL - original licence link has changed is not relivant.
26017  *
26018  * Fork - LGPL
26019  * <script type="text/javascript">
26020  */
26021
26022 /**
26023  * @class Roo.Button
26024  * @extends Roo.util.Observable
26025  * Simple Button class
26026  * @cfg {String} text The button text
26027  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26028  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26029  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26030  * @cfg {Object} scope The scope of the handler
26031  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26032  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26033  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26034  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26035  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26036  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26037    applies if enableToggle = true)
26038  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26039  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26040   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26041  * @constructor
26042  * Create a new button
26043  * @param {Object} config The config object
26044  */
26045 Roo.Button = function(renderTo, config)
26046 {
26047     if (!config) {
26048         config = renderTo;
26049         renderTo = config.renderTo || false;
26050     }
26051     
26052     Roo.apply(this, config);
26053     this.addEvents({
26054         /**
26055              * @event click
26056              * Fires when this button is clicked
26057              * @param {Button} this
26058              * @param {EventObject} e The click event
26059              */
26060             "click" : true,
26061         /**
26062              * @event toggle
26063              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26064              * @param {Button} this
26065              * @param {Boolean} pressed
26066              */
26067             "toggle" : true,
26068         /**
26069              * @event mouseover
26070              * Fires when the mouse hovers over the button
26071              * @param {Button} this
26072              * @param {Event} e The event object
26073              */
26074         'mouseover' : true,
26075         /**
26076              * @event mouseout
26077              * Fires when the mouse exits the button
26078              * @param {Button} this
26079              * @param {Event} e The event object
26080              */
26081         'mouseout': true,
26082          /**
26083              * @event render
26084              * Fires when the button is rendered
26085              * @param {Button} this
26086              */
26087         'render': true
26088     });
26089     if(this.menu){
26090         this.menu = Roo.menu.MenuMgr.get(this.menu);
26091     }
26092     // register listeners first!!  - so render can be captured..
26093     Roo.util.Observable.call(this);
26094     if(renderTo){
26095         this.render(renderTo);
26096     }
26097     
26098   
26099 };
26100
26101 Roo.extend(Roo.Button, Roo.util.Observable, {
26102     /**
26103      * 
26104      */
26105     
26106     /**
26107      * Read-only. True if this button is hidden
26108      * @type Boolean
26109      */
26110     hidden : false,
26111     /**
26112      * Read-only. True if this button is disabled
26113      * @type Boolean
26114      */
26115     disabled : false,
26116     /**
26117      * Read-only. True if this button is pressed (only if enableToggle = true)
26118      * @type Boolean
26119      */
26120     pressed : false,
26121
26122     /**
26123      * @cfg {Number} tabIndex 
26124      * The DOM tabIndex for this button (defaults to undefined)
26125      */
26126     tabIndex : undefined,
26127
26128     /**
26129      * @cfg {Boolean} enableToggle
26130      * True to enable pressed/not pressed toggling (defaults to false)
26131      */
26132     enableToggle: false,
26133     /**
26134      * @cfg {Mixed} menu
26135      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26136      */
26137     menu : undefined,
26138     /**
26139      * @cfg {String} menuAlign
26140      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26141      */
26142     menuAlign : "tl-bl?",
26143
26144     /**
26145      * @cfg {String} iconCls
26146      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26147      */
26148     iconCls : undefined,
26149     /**
26150      * @cfg {String} type
26151      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26152      */
26153     type : 'button',
26154
26155     // private
26156     menuClassTarget: 'tr',
26157
26158     /**
26159      * @cfg {String} clickEvent
26160      * The type of event to map to the button's event handler (defaults to 'click')
26161      */
26162     clickEvent : 'click',
26163
26164     /**
26165      * @cfg {Boolean} handleMouseEvents
26166      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26167      */
26168     handleMouseEvents : true,
26169
26170     /**
26171      * @cfg {String} tooltipType
26172      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26173      */
26174     tooltipType : 'qtip',
26175
26176     /**
26177      * @cfg {String} cls
26178      * A CSS class to apply to the button's main element.
26179      */
26180     
26181     /**
26182      * @cfg {Roo.Template} template (Optional)
26183      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26184      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26185      * require code modifications if required elements (e.g. a button) aren't present.
26186      */
26187
26188     // private
26189     render : function(renderTo){
26190         var btn;
26191         if(this.hideParent){
26192             this.parentEl = Roo.get(renderTo);
26193         }
26194         if(!this.dhconfig){
26195             if(!this.template){
26196                 if(!Roo.Button.buttonTemplate){
26197                     // hideous table template
26198                     Roo.Button.buttonTemplate = new Roo.Template(
26199                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26200                         '<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>',
26201                         "</tr></tbody></table>");
26202                 }
26203                 this.template = Roo.Button.buttonTemplate;
26204             }
26205             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26206             var btnEl = btn.child("button:first");
26207             btnEl.on('focus', this.onFocus, this);
26208             btnEl.on('blur', this.onBlur, this);
26209             if(this.cls){
26210                 btn.addClass(this.cls);
26211             }
26212             if(this.icon){
26213                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26214             }
26215             if(this.iconCls){
26216                 btnEl.addClass(this.iconCls);
26217                 if(!this.cls){
26218                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26219                 }
26220             }
26221             if(this.tabIndex !== undefined){
26222                 btnEl.dom.tabIndex = this.tabIndex;
26223             }
26224             if(this.tooltip){
26225                 if(typeof this.tooltip == 'object'){
26226                     Roo.QuickTips.tips(Roo.apply({
26227                           target: btnEl.id
26228                     }, this.tooltip));
26229                 } else {
26230                     btnEl.dom[this.tooltipType] = this.tooltip;
26231                 }
26232             }
26233         }else{
26234             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26235         }
26236         this.el = btn;
26237         if(this.id){
26238             this.el.dom.id = this.el.id = this.id;
26239         }
26240         if(this.menu){
26241             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26242             this.menu.on("show", this.onMenuShow, this);
26243             this.menu.on("hide", this.onMenuHide, this);
26244         }
26245         btn.addClass("x-btn");
26246         if(Roo.isIE && !Roo.isIE7){
26247             this.autoWidth.defer(1, this);
26248         }else{
26249             this.autoWidth();
26250         }
26251         if(this.handleMouseEvents){
26252             btn.on("mouseover", this.onMouseOver, this);
26253             btn.on("mouseout", this.onMouseOut, this);
26254             btn.on("mousedown", this.onMouseDown, this);
26255         }
26256         btn.on(this.clickEvent, this.onClick, this);
26257         //btn.on("mouseup", this.onMouseUp, this);
26258         if(this.hidden){
26259             this.hide();
26260         }
26261         if(this.disabled){
26262             this.disable();
26263         }
26264         Roo.ButtonToggleMgr.register(this);
26265         if(this.pressed){
26266             this.el.addClass("x-btn-pressed");
26267         }
26268         if(this.repeat){
26269             var repeater = new Roo.util.ClickRepeater(btn,
26270                 typeof this.repeat == "object" ? this.repeat : {}
26271             );
26272             repeater.on("click", this.onClick,  this);
26273         }
26274         
26275         this.fireEvent('render', this);
26276         
26277     },
26278     /**
26279      * Returns the button's underlying element
26280      * @return {Roo.Element} The element
26281      */
26282     getEl : function(){
26283         return this.el;  
26284     },
26285     
26286     /**
26287      * Destroys this Button and removes any listeners.
26288      */
26289     destroy : function(){
26290         Roo.ButtonToggleMgr.unregister(this);
26291         this.el.removeAllListeners();
26292         this.purgeListeners();
26293         this.el.remove();
26294     },
26295
26296     // private
26297     autoWidth : function(){
26298         if(this.el){
26299             this.el.setWidth("auto");
26300             if(Roo.isIE7 && Roo.isStrict){
26301                 var ib = this.el.child('button');
26302                 if(ib && ib.getWidth() > 20){
26303                     ib.clip();
26304                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26305                 }
26306             }
26307             if(this.minWidth){
26308                 if(this.hidden){
26309                     this.el.beginMeasure();
26310                 }
26311                 if(this.el.getWidth() < this.minWidth){
26312                     this.el.setWidth(this.minWidth);
26313                 }
26314                 if(this.hidden){
26315                     this.el.endMeasure();
26316                 }
26317             }
26318         }
26319     },
26320
26321     /**
26322      * Assigns this button's click handler
26323      * @param {Function} handler The function to call when the button is clicked
26324      * @param {Object} scope (optional) Scope for the function passed in
26325      */
26326     setHandler : function(handler, scope){
26327         this.handler = handler;
26328         this.scope = scope;  
26329     },
26330     
26331     /**
26332      * Sets this button's text
26333      * @param {String} text The button text
26334      */
26335     setText : function(text){
26336         this.text = text;
26337         if(this.el){
26338             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26339         }
26340         this.autoWidth();
26341     },
26342     
26343     /**
26344      * Gets the text for this button
26345      * @return {String} The button text
26346      */
26347     getText : function(){
26348         return this.text;  
26349     },
26350     
26351     /**
26352      * Show this button
26353      */
26354     show: function(){
26355         this.hidden = false;
26356         if(this.el){
26357             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26358         }
26359     },
26360     
26361     /**
26362      * Hide this button
26363      */
26364     hide: function(){
26365         this.hidden = true;
26366         if(this.el){
26367             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26368         }
26369     },
26370     
26371     /**
26372      * Convenience function for boolean show/hide
26373      * @param {Boolean} visible True to show, false to hide
26374      */
26375     setVisible: function(visible){
26376         if(visible) {
26377             this.show();
26378         }else{
26379             this.hide();
26380         }
26381     },
26382     
26383     /**
26384      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26385      * @param {Boolean} state (optional) Force a particular state
26386      */
26387     toggle : function(state){
26388         state = state === undefined ? !this.pressed : state;
26389         if(state != this.pressed){
26390             if(state){
26391                 this.el.addClass("x-btn-pressed");
26392                 this.pressed = true;
26393                 this.fireEvent("toggle", this, true);
26394             }else{
26395                 this.el.removeClass("x-btn-pressed");
26396                 this.pressed = false;
26397                 this.fireEvent("toggle", this, false);
26398             }
26399             if(this.toggleHandler){
26400                 this.toggleHandler.call(this.scope || this, this, state);
26401             }
26402         }
26403     },
26404     
26405     /**
26406      * Focus the button
26407      */
26408     focus : function(){
26409         this.el.child('button:first').focus();
26410     },
26411     
26412     /**
26413      * Disable this button
26414      */
26415     disable : function(){
26416         if(this.el){
26417             this.el.addClass("x-btn-disabled");
26418         }
26419         this.disabled = true;
26420     },
26421     
26422     /**
26423      * Enable this button
26424      */
26425     enable : function(){
26426         if(this.el){
26427             this.el.removeClass("x-btn-disabled");
26428         }
26429         this.disabled = false;
26430     },
26431
26432     /**
26433      * Convenience function for boolean enable/disable
26434      * @param {Boolean} enabled True to enable, false to disable
26435      */
26436     setDisabled : function(v){
26437         this[v !== true ? "enable" : "disable"]();
26438     },
26439
26440     // private
26441     onClick : function(e){
26442         if(e){
26443             e.preventDefault();
26444         }
26445         if(e.button != 0){
26446             return;
26447         }
26448         if(!this.disabled){
26449             if(this.enableToggle){
26450                 this.toggle();
26451             }
26452             if(this.menu && !this.menu.isVisible()){
26453                 this.menu.show(this.el, this.menuAlign);
26454             }
26455             this.fireEvent("click", this, e);
26456             if(this.handler){
26457                 this.el.removeClass("x-btn-over");
26458                 this.handler.call(this.scope || this, this, e);
26459             }
26460         }
26461     },
26462     // private
26463     onMouseOver : function(e){
26464         if(!this.disabled){
26465             this.el.addClass("x-btn-over");
26466             this.fireEvent('mouseover', this, e);
26467         }
26468     },
26469     // private
26470     onMouseOut : function(e){
26471         if(!e.within(this.el,  true)){
26472             this.el.removeClass("x-btn-over");
26473             this.fireEvent('mouseout', this, e);
26474         }
26475     },
26476     // private
26477     onFocus : function(e){
26478         if(!this.disabled){
26479             this.el.addClass("x-btn-focus");
26480         }
26481     },
26482     // private
26483     onBlur : function(e){
26484         this.el.removeClass("x-btn-focus");
26485     },
26486     // private
26487     onMouseDown : function(e){
26488         if(!this.disabled && e.button == 0){
26489             this.el.addClass("x-btn-click");
26490             Roo.get(document).on('mouseup', this.onMouseUp, this);
26491         }
26492     },
26493     // private
26494     onMouseUp : function(e){
26495         if(e.button == 0){
26496             this.el.removeClass("x-btn-click");
26497             Roo.get(document).un('mouseup', this.onMouseUp, this);
26498         }
26499     },
26500     // private
26501     onMenuShow : function(e){
26502         this.el.addClass("x-btn-menu-active");
26503     },
26504     // private
26505     onMenuHide : function(e){
26506         this.el.removeClass("x-btn-menu-active");
26507     }   
26508 });
26509
26510 // Private utility class used by Button
26511 Roo.ButtonToggleMgr = function(){
26512    var groups = {};
26513    
26514    function toggleGroup(btn, state){
26515        if(state){
26516            var g = groups[btn.toggleGroup];
26517            for(var i = 0, l = g.length; i < l; i++){
26518                if(g[i] != btn){
26519                    g[i].toggle(false);
26520                }
26521            }
26522        }
26523    }
26524    
26525    return {
26526        register : function(btn){
26527            if(!btn.toggleGroup){
26528                return;
26529            }
26530            var g = groups[btn.toggleGroup];
26531            if(!g){
26532                g = groups[btn.toggleGroup] = [];
26533            }
26534            g.push(btn);
26535            btn.on("toggle", toggleGroup);
26536        },
26537        
26538        unregister : function(btn){
26539            if(!btn.toggleGroup){
26540                return;
26541            }
26542            var g = groups[btn.toggleGroup];
26543            if(g){
26544                g.remove(btn);
26545                btn.un("toggle", toggleGroup);
26546            }
26547        }
26548    };
26549 }();/*
26550  * Based on:
26551  * Ext JS Library 1.1.1
26552  * Copyright(c) 2006-2007, Ext JS, LLC.
26553  *
26554  * Originally Released Under LGPL - original licence link has changed is not relivant.
26555  *
26556  * Fork - LGPL
26557  * <script type="text/javascript">
26558  */
26559  
26560 /**
26561  * @class Roo.SplitButton
26562  * @extends Roo.Button
26563  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26564  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26565  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26566  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26567  * @cfg {String} arrowTooltip The title attribute of the arrow
26568  * @constructor
26569  * Create a new menu button
26570  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26571  * @param {Object} config The config object
26572  */
26573 Roo.SplitButton = function(renderTo, config){
26574     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26575     /**
26576      * @event arrowclick
26577      * Fires when this button's arrow is clicked
26578      * @param {SplitButton} this
26579      * @param {EventObject} e The click event
26580      */
26581     this.addEvents({"arrowclick":true});
26582 };
26583
26584 Roo.extend(Roo.SplitButton, Roo.Button, {
26585     render : function(renderTo){
26586         // this is one sweet looking template!
26587         var tpl = new Roo.Template(
26588             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26589             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26590             '<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>',
26591             "</tbody></table></td><td>",
26592             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26593             '<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>',
26594             "</tbody></table></td></tr></table>"
26595         );
26596         var btn = tpl.append(renderTo, [this.text, this.type], true);
26597         var btnEl = btn.child("button");
26598         if(this.cls){
26599             btn.addClass(this.cls);
26600         }
26601         if(this.icon){
26602             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26603         }
26604         if(this.iconCls){
26605             btnEl.addClass(this.iconCls);
26606             if(!this.cls){
26607                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26608             }
26609         }
26610         this.el = btn;
26611         if(this.handleMouseEvents){
26612             btn.on("mouseover", this.onMouseOver, this);
26613             btn.on("mouseout", this.onMouseOut, this);
26614             btn.on("mousedown", this.onMouseDown, this);
26615             btn.on("mouseup", this.onMouseUp, this);
26616         }
26617         btn.on(this.clickEvent, this.onClick, this);
26618         if(this.tooltip){
26619             if(typeof this.tooltip == 'object'){
26620                 Roo.QuickTips.tips(Roo.apply({
26621                       target: btnEl.id
26622                 }, this.tooltip));
26623             } else {
26624                 btnEl.dom[this.tooltipType] = this.tooltip;
26625             }
26626         }
26627         if(this.arrowTooltip){
26628             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26629         }
26630         if(this.hidden){
26631             this.hide();
26632         }
26633         if(this.disabled){
26634             this.disable();
26635         }
26636         if(this.pressed){
26637             this.el.addClass("x-btn-pressed");
26638         }
26639         if(Roo.isIE && !Roo.isIE7){
26640             this.autoWidth.defer(1, this);
26641         }else{
26642             this.autoWidth();
26643         }
26644         if(this.menu){
26645             this.menu.on("show", this.onMenuShow, this);
26646             this.menu.on("hide", this.onMenuHide, this);
26647         }
26648         this.fireEvent('render', this);
26649     },
26650
26651     // private
26652     autoWidth : function(){
26653         if(this.el){
26654             var tbl = this.el.child("table:first");
26655             var tbl2 = this.el.child("table:last");
26656             this.el.setWidth("auto");
26657             tbl.setWidth("auto");
26658             if(Roo.isIE7 && Roo.isStrict){
26659                 var ib = this.el.child('button:first');
26660                 if(ib && ib.getWidth() > 20){
26661                     ib.clip();
26662                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26663                 }
26664             }
26665             if(this.minWidth){
26666                 if(this.hidden){
26667                     this.el.beginMeasure();
26668                 }
26669                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26670                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26671                 }
26672                 if(this.hidden){
26673                     this.el.endMeasure();
26674                 }
26675             }
26676             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26677         } 
26678     },
26679     /**
26680      * Sets this button's click handler
26681      * @param {Function} handler The function to call when the button is clicked
26682      * @param {Object} scope (optional) Scope for the function passed above
26683      */
26684     setHandler : function(handler, scope){
26685         this.handler = handler;
26686         this.scope = scope;  
26687     },
26688     
26689     /**
26690      * Sets this button's arrow click handler
26691      * @param {Function} handler The function to call when the arrow is clicked
26692      * @param {Object} scope (optional) Scope for the function passed above
26693      */
26694     setArrowHandler : function(handler, scope){
26695         this.arrowHandler = handler;
26696         this.scope = scope;  
26697     },
26698     
26699     /**
26700      * Focus the button
26701      */
26702     focus : function(){
26703         if(this.el){
26704             this.el.child("button:first").focus();
26705         }
26706     },
26707
26708     // private
26709     onClick : function(e){
26710         e.preventDefault();
26711         if(!this.disabled){
26712             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26713                 if(this.menu && !this.menu.isVisible()){
26714                     this.menu.show(this.el, this.menuAlign);
26715                 }
26716                 this.fireEvent("arrowclick", this, e);
26717                 if(this.arrowHandler){
26718                     this.arrowHandler.call(this.scope || this, this, e);
26719                 }
26720             }else{
26721                 this.fireEvent("click", this, e);
26722                 if(this.handler){
26723                     this.handler.call(this.scope || this, this, e);
26724                 }
26725             }
26726         }
26727     },
26728     // private
26729     onMouseDown : function(e){
26730         if(!this.disabled){
26731             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26732         }
26733     },
26734     // private
26735     onMouseUp : function(e){
26736         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26737     }   
26738 });
26739
26740
26741 // backwards compat
26742 Roo.MenuButton = Roo.SplitButton;/*
26743  * Based on:
26744  * Ext JS Library 1.1.1
26745  * Copyright(c) 2006-2007, Ext JS, LLC.
26746  *
26747  * Originally Released Under LGPL - original licence link has changed is not relivant.
26748  *
26749  * Fork - LGPL
26750  * <script type="text/javascript">
26751  */
26752
26753 /**
26754  * @class Roo.Toolbar
26755  * Basic Toolbar class.
26756  * @constructor
26757  * Creates a new Toolbar
26758  * @param {Object} container The config object
26759  */ 
26760 Roo.Toolbar = function(container, buttons, config)
26761 {
26762     /// old consturctor format still supported..
26763     if(container instanceof Array){ // omit the container for later rendering
26764         buttons = container;
26765         config = buttons;
26766         container = null;
26767     }
26768     if (typeof(container) == 'object' && container.xtype) {
26769         config = container;
26770         container = config.container;
26771         buttons = config.buttons || []; // not really - use items!!
26772     }
26773     var xitems = [];
26774     if (config && config.items) {
26775         xitems = config.items;
26776         delete config.items;
26777     }
26778     Roo.apply(this, config);
26779     this.buttons = buttons;
26780     
26781     if(container){
26782         this.render(container);
26783     }
26784     this.xitems = xitems;
26785     Roo.each(xitems, function(b) {
26786         this.add(b);
26787     }, this);
26788     
26789 };
26790
26791 Roo.Toolbar.prototype = {
26792     /**
26793      * @cfg {Array} items
26794      * array of button configs or elements to add (will be converted to a MixedCollection)
26795      */
26796     
26797     /**
26798      * @cfg {String/HTMLElement/Element} container
26799      * The id or element that will contain the toolbar
26800      */
26801     // private
26802     render : function(ct){
26803         this.el = Roo.get(ct);
26804         if(this.cls){
26805             this.el.addClass(this.cls);
26806         }
26807         // using a table allows for vertical alignment
26808         // 100% width is needed by Safari...
26809         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26810         this.tr = this.el.child("tr", true);
26811         var autoId = 0;
26812         this.items = new Roo.util.MixedCollection(false, function(o){
26813             return o.id || ("item" + (++autoId));
26814         });
26815         if(this.buttons){
26816             this.add.apply(this, this.buttons);
26817             delete this.buttons;
26818         }
26819     },
26820
26821     /**
26822      * Adds element(s) to the toolbar -- this function takes a variable number of 
26823      * arguments of mixed type and adds them to the toolbar.
26824      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26825      * <ul>
26826      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26827      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26828      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26829      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26830      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26831      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26832      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26833      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26834      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26835      * </ul>
26836      * @param {Mixed} arg2
26837      * @param {Mixed} etc.
26838      */
26839     add : function(){
26840         var a = arguments, l = a.length;
26841         for(var i = 0; i < l; i++){
26842             this._add(a[i]);
26843         }
26844     },
26845     // private..
26846     _add : function(el) {
26847         
26848         if (el.xtype) {
26849             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26850         }
26851         
26852         if (el.applyTo){ // some kind of form field
26853             return this.addField(el);
26854         } 
26855         if (el.render){ // some kind of Toolbar.Item
26856             return this.addItem(el);
26857         }
26858         if (typeof el == "string"){ // string
26859             if(el == "separator" || el == "-"){
26860                 return this.addSeparator();
26861             }
26862             if (el == " "){
26863                 return this.addSpacer();
26864             }
26865             if(el == "->"){
26866                 return this.addFill();
26867             }
26868             return this.addText(el);
26869             
26870         }
26871         if(el.tagName){ // element
26872             return this.addElement(el);
26873         }
26874         if(typeof el == "object"){ // must be button config?
26875             return this.addButton(el);
26876         }
26877         // and now what?!?!
26878         return false;
26879         
26880     },
26881     
26882     /**
26883      * Add an Xtype element
26884      * @param {Object} xtype Xtype Object
26885      * @return {Object} created Object
26886      */
26887     addxtype : function(e){
26888         return this.add(e);  
26889     },
26890     
26891     /**
26892      * Returns the Element for this toolbar.
26893      * @return {Roo.Element}
26894      */
26895     getEl : function(){
26896         return this.el;  
26897     },
26898     
26899     /**
26900      * Adds a separator
26901      * @return {Roo.Toolbar.Item} The separator item
26902      */
26903     addSeparator : function(){
26904         return this.addItem(new Roo.Toolbar.Separator());
26905     },
26906
26907     /**
26908      * Adds a spacer element
26909      * @return {Roo.Toolbar.Spacer} The spacer item
26910      */
26911     addSpacer : function(){
26912         return this.addItem(new Roo.Toolbar.Spacer());
26913     },
26914
26915     /**
26916      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26917      * @return {Roo.Toolbar.Fill} The fill item
26918      */
26919     addFill : function(){
26920         return this.addItem(new Roo.Toolbar.Fill());
26921     },
26922
26923     /**
26924      * Adds any standard HTML element to the toolbar
26925      * @param {String/HTMLElement/Element} el The element or id of the element to add
26926      * @return {Roo.Toolbar.Item} The element's item
26927      */
26928     addElement : function(el){
26929         return this.addItem(new Roo.Toolbar.Item(el));
26930     },
26931     /**
26932      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26933      * @type Roo.util.MixedCollection  
26934      */
26935     items : false,
26936      
26937     /**
26938      * Adds any Toolbar.Item or subclass
26939      * @param {Roo.Toolbar.Item} item
26940      * @return {Roo.Toolbar.Item} The item
26941      */
26942     addItem : function(item){
26943         var td = this.nextBlock();
26944         item.render(td);
26945         this.items.add(item);
26946         return item;
26947     },
26948     
26949     /**
26950      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26951      * @param {Object/Array} config A button config or array of configs
26952      * @return {Roo.Toolbar.Button/Array}
26953      */
26954     addButton : function(config){
26955         if(config instanceof Array){
26956             var buttons = [];
26957             for(var i = 0, len = config.length; i < len; i++) {
26958                 buttons.push(this.addButton(config[i]));
26959             }
26960             return buttons;
26961         }
26962         var b = config;
26963         if(!(config instanceof Roo.Toolbar.Button)){
26964             b = config.split ?
26965                 new Roo.Toolbar.SplitButton(config) :
26966                 new Roo.Toolbar.Button(config);
26967         }
26968         var td = this.nextBlock();
26969         b.render(td);
26970         this.items.add(b);
26971         return b;
26972     },
26973     
26974     /**
26975      * Adds text to the toolbar
26976      * @param {String} text The text to add
26977      * @return {Roo.Toolbar.Item} The element's item
26978      */
26979     addText : function(text){
26980         return this.addItem(new Roo.Toolbar.TextItem(text));
26981     },
26982     
26983     /**
26984      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26985      * @param {Number} index The index where the item is to be inserted
26986      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26987      * @return {Roo.Toolbar.Button/Item}
26988      */
26989     insertButton : function(index, item){
26990         if(item instanceof Array){
26991             var buttons = [];
26992             for(var i = 0, len = item.length; i < len; i++) {
26993                buttons.push(this.insertButton(index + i, item[i]));
26994             }
26995             return buttons;
26996         }
26997         if (!(item instanceof Roo.Toolbar.Button)){
26998            item = new Roo.Toolbar.Button(item);
26999         }
27000         var td = document.createElement("td");
27001         this.tr.insertBefore(td, this.tr.childNodes[index]);
27002         item.render(td);
27003         this.items.insert(index, item);
27004         return item;
27005     },
27006     
27007     /**
27008      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27009      * @param {Object} config
27010      * @return {Roo.Toolbar.Item} The element's item
27011      */
27012     addDom : function(config, returnEl){
27013         var td = this.nextBlock();
27014         Roo.DomHelper.overwrite(td, config);
27015         var ti = new Roo.Toolbar.Item(td.firstChild);
27016         ti.render(td);
27017         this.items.add(ti);
27018         return ti;
27019     },
27020
27021     /**
27022      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27023      * @type Roo.util.MixedCollection  
27024      */
27025     fields : false,
27026     
27027     /**
27028      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27029      * Note: the field should not have been rendered yet. For a field that has already been
27030      * rendered, use {@link #addElement}.
27031      * @param {Roo.form.Field} field
27032      * @return {Roo.ToolbarItem}
27033      */
27034      
27035       
27036     addField : function(field) {
27037         if (!this.fields) {
27038             var autoId = 0;
27039             this.fields = new Roo.util.MixedCollection(false, function(o){
27040                 return o.id || ("item" + (++autoId));
27041             });
27042
27043         }
27044         
27045         var td = this.nextBlock();
27046         field.render(td);
27047         var ti = new Roo.Toolbar.Item(td.firstChild);
27048         ti.render(td);
27049         this.items.add(ti);
27050         this.fields.add(field);
27051         return ti;
27052     },
27053     /**
27054      * Hide the toolbar
27055      * @method hide
27056      */
27057      
27058       
27059     hide : function()
27060     {
27061         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27062         this.el.child('div').hide();
27063     },
27064     /**
27065      * Show the toolbar
27066      * @method show
27067      */
27068     show : function()
27069     {
27070         this.el.child('div').show();
27071     },
27072       
27073     // private
27074     nextBlock : function(){
27075         var td = document.createElement("td");
27076         this.tr.appendChild(td);
27077         return td;
27078     },
27079
27080     // private
27081     destroy : function(){
27082         if(this.items){ // rendered?
27083             Roo.destroy.apply(Roo, this.items.items);
27084         }
27085         if(this.fields){ // rendered?
27086             Roo.destroy.apply(Roo, this.fields.items);
27087         }
27088         Roo.Element.uncache(this.el, this.tr);
27089     }
27090 };
27091
27092 /**
27093  * @class Roo.Toolbar.Item
27094  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27095  * @constructor
27096  * Creates a new Item
27097  * @param {HTMLElement} el 
27098  */
27099 Roo.Toolbar.Item = function(el){
27100     this.el = Roo.getDom(el);
27101     this.id = Roo.id(this.el);
27102     this.hidden = false;
27103 };
27104
27105 Roo.Toolbar.Item.prototype = {
27106     
27107     /**
27108      * Get this item's HTML Element
27109      * @return {HTMLElement}
27110      */
27111     getEl : function(){
27112        return this.el;  
27113     },
27114
27115     // private
27116     render : function(td){
27117         this.td = td;
27118         td.appendChild(this.el);
27119     },
27120     
27121     /**
27122      * Removes and destroys this item.
27123      */
27124     destroy : function(){
27125         this.td.parentNode.removeChild(this.td);
27126     },
27127     
27128     /**
27129      * Shows this item.
27130      */
27131     show: function(){
27132         this.hidden = false;
27133         this.td.style.display = "";
27134     },
27135     
27136     /**
27137      * Hides this item.
27138      */
27139     hide: function(){
27140         this.hidden = true;
27141         this.td.style.display = "none";
27142     },
27143     
27144     /**
27145      * Convenience function for boolean show/hide.
27146      * @param {Boolean} visible true to show/false to hide
27147      */
27148     setVisible: function(visible){
27149         if(visible) {
27150             this.show();
27151         }else{
27152             this.hide();
27153         }
27154     },
27155     
27156     /**
27157      * Try to focus this item.
27158      */
27159     focus : function(){
27160         Roo.fly(this.el).focus();
27161     },
27162     
27163     /**
27164      * Disables this item.
27165      */
27166     disable : function(){
27167         Roo.fly(this.td).addClass("x-item-disabled");
27168         this.disabled = true;
27169         this.el.disabled = true;
27170     },
27171     
27172     /**
27173      * Enables this item.
27174      */
27175     enable : function(){
27176         Roo.fly(this.td).removeClass("x-item-disabled");
27177         this.disabled = false;
27178         this.el.disabled = false;
27179     }
27180 };
27181
27182
27183 /**
27184  * @class Roo.Toolbar.Separator
27185  * @extends Roo.Toolbar.Item
27186  * A simple toolbar separator class
27187  * @constructor
27188  * Creates a new Separator
27189  */
27190 Roo.Toolbar.Separator = function(){
27191     var s = document.createElement("span");
27192     s.className = "ytb-sep";
27193     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27194 };
27195 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27196     enable:Roo.emptyFn,
27197     disable:Roo.emptyFn,
27198     focus:Roo.emptyFn
27199 });
27200
27201 /**
27202  * @class Roo.Toolbar.Spacer
27203  * @extends Roo.Toolbar.Item
27204  * A simple element that adds extra horizontal space to a toolbar.
27205  * @constructor
27206  * Creates a new Spacer
27207  */
27208 Roo.Toolbar.Spacer = function(){
27209     var s = document.createElement("div");
27210     s.className = "ytb-spacer";
27211     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27212 };
27213 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27214     enable:Roo.emptyFn,
27215     disable:Roo.emptyFn,
27216     focus:Roo.emptyFn
27217 });
27218
27219 /**
27220  * @class Roo.Toolbar.Fill
27221  * @extends Roo.Toolbar.Spacer
27222  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27223  * @constructor
27224  * Creates a new Spacer
27225  */
27226 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27227     // private
27228     render : function(td){
27229         td.style.width = '100%';
27230         Roo.Toolbar.Fill.superclass.render.call(this, td);
27231     }
27232 });
27233
27234 /**
27235  * @class Roo.Toolbar.TextItem
27236  * @extends Roo.Toolbar.Item
27237  * A simple class that renders text directly into a toolbar.
27238  * @constructor
27239  * Creates a new TextItem
27240  * @param {String} text
27241  */
27242 Roo.Toolbar.TextItem = function(text){
27243     if (typeof(text) == 'object') {
27244         text = text.text;
27245     }
27246     var s = document.createElement("span");
27247     s.className = "ytb-text";
27248     s.innerHTML = text;
27249     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27250 };
27251 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27252     enable:Roo.emptyFn,
27253     disable:Roo.emptyFn,
27254     focus:Roo.emptyFn
27255 });
27256
27257 /**
27258  * @class Roo.Toolbar.Button
27259  * @extends Roo.Button
27260  * A button that renders into a toolbar.
27261  * @constructor
27262  * Creates a new Button
27263  * @param {Object} config A standard {@link Roo.Button} config object
27264  */
27265 Roo.Toolbar.Button = function(config){
27266     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27267 };
27268 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27269     render : function(td){
27270         this.td = td;
27271         Roo.Toolbar.Button.superclass.render.call(this, td);
27272     },
27273     
27274     /**
27275      * Removes and destroys this button
27276      */
27277     destroy : function(){
27278         Roo.Toolbar.Button.superclass.destroy.call(this);
27279         this.td.parentNode.removeChild(this.td);
27280     },
27281     
27282     /**
27283      * Shows this button
27284      */
27285     show: function(){
27286         this.hidden = false;
27287         this.td.style.display = "";
27288     },
27289     
27290     /**
27291      * Hides this button
27292      */
27293     hide: function(){
27294         this.hidden = true;
27295         this.td.style.display = "none";
27296     },
27297
27298     /**
27299      * Disables this item
27300      */
27301     disable : function(){
27302         Roo.fly(this.td).addClass("x-item-disabled");
27303         this.disabled = true;
27304     },
27305
27306     /**
27307      * Enables this item
27308      */
27309     enable : function(){
27310         Roo.fly(this.td).removeClass("x-item-disabled");
27311         this.disabled = false;
27312     }
27313 });
27314 // backwards compat
27315 Roo.ToolbarButton = Roo.Toolbar.Button;
27316
27317 /**
27318  * @class Roo.Toolbar.SplitButton
27319  * @extends Roo.SplitButton
27320  * A menu button that renders into a toolbar.
27321  * @constructor
27322  * Creates a new SplitButton
27323  * @param {Object} config A standard {@link Roo.SplitButton} config object
27324  */
27325 Roo.Toolbar.SplitButton = function(config){
27326     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27327 };
27328 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27329     render : function(td){
27330         this.td = td;
27331         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27332     },
27333     
27334     /**
27335      * Removes and destroys this button
27336      */
27337     destroy : function(){
27338         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27339         this.td.parentNode.removeChild(this.td);
27340     },
27341     
27342     /**
27343      * Shows this button
27344      */
27345     show: function(){
27346         this.hidden = false;
27347         this.td.style.display = "";
27348     },
27349     
27350     /**
27351      * Hides this button
27352      */
27353     hide: function(){
27354         this.hidden = true;
27355         this.td.style.display = "none";
27356     }
27357 });
27358
27359 // backwards compat
27360 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27361  * Based on:
27362  * Ext JS Library 1.1.1
27363  * Copyright(c) 2006-2007, Ext JS, LLC.
27364  *
27365  * Originally Released Under LGPL - original licence link has changed is not relivant.
27366  *
27367  * Fork - LGPL
27368  * <script type="text/javascript">
27369  */
27370  
27371 /**
27372  * @class Roo.PagingToolbar
27373  * @extends Roo.Toolbar
27374  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27375  * @constructor
27376  * Create a new PagingToolbar
27377  * @param {Object} config The config object
27378  */
27379 Roo.PagingToolbar = function(el, ds, config)
27380 {
27381     // old args format still supported... - xtype is prefered..
27382     if (typeof(el) == 'object' && el.xtype) {
27383         // created from xtype...
27384         config = el;
27385         ds = el.dataSource;
27386         el = config.container;
27387     }
27388     var items = [];
27389     if (config.items) {
27390         items = config.items;
27391         config.items = [];
27392     }
27393     
27394     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27395     this.ds = ds;
27396     this.cursor = 0;
27397     this.renderButtons(this.el);
27398     this.bind(ds);
27399     
27400     // supprot items array.
27401    
27402     Roo.each(items, function(e) {
27403         this.add(Roo.factory(e));
27404     },this);
27405     
27406 };
27407
27408 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27409     /**
27410      * @cfg {Roo.data.Store} dataSource
27411      * The underlying data store providing the paged data
27412      */
27413     /**
27414      * @cfg {String/HTMLElement/Element} container
27415      * container The id or element that will contain the toolbar
27416      */
27417     /**
27418      * @cfg {Boolean} displayInfo
27419      * True to display the displayMsg (defaults to false)
27420      */
27421     /**
27422      * @cfg {Number} pageSize
27423      * The number of records to display per page (defaults to 20)
27424      */
27425     pageSize: 20,
27426     /**
27427      * @cfg {String} displayMsg
27428      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27429      */
27430     displayMsg : 'Displaying {0} - {1} of {2}',
27431     /**
27432      * @cfg {String} emptyMsg
27433      * The message to display when no records are found (defaults to "No data to display")
27434      */
27435     emptyMsg : 'No data to display',
27436     /**
27437      * Customizable piece of the default paging text (defaults to "Page")
27438      * @type String
27439      */
27440     beforePageText : "Page",
27441     /**
27442      * Customizable piece of the default paging text (defaults to "of %0")
27443      * @type String
27444      */
27445     afterPageText : "of {0}",
27446     /**
27447      * Customizable piece of the default paging text (defaults to "First Page")
27448      * @type String
27449      */
27450     firstText : "First Page",
27451     /**
27452      * Customizable piece of the default paging text (defaults to "Previous Page")
27453      * @type String
27454      */
27455     prevText : "Previous Page",
27456     /**
27457      * Customizable piece of the default paging text (defaults to "Next Page")
27458      * @type String
27459      */
27460     nextText : "Next Page",
27461     /**
27462      * Customizable piece of the default paging text (defaults to "Last Page")
27463      * @type String
27464      */
27465     lastText : "Last Page",
27466     /**
27467      * Customizable piece of the default paging text (defaults to "Refresh")
27468      * @type String
27469      */
27470     refreshText : "Refresh",
27471
27472     // private
27473     renderButtons : function(el){
27474         Roo.PagingToolbar.superclass.render.call(this, el);
27475         this.first = this.addButton({
27476             tooltip: this.firstText,
27477             cls: "x-btn-icon x-grid-page-first",
27478             disabled: true,
27479             handler: this.onClick.createDelegate(this, ["first"])
27480         });
27481         this.prev = this.addButton({
27482             tooltip: this.prevText,
27483             cls: "x-btn-icon x-grid-page-prev",
27484             disabled: true,
27485             handler: this.onClick.createDelegate(this, ["prev"])
27486         });
27487         //this.addSeparator();
27488         this.add(this.beforePageText);
27489         this.field = Roo.get(this.addDom({
27490            tag: "input",
27491            type: "text",
27492            size: "3",
27493            value: "1",
27494            cls: "x-grid-page-number"
27495         }).el);
27496         this.field.on("keydown", this.onPagingKeydown, this);
27497         this.field.on("focus", function(){this.dom.select();});
27498         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27499         this.field.setHeight(18);
27500         //this.addSeparator();
27501         this.next = this.addButton({
27502             tooltip: this.nextText,
27503             cls: "x-btn-icon x-grid-page-next",
27504             disabled: true,
27505             handler: this.onClick.createDelegate(this, ["next"])
27506         });
27507         this.last = this.addButton({
27508             tooltip: this.lastText,
27509             cls: "x-btn-icon x-grid-page-last",
27510             disabled: true,
27511             handler: this.onClick.createDelegate(this, ["last"])
27512         });
27513         //this.addSeparator();
27514         this.loading = this.addButton({
27515             tooltip: this.refreshText,
27516             cls: "x-btn-icon x-grid-loading",
27517             handler: this.onClick.createDelegate(this, ["refresh"])
27518         });
27519
27520         if(this.displayInfo){
27521             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27522         }
27523     },
27524
27525     // private
27526     updateInfo : function(){
27527         if(this.displayEl){
27528             var count = this.ds.getCount();
27529             var msg = count == 0 ?
27530                 this.emptyMsg :
27531                 String.format(
27532                     this.displayMsg,
27533                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27534                 );
27535             this.displayEl.update(msg);
27536         }
27537     },
27538
27539     // private
27540     onLoad : function(ds, r, o){
27541        this.cursor = o.params ? o.params.start : 0;
27542        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27543
27544        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27545        this.field.dom.value = ap;
27546        this.first.setDisabled(ap == 1);
27547        this.prev.setDisabled(ap == 1);
27548        this.next.setDisabled(ap == ps);
27549        this.last.setDisabled(ap == ps);
27550        this.loading.enable();
27551        this.updateInfo();
27552     },
27553
27554     // private
27555     getPageData : function(){
27556         var total = this.ds.getTotalCount();
27557         return {
27558             total : total,
27559             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27560             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27561         };
27562     },
27563
27564     // private
27565     onLoadError : function(){
27566         this.loading.enable();
27567     },
27568
27569     // private
27570     onPagingKeydown : function(e){
27571         var k = e.getKey();
27572         var d = this.getPageData();
27573         if(k == e.RETURN){
27574             var v = this.field.dom.value, pageNum;
27575             if(!v || isNaN(pageNum = parseInt(v, 10))){
27576                 this.field.dom.value = d.activePage;
27577                 return;
27578             }
27579             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27580             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27581             e.stopEvent();
27582         }
27583         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))
27584         {
27585           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27586           this.field.dom.value = pageNum;
27587           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27588           e.stopEvent();
27589         }
27590         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27591         {
27592           var v = this.field.dom.value, pageNum; 
27593           var increment = (e.shiftKey) ? 10 : 1;
27594           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27595             increment *= -1;
27596           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27597             this.field.dom.value = d.activePage;
27598             return;
27599           }
27600           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27601           {
27602             this.field.dom.value = parseInt(v, 10) + increment;
27603             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27604             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27605           }
27606           e.stopEvent();
27607         }
27608     },
27609
27610     // private
27611     beforeLoad : function(){
27612         if(this.loading){
27613             this.loading.disable();
27614         }
27615     },
27616
27617     // private
27618     onClick : function(which){
27619         var ds = this.ds;
27620         switch(which){
27621             case "first":
27622                 ds.load({params:{start: 0, limit: this.pageSize}});
27623             break;
27624             case "prev":
27625                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27626             break;
27627             case "next":
27628                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27629             break;
27630             case "last":
27631                 var total = ds.getTotalCount();
27632                 var extra = total % this.pageSize;
27633                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27634                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27635             break;
27636             case "refresh":
27637                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27638             break;
27639         }
27640     },
27641
27642     /**
27643      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27644      * @param {Roo.data.Store} store The data store to unbind
27645      */
27646     unbind : function(ds){
27647         ds.un("beforeload", this.beforeLoad, this);
27648         ds.un("load", this.onLoad, this);
27649         ds.un("loadexception", this.onLoadError, this);
27650         ds.un("remove", this.updateInfo, this);
27651         ds.un("add", this.updateInfo, this);
27652         this.ds = undefined;
27653     },
27654
27655     /**
27656      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27657      * @param {Roo.data.Store} store The data store to bind
27658      */
27659     bind : function(ds){
27660         ds.on("beforeload", this.beforeLoad, this);
27661         ds.on("load", this.onLoad, this);
27662         ds.on("loadexception", this.onLoadError, this);
27663         ds.on("remove", this.updateInfo, this);
27664         ds.on("add", this.updateInfo, this);
27665         this.ds = ds;
27666     }
27667 });/*
27668  * Based on:
27669  * Ext JS Library 1.1.1
27670  * Copyright(c) 2006-2007, Ext JS, LLC.
27671  *
27672  * Originally Released Under LGPL - original licence link has changed is not relivant.
27673  *
27674  * Fork - LGPL
27675  * <script type="text/javascript">
27676  */
27677
27678 /**
27679  * @class Roo.Resizable
27680  * @extends Roo.util.Observable
27681  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27682  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27683  * 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
27684  * the element will be wrapped for you automatically.</p>
27685  * <p>Here is the list of valid resize handles:</p>
27686  * <pre>
27687 Value   Description
27688 ------  -------------------
27689  'n'     north
27690  's'     south
27691  'e'     east
27692  'w'     west
27693  'nw'    northwest
27694  'sw'    southwest
27695  'se'    southeast
27696  'ne'    northeast
27697  'hd'    horizontal drag
27698  'all'   all
27699 </pre>
27700  * <p>Here's an example showing the creation of a typical Resizable:</p>
27701  * <pre><code>
27702 var resizer = new Roo.Resizable("element-id", {
27703     handles: 'all',
27704     minWidth: 200,
27705     minHeight: 100,
27706     maxWidth: 500,
27707     maxHeight: 400,
27708     pinned: true
27709 });
27710 resizer.on("resize", myHandler);
27711 </code></pre>
27712  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27713  * resizer.east.setDisplayed(false);</p>
27714  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27715  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27716  * resize operation's new size (defaults to [0, 0])
27717  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27718  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27719  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27720  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27721  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27722  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27723  * @cfg {Number} width The width of the element in pixels (defaults to null)
27724  * @cfg {Number} height The height of the element in pixels (defaults to null)
27725  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27726  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27727  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27728  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27729  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27730  * in favor of the handles config option (defaults to false)
27731  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27732  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27733  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27734  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27735  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27736  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27737  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27738  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27739  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27740  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27741  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27742  * @constructor
27743  * Create a new resizable component
27744  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27745  * @param {Object} config configuration options
27746   */
27747 Roo.Resizable = function(el, config)
27748 {
27749     this.el = Roo.get(el);
27750
27751     if(config && config.wrap){
27752         config.resizeChild = this.el;
27753         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27754         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27755         this.el.setStyle("overflow", "hidden");
27756         this.el.setPositioning(config.resizeChild.getPositioning());
27757         config.resizeChild.clearPositioning();
27758         if(!config.width || !config.height){
27759             var csize = config.resizeChild.getSize();
27760             this.el.setSize(csize.width, csize.height);
27761         }
27762         if(config.pinned && !config.adjustments){
27763             config.adjustments = "auto";
27764         }
27765     }
27766
27767     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27768     this.proxy.unselectable();
27769     this.proxy.enableDisplayMode('block');
27770
27771     Roo.apply(this, config);
27772
27773     if(this.pinned){
27774         this.disableTrackOver = true;
27775         this.el.addClass("x-resizable-pinned");
27776     }
27777     // if the element isn't positioned, make it relative
27778     var position = this.el.getStyle("position");
27779     if(position != "absolute" && position != "fixed"){
27780         this.el.setStyle("position", "relative");
27781     }
27782     if(!this.handles){ // no handles passed, must be legacy style
27783         this.handles = 's,e,se';
27784         if(this.multiDirectional){
27785             this.handles += ',n,w';
27786         }
27787     }
27788     if(this.handles == "all"){
27789         this.handles = "n s e w ne nw se sw";
27790     }
27791     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27792     var ps = Roo.Resizable.positions;
27793     for(var i = 0, len = hs.length; i < len; i++){
27794         if(hs[i] && ps[hs[i]]){
27795             var pos = ps[hs[i]];
27796             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27797         }
27798     }
27799     // legacy
27800     this.corner = this.southeast;
27801     
27802     // updateBox = the box can move..
27803     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27804         this.updateBox = true;
27805     }
27806
27807     this.activeHandle = null;
27808
27809     if(this.resizeChild){
27810         if(typeof this.resizeChild == "boolean"){
27811             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27812         }else{
27813             this.resizeChild = Roo.get(this.resizeChild, true);
27814         }
27815     }
27816     
27817     if(this.adjustments == "auto"){
27818         var rc = this.resizeChild;
27819         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27820         if(rc && (hw || hn)){
27821             rc.position("relative");
27822             rc.setLeft(hw ? hw.el.getWidth() : 0);
27823             rc.setTop(hn ? hn.el.getHeight() : 0);
27824         }
27825         this.adjustments = [
27826             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27827             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27828         ];
27829     }
27830
27831     if(this.draggable){
27832         this.dd = this.dynamic ?
27833             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27834         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27835     }
27836
27837     // public events
27838     this.addEvents({
27839         /**
27840          * @event beforeresize
27841          * Fired before resize is allowed. Set enabled to false to cancel resize.
27842          * @param {Roo.Resizable} this
27843          * @param {Roo.EventObject} e The mousedown event
27844          */
27845         "beforeresize" : true,
27846         /**
27847          * @event resize
27848          * Fired after a resize.
27849          * @param {Roo.Resizable} this
27850          * @param {Number} width The new width
27851          * @param {Number} height The new height
27852          * @param {Roo.EventObject} e The mouseup event
27853          */
27854         "resize" : true
27855     });
27856
27857     if(this.width !== null && this.height !== null){
27858         this.resizeTo(this.width, this.height);
27859     }else{
27860         this.updateChildSize();
27861     }
27862     if(Roo.isIE){
27863         this.el.dom.style.zoom = 1;
27864     }
27865     Roo.Resizable.superclass.constructor.call(this);
27866 };
27867
27868 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27869         resizeChild : false,
27870         adjustments : [0, 0],
27871         minWidth : 5,
27872         minHeight : 5,
27873         maxWidth : 10000,
27874         maxHeight : 10000,
27875         enabled : true,
27876         animate : false,
27877         duration : .35,
27878         dynamic : false,
27879         handles : false,
27880         multiDirectional : false,
27881         disableTrackOver : false,
27882         easing : 'easeOutStrong',
27883         widthIncrement : 0,
27884         heightIncrement : 0,
27885         pinned : false,
27886         width : null,
27887         height : null,
27888         preserveRatio : false,
27889         transparent: false,
27890         minX: 0,
27891         minY: 0,
27892         draggable: false,
27893
27894         /**
27895          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27896          */
27897         constrainTo: undefined,
27898         /**
27899          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27900          */
27901         resizeRegion: undefined,
27902
27903
27904     /**
27905      * Perform a manual resize
27906      * @param {Number} width
27907      * @param {Number} height
27908      */
27909     resizeTo : function(width, height){
27910         this.el.setSize(width, height);
27911         this.updateChildSize();
27912         this.fireEvent("resize", this, width, height, null);
27913     },
27914
27915     // private
27916     startSizing : function(e, handle){
27917         this.fireEvent("beforeresize", this, e);
27918         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27919
27920             if(!this.overlay){
27921                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27922                 this.overlay.unselectable();
27923                 this.overlay.enableDisplayMode("block");
27924                 this.overlay.on("mousemove", this.onMouseMove, this);
27925                 this.overlay.on("mouseup", this.onMouseUp, this);
27926             }
27927             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27928
27929             this.resizing = true;
27930             this.startBox = this.el.getBox();
27931             this.startPoint = e.getXY();
27932             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27933                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27934
27935             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27936             this.overlay.show();
27937
27938             if(this.constrainTo) {
27939                 var ct = Roo.get(this.constrainTo);
27940                 this.resizeRegion = ct.getRegion().adjust(
27941                     ct.getFrameWidth('t'),
27942                     ct.getFrameWidth('l'),
27943                     -ct.getFrameWidth('b'),
27944                     -ct.getFrameWidth('r')
27945                 );
27946             }
27947
27948             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27949             this.proxy.show();
27950             this.proxy.setBox(this.startBox);
27951             if(!this.dynamic){
27952                 this.proxy.setStyle('visibility', 'visible');
27953             }
27954         }
27955     },
27956
27957     // private
27958     onMouseDown : function(handle, e){
27959         if(this.enabled){
27960             e.stopEvent();
27961             this.activeHandle = handle;
27962             this.startSizing(e, handle);
27963         }
27964     },
27965
27966     // private
27967     onMouseUp : function(e){
27968         var size = this.resizeElement();
27969         this.resizing = false;
27970         this.handleOut();
27971         this.overlay.hide();
27972         this.proxy.hide();
27973         this.fireEvent("resize", this, size.width, size.height, e);
27974     },
27975
27976     // private
27977     updateChildSize : function(){
27978         if(this.resizeChild){
27979             var el = this.el;
27980             var child = this.resizeChild;
27981             var adj = this.adjustments;
27982             if(el.dom.offsetWidth){
27983                 var b = el.getSize(true);
27984                 child.setSize(b.width+adj[0], b.height+adj[1]);
27985             }
27986             // Second call here for IE
27987             // The first call enables instant resizing and
27988             // the second call corrects scroll bars if they
27989             // exist
27990             if(Roo.isIE){
27991                 setTimeout(function(){
27992                     if(el.dom.offsetWidth){
27993                         var b = el.getSize(true);
27994                         child.setSize(b.width+adj[0], b.height+adj[1]);
27995                     }
27996                 }, 10);
27997             }
27998         }
27999     },
28000
28001     // private
28002     snap : function(value, inc, min){
28003         if(!inc || !value) return value;
28004         var newValue = value;
28005         var m = value % inc;
28006         if(m > 0){
28007             if(m > (inc/2)){
28008                 newValue = value + (inc-m);
28009             }else{
28010                 newValue = value - m;
28011             }
28012         }
28013         return Math.max(min, newValue);
28014     },
28015
28016     // private
28017     resizeElement : function(){
28018         var box = this.proxy.getBox();
28019         if(this.updateBox){
28020             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28021         }else{
28022             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28023         }
28024         this.updateChildSize();
28025         if(!this.dynamic){
28026             this.proxy.hide();
28027         }
28028         return box;
28029     },
28030
28031     // private
28032     constrain : function(v, diff, m, mx){
28033         if(v - diff < m){
28034             diff = v - m;
28035         }else if(v - diff > mx){
28036             diff = mx - v;
28037         }
28038         return diff;
28039     },
28040
28041     // private
28042     onMouseMove : function(e){
28043         if(this.enabled){
28044             try{// try catch so if something goes wrong the user doesn't get hung
28045
28046             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28047                 return;
28048             }
28049
28050             //var curXY = this.startPoint;
28051             var curSize = this.curSize || this.startBox;
28052             var x = this.startBox.x, y = this.startBox.y;
28053             var ox = x, oy = y;
28054             var w = curSize.width, h = curSize.height;
28055             var ow = w, oh = h;
28056             var mw = this.minWidth, mh = this.minHeight;
28057             var mxw = this.maxWidth, mxh = this.maxHeight;
28058             var wi = this.widthIncrement;
28059             var hi = this.heightIncrement;
28060
28061             var eventXY = e.getXY();
28062             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28063             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28064
28065             var pos = this.activeHandle.position;
28066
28067             switch(pos){
28068                 case "east":
28069                     w += diffX;
28070                     w = Math.min(Math.max(mw, w), mxw);
28071                     break;
28072              
28073                 case "south":
28074                     h += diffY;
28075                     h = Math.min(Math.max(mh, h), mxh);
28076                     break;
28077                 case "southeast":
28078                     w += diffX;
28079                     h += diffY;
28080                     w = Math.min(Math.max(mw, w), mxw);
28081                     h = Math.min(Math.max(mh, h), mxh);
28082                     break;
28083                 case "north":
28084                     diffY = this.constrain(h, diffY, mh, mxh);
28085                     y += diffY;
28086                     h -= diffY;
28087                     break;
28088                 case "hdrag":
28089                     
28090                     if (wi) {
28091                         var adiffX = Math.abs(diffX);
28092                         var sub = (adiffX % wi); // how much 
28093                         if (sub > (wi/2)) { // far enough to snap
28094                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28095                         } else {
28096                             // remove difference.. 
28097                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28098                         }
28099                     }
28100                     x += diffX;
28101                     x = Math.max(this.minX, x);
28102                     break;
28103                 case "west":
28104                     diffX = this.constrain(w, diffX, mw, mxw);
28105                     x += diffX;
28106                     w -= diffX;
28107                     break;
28108                 case "northeast":
28109                     w += diffX;
28110                     w = Math.min(Math.max(mw, w), mxw);
28111                     diffY = this.constrain(h, diffY, mh, mxh);
28112                     y += diffY;
28113                     h -= diffY;
28114                     break;
28115                 case "northwest":
28116                     diffX = this.constrain(w, diffX, mw, mxw);
28117                     diffY = this.constrain(h, diffY, mh, mxh);
28118                     y += diffY;
28119                     h -= diffY;
28120                     x += diffX;
28121                     w -= diffX;
28122                     break;
28123                case "southwest":
28124                     diffX = this.constrain(w, diffX, mw, mxw);
28125                     h += diffY;
28126                     h = Math.min(Math.max(mh, h), mxh);
28127                     x += diffX;
28128                     w -= diffX;
28129                     break;
28130             }
28131
28132             var sw = this.snap(w, wi, mw);
28133             var sh = this.snap(h, hi, mh);
28134             if(sw != w || sh != h){
28135                 switch(pos){
28136                     case "northeast":
28137                         y -= sh - h;
28138                     break;
28139                     case "north":
28140                         y -= sh - h;
28141                         break;
28142                     case "southwest":
28143                         x -= sw - w;
28144                     break;
28145                     case "west":
28146                         x -= sw - w;
28147                         break;
28148                     case "northwest":
28149                         x -= sw - w;
28150                         y -= sh - h;
28151                     break;
28152                 }
28153                 w = sw;
28154                 h = sh;
28155             }
28156
28157             if(this.preserveRatio){
28158                 switch(pos){
28159                     case "southeast":
28160                     case "east":
28161                         h = oh * (w/ow);
28162                         h = Math.min(Math.max(mh, h), mxh);
28163                         w = ow * (h/oh);
28164                        break;
28165                     case "south":
28166                         w = ow * (h/oh);
28167                         w = Math.min(Math.max(mw, w), mxw);
28168                         h = oh * (w/ow);
28169                         break;
28170                     case "northeast":
28171                         w = ow * (h/oh);
28172                         w = Math.min(Math.max(mw, w), mxw);
28173                         h = oh * (w/ow);
28174                     break;
28175                     case "north":
28176                         var tw = w;
28177                         w = ow * (h/oh);
28178                         w = Math.min(Math.max(mw, w), mxw);
28179                         h = oh * (w/ow);
28180                         x += (tw - w) / 2;
28181                         break;
28182                     case "southwest":
28183                         h = oh * (w/ow);
28184                         h = Math.min(Math.max(mh, h), mxh);
28185                         var tw = w;
28186                         w = ow * (h/oh);
28187                         x += tw - w;
28188                         break;
28189                     case "west":
28190                         var th = h;
28191                         h = oh * (w/ow);
28192                         h = Math.min(Math.max(mh, h), mxh);
28193                         y += (th - h) / 2;
28194                         var tw = w;
28195                         w = ow * (h/oh);
28196                         x += tw - w;
28197                        break;
28198                     case "northwest":
28199                         var tw = w;
28200                         var th = h;
28201                         h = oh * (w/ow);
28202                         h = Math.min(Math.max(mh, h), mxh);
28203                         w = ow * (h/oh);
28204                         y += th - h;
28205                         x += tw - w;
28206                        break;
28207
28208                 }
28209             }
28210             if (pos == 'hdrag') {
28211                 w = ow;
28212             }
28213             this.proxy.setBounds(x, y, w, h);
28214             if(this.dynamic){
28215                 this.resizeElement();
28216             }
28217             }catch(e){}
28218         }
28219     },
28220
28221     // private
28222     handleOver : function(){
28223         if(this.enabled){
28224             this.el.addClass("x-resizable-over");
28225         }
28226     },
28227
28228     // private
28229     handleOut : function(){
28230         if(!this.resizing){
28231             this.el.removeClass("x-resizable-over");
28232         }
28233     },
28234
28235     /**
28236      * Returns the element this component is bound to.
28237      * @return {Roo.Element}
28238      */
28239     getEl : function(){
28240         return this.el;
28241     },
28242
28243     /**
28244      * Returns the resizeChild element (or null).
28245      * @return {Roo.Element}
28246      */
28247     getResizeChild : function(){
28248         return this.resizeChild;
28249     },
28250
28251     /**
28252      * Destroys this resizable. If the element was wrapped and
28253      * removeEl is not true then the element remains.
28254      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28255      */
28256     destroy : function(removeEl){
28257         this.proxy.remove();
28258         if(this.overlay){
28259             this.overlay.removeAllListeners();
28260             this.overlay.remove();
28261         }
28262         var ps = Roo.Resizable.positions;
28263         for(var k in ps){
28264             if(typeof ps[k] != "function" && this[ps[k]]){
28265                 var h = this[ps[k]];
28266                 h.el.removeAllListeners();
28267                 h.el.remove();
28268             }
28269         }
28270         if(removeEl){
28271             this.el.update("");
28272             this.el.remove();
28273         }
28274     }
28275 });
28276
28277 // private
28278 // hash to map config positions to true positions
28279 Roo.Resizable.positions = {
28280     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28281     hd: "hdrag"
28282 };
28283
28284 // private
28285 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28286     if(!this.tpl){
28287         // only initialize the template if resizable is used
28288         var tpl = Roo.DomHelper.createTemplate(
28289             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28290         );
28291         tpl.compile();
28292         Roo.Resizable.Handle.prototype.tpl = tpl;
28293     }
28294     this.position = pos;
28295     this.rz = rz;
28296     // show north drag fro topdra
28297     var handlepos = pos == 'hdrag' ? 'north' : pos;
28298     
28299     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28300     if (pos == 'hdrag') {
28301         this.el.setStyle('cursor', 'pointer');
28302     }
28303     this.el.unselectable();
28304     if(transparent){
28305         this.el.setOpacity(0);
28306     }
28307     this.el.on("mousedown", this.onMouseDown, this);
28308     if(!disableTrackOver){
28309         this.el.on("mouseover", this.onMouseOver, this);
28310         this.el.on("mouseout", this.onMouseOut, this);
28311     }
28312 };
28313
28314 // private
28315 Roo.Resizable.Handle.prototype = {
28316     afterResize : function(rz){
28317         // do nothing
28318     },
28319     // private
28320     onMouseDown : function(e){
28321         this.rz.onMouseDown(this, e);
28322     },
28323     // private
28324     onMouseOver : function(e){
28325         this.rz.handleOver(this, e);
28326     },
28327     // private
28328     onMouseOut : function(e){
28329         this.rz.handleOut(this, e);
28330     }
28331 };/*
28332  * Based on:
28333  * Ext JS Library 1.1.1
28334  * Copyright(c) 2006-2007, Ext JS, LLC.
28335  *
28336  * Originally Released Under LGPL - original licence link has changed is not relivant.
28337  *
28338  * Fork - LGPL
28339  * <script type="text/javascript">
28340  */
28341
28342 /**
28343  * @class Roo.Editor
28344  * @extends Roo.Component
28345  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28346  * @constructor
28347  * Create a new Editor
28348  * @param {Roo.form.Field} field The Field object (or descendant)
28349  * @param {Object} config The config object
28350  */
28351 Roo.Editor = function(field, config){
28352     Roo.Editor.superclass.constructor.call(this, config);
28353     this.field = field;
28354     this.addEvents({
28355         /**
28356              * @event beforestartedit
28357              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28358              * false from the handler of this event.
28359              * @param {Editor} this
28360              * @param {Roo.Element} boundEl The underlying element bound to this editor
28361              * @param {Mixed} value The field value being set
28362              */
28363         "beforestartedit" : true,
28364         /**
28365              * @event startedit
28366              * Fires when this editor is displayed
28367              * @param {Roo.Element} boundEl The underlying element bound to this editor
28368              * @param {Mixed} value The starting field value
28369              */
28370         "startedit" : true,
28371         /**
28372              * @event beforecomplete
28373              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28374              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28375              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28376              * event will not fire since no edit actually occurred.
28377              * @param {Editor} this
28378              * @param {Mixed} value The current field value
28379              * @param {Mixed} startValue The original field value
28380              */
28381         "beforecomplete" : true,
28382         /**
28383              * @event complete
28384              * Fires after editing is complete and any changed value has been written to the underlying field.
28385              * @param {Editor} this
28386              * @param {Mixed} value The current field value
28387              * @param {Mixed} startValue The original field value
28388              */
28389         "complete" : true,
28390         /**
28391          * @event specialkey
28392          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28393          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28394          * @param {Roo.form.Field} this
28395          * @param {Roo.EventObject} e The event object
28396          */
28397         "specialkey" : true
28398     });
28399 };
28400
28401 Roo.extend(Roo.Editor, Roo.Component, {
28402     /**
28403      * @cfg {Boolean/String} autosize
28404      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28405      * or "height" to adopt the height only (defaults to false)
28406      */
28407     /**
28408      * @cfg {Boolean} revertInvalid
28409      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28410      * validation fails (defaults to true)
28411      */
28412     /**
28413      * @cfg {Boolean} ignoreNoChange
28414      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28415      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28416      * will never be ignored.
28417      */
28418     /**
28419      * @cfg {Boolean} hideEl
28420      * False to keep the bound element visible while the editor is displayed (defaults to true)
28421      */
28422     /**
28423      * @cfg {Mixed} value
28424      * The data value of the underlying field (defaults to "")
28425      */
28426     value : "",
28427     /**
28428      * @cfg {String} alignment
28429      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28430      */
28431     alignment: "c-c?",
28432     /**
28433      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28434      * for bottom-right shadow (defaults to "frame")
28435      */
28436     shadow : "frame",
28437     /**
28438      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28439      */
28440     constrain : false,
28441     /**
28442      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28443      */
28444     completeOnEnter : false,
28445     /**
28446      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28447      */
28448     cancelOnEsc : false,
28449     /**
28450      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28451      */
28452     updateEl : false,
28453
28454     // private
28455     onRender : function(ct, position){
28456         this.el = new Roo.Layer({
28457             shadow: this.shadow,
28458             cls: "x-editor",
28459             parentEl : ct,
28460             shim : this.shim,
28461             shadowOffset:4,
28462             id: this.id,
28463             constrain: this.constrain
28464         });
28465         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28466         if(this.field.msgTarget != 'title'){
28467             this.field.msgTarget = 'qtip';
28468         }
28469         this.field.render(this.el);
28470         if(Roo.isGecko){
28471             this.field.el.dom.setAttribute('autocomplete', 'off');
28472         }
28473         this.field.on("specialkey", this.onSpecialKey, this);
28474         if(this.swallowKeys){
28475             this.field.el.swallowEvent(['keydown','keypress']);
28476         }
28477         this.field.show();
28478         this.field.on("blur", this.onBlur, this);
28479         if(this.field.grow){
28480             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28481         }
28482     },
28483
28484     onSpecialKey : function(field, e)
28485     {
28486         //Roo.log('editor onSpecialKey');
28487         if(this.completeOnEnter && e.getKey() == e.ENTER){
28488             e.stopEvent();
28489             this.completeEdit();
28490             return;
28491         }
28492         // do not fire special key otherwise it might hide close the editor...
28493         if(e.getKey() == e.ENTER){    
28494             return;
28495         }
28496         if(this.cancelOnEsc && e.getKey() == e.ESC){
28497             this.cancelEdit();
28498             return;
28499         } 
28500         this.fireEvent('specialkey', field, e);
28501     
28502     },
28503
28504     /**
28505      * Starts the editing process and shows the editor.
28506      * @param {String/HTMLElement/Element} el The element to edit
28507      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28508       * to the innerHTML of el.
28509      */
28510     startEdit : function(el, value){
28511         if(this.editing){
28512             this.completeEdit();
28513         }
28514         this.boundEl = Roo.get(el);
28515         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28516         if(!this.rendered){
28517             this.render(this.parentEl || document.body);
28518         }
28519         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28520             return;
28521         }
28522         this.startValue = v;
28523         this.field.setValue(v);
28524         if(this.autoSize){
28525             var sz = this.boundEl.getSize();
28526             switch(this.autoSize){
28527                 case "width":
28528                 this.setSize(sz.width,  "");
28529                 break;
28530                 case "height":
28531                 this.setSize("",  sz.height);
28532                 break;
28533                 default:
28534                 this.setSize(sz.width,  sz.height);
28535             }
28536         }
28537         this.el.alignTo(this.boundEl, this.alignment);
28538         this.editing = true;
28539         if(Roo.QuickTips){
28540             Roo.QuickTips.disable();
28541         }
28542         this.show();
28543     },
28544
28545     /**
28546      * Sets the height and width of this editor.
28547      * @param {Number} width The new width
28548      * @param {Number} height The new height
28549      */
28550     setSize : function(w, h){
28551         this.field.setSize(w, h);
28552         if(this.el){
28553             this.el.sync();
28554         }
28555     },
28556
28557     /**
28558      * Realigns the editor to the bound field based on the current alignment config value.
28559      */
28560     realign : function(){
28561         this.el.alignTo(this.boundEl, this.alignment);
28562     },
28563
28564     /**
28565      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28566      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28567      */
28568     completeEdit : function(remainVisible){
28569         if(!this.editing){
28570             return;
28571         }
28572         var v = this.getValue();
28573         if(this.revertInvalid !== false && !this.field.isValid()){
28574             v = this.startValue;
28575             this.cancelEdit(true);
28576         }
28577         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28578             this.editing = false;
28579             this.hide();
28580             return;
28581         }
28582         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28583             this.editing = false;
28584             if(this.updateEl && this.boundEl){
28585                 this.boundEl.update(v);
28586             }
28587             if(remainVisible !== true){
28588                 this.hide();
28589             }
28590             this.fireEvent("complete", this, v, this.startValue);
28591         }
28592     },
28593
28594     // private
28595     onShow : function(){
28596         this.el.show();
28597         if(this.hideEl !== false){
28598             this.boundEl.hide();
28599         }
28600         this.field.show();
28601         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28602             this.fixIEFocus = true;
28603             this.deferredFocus.defer(50, this);
28604         }else{
28605             this.field.focus();
28606         }
28607         this.fireEvent("startedit", this.boundEl, this.startValue);
28608     },
28609
28610     deferredFocus : function(){
28611         if(this.editing){
28612             this.field.focus();
28613         }
28614     },
28615
28616     /**
28617      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28618      * reverted to the original starting value.
28619      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28620      * cancel (defaults to false)
28621      */
28622     cancelEdit : function(remainVisible){
28623         if(this.editing){
28624             this.setValue(this.startValue);
28625             if(remainVisible !== true){
28626                 this.hide();
28627             }
28628         }
28629     },
28630
28631     // private
28632     onBlur : function(){
28633         if(this.allowBlur !== true && this.editing){
28634             this.completeEdit();
28635         }
28636     },
28637
28638     // private
28639     onHide : function(){
28640         if(this.editing){
28641             this.completeEdit();
28642             return;
28643         }
28644         this.field.blur();
28645         if(this.field.collapse){
28646             this.field.collapse();
28647         }
28648         this.el.hide();
28649         if(this.hideEl !== false){
28650             this.boundEl.show();
28651         }
28652         if(Roo.QuickTips){
28653             Roo.QuickTips.enable();
28654         }
28655     },
28656
28657     /**
28658      * Sets the data value of the editor
28659      * @param {Mixed} value Any valid value supported by the underlying field
28660      */
28661     setValue : function(v){
28662         this.field.setValue(v);
28663     },
28664
28665     /**
28666      * Gets the data value of the editor
28667      * @return {Mixed} The data value
28668      */
28669     getValue : function(){
28670         return this.field.getValue();
28671     }
28672 });/*
28673  * Based on:
28674  * Ext JS Library 1.1.1
28675  * Copyright(c) 2006-2007, Ext JS, LLC.
28676  *
28677  * Originally Released Under LGPL - original licence link has changed is not relivant.
28678  *
28679  * Fork - LGPL
28680  * <script type="text/javascript">
28681  */
28682  
28683 /**
28684  * @class Roo.BasicDialog
28685  * @extends Roo.util.Observable
28686  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28687  * <pre><code>
28688 var dlg = new Roo.BasicDialog("my-dlg", {
28689     height: 200,
28690     width: 300,
28691     minHeight: 100,
28692     minWidth: 150,
28693     modal: true,
28694     proxyDrag: true,
28695     shadow: true
28696 });
28697 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28698 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28699 dlg.addButton('Cancel', dlg.hide, dlg);
28700 dlg.show();
28701 </code></pre>
28702   <b>A Dialog should always be a direct child of the body element.</b>
28703  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28704  * @cfg {String} title Default text to display in the title bar (defaults to null)
28705  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28706  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28707  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28708  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28709  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28710  * (defaults to null with no animation)
28711  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28712  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28713  * property for valid values (defaults to 'all')
28714  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28715  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28716  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28717  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28718  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28719  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28720  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28721  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28722  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28723  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28724  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28725  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28726  * draggable = true (defaults to false)
28727  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28728  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28729  * shadow (defaults to false)
28730  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28731  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28732  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28733  * @cfg {Array} buttons Array of buttons
28734  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28735  * @constructor
28736  * Create a new BasicDialog.
28737  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28738  * @param {Object} config Configuration options
28739  */
28740 Roo.BasicDialog = function(el, config){
28741     this.el = Roo.get(el);
28742     var dh = Roo.DomHelper;
28743     if(!this.el && config && config.autoCreate){
28744         if(typeof config.autoCreate == "object"){
28745             if(!config.autoCreate.id){
28746                 config.autoCreate.id = el;
28747             }
28748             this.el = dh.append(document.body,
28749                         config.autoCreate, true);
28750         }else{
28751             this.el = dh.append(document.body,
28752                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28753         }
28754     }
28755     el = this.el;
28756     el.setDisplayed(true);
28757     el.hide = this.hideAction;
28758     this.id = el.id;
28759     el.addClass("x-dlg");
28760
28761     Roo.apply(this, config);
28762
28763     this.proxy = el.createProxy("x-dlg-proxy");
28764     this.proxy.hide = this.hideAction;
28765     this.proxy.setOpacity(.5);
28766     this.proxy.hide();
28767
28768     if(config.width){
28769         el.setWidth(config.width);
28770     }
28771     if(config.height){
28772         el.setHeight(config.height);
28773     }
28774     this.size = el.getSize();
28775     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28776         this.xy = [config.x,config.y];
28777     }else{
28778         this.xy = el.getCenterXY(true);
28779     }
28780     /** The header element @type Roo.Element */
28781     this.header = el.child("> .x-dlg-hd");
28782     /** The body element @type Roo.Element */
28783     this.body = el.child("> .x-dlg-bd");
28784     /** The footer element @type Roo.Element */
28785     this.footer = el.child("> .x-dlg-ft");
28786
28787     if(!this.header){
28788         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28789     }
28790     if(!this.body){
28791         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28792     }
28793
28794     this.header.unselectable();
28795     if(this.title){
28796         this.header.update(this.title);
28797     }
28798     // this element allows the dialog to be focused for keyboard event
28799     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28800     this.focusEl.swallowEvent("click", true);
28801
28802     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28803
28804     // wrap the body and footer for special rendering
28805     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28806     if(this.footer){
28807         this.bwrap.dom.appendChild(this.footer.dom);
28808     }
28809
28810     this.bg = this.el.createChild({
28811         tag: "div", cls:"x-dlg-bg",
28812         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28813     });
28814     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28815
28816
28817     if(this.autoScroll !== false && !this.autoTabs){
28818         this.body.setStyle("overflow", "auto");
28819     }
28820
28821     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28822
28823     if(this.closable !== false){
28824         this.el.addClass("x-dlg-closable");
28825         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28826         this.close.on("click", this.closeClick, this);
28827         this.close.addClassOnOver("x-dlg-close-over");
28828     }
28829     if(this.collapsible !== false){
28830         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28831         this.collapseBtn.on("click", this.collapseClick, this);
28832         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28833         this.header.on("dblclick", this.collapseClick, this);
28834     }
28835     if(this.resizable !== false){
28836         this.el.addClass("x-dlg-resizable");
28837         this.resizer = new Roo.Resizable(el, {
28838             minWidth: this.minWidth || 80,
28839             minHeight:this.minHeight || 80,
28840             handles: this.resizeHandles || "all",
28841             pinned: true
28842         });
28843         this.resizer.on("beforeresize", this.beforeResize, this);
28844         this.resizer.on("resize", this.onResize, this);
28845     }
28846     if(this.draggable !== false){
28847         el.addClass("x-dlg-draggable");
28848         if (!this.proxyDrag) {
28849             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28850         }
28851         else {
28852             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28853         }
28854         dd.setHandleElId(this.header.id);
28855         dd.endDrag = this.endMove.createDelegate(this);
28856         dd.startDrag = this.startMove.createDelegate(this);
28857         dd.onDrag = this.onDrag.createDelegate(this);
28858         dd.scroll = false;
28859         this.dd = dd;
28860     }
28861     if(this.modal){
28862         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28863         this.mask.enableDisplayMode("block");
28864         this.mask.hide();
28865         this.el.addClass("x-dlg-modal");
28866     }
28867     if(this.shadow){
28868         this.shadow = new Roo.Shadow({
28869             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28870             offset : this.shadowOffset
28871         });
28872     }else{
28873         this.shadowOffset = 0;
28874     }
28875     if(Roo.useShims && this.shim !== false){
28876         this.shim = this.el.createShim();
28877         this.shim.hide = this.hideAction;
28878         this.shim.hide();
28879     }else{
28880         this.shim = false;
28881     }
28882     if(this.autoTabs){
28883         this.initTabs();
28884     }
28885     if (this.buttons) { 
28886         var bts= this.buttons;
28887         this.buttons = [];
28888         Roo.each(bts, function(b) {
28889             this.addButton(b);
28890         }, this);
28891     }
28892     
28893     
28894     this.addEvents({
28895         /**
28896          * @event keydown
28897          * Fires when a key is pressed
28898          * @param {Roo.BasicDialog} this
28899          * @param {Roo.EventObject} e
28900          */
28901         "keydown" : true,
28902         /**
28903          * @event move
28904          * Fires when this dialog is moved by the user.
28905          * @param {Roo.BasicDialog} this
28906          * @param {Number} x The new page X
28907          * @param {Number} y The new page Y
28908          */
28909         "move" : true,
28910         /**
28911          * @event resize
28912          * Fires when this dialog is resized by the user.
28913          * @param {Roo.BasicDialog} this
28914          * @param {Number} width The new width
28915          * @param {Number} height The new height
28916          */
28917         "resize" : true,
28918         /**
28919          * @event beforehide
28920          * Fires before this dialog is hidden.
28921          * @param {Roo.BasicDialog} this
28922          */
28923         "beforehide" : true,
28924         /**
28925          * @event hide
28926          * Fires when this dialog is hidden.
28927          * @param {Roo.BasicDialog} this
28928          */
28929         "hide" : true,
28930         /**
28931          * @event beforeshow
28932          * Fires before this dialog is shown.
28933          * @param {Roo.BasicDialog} this
28934          */
28935         "beforeshow" : true,
28936         /**
28937          * @event show
28938          * Fires when this dialog is shown.
28939          * @param {Roo.BasicDialog} this
28940          */
28941         "show" : true
28942     });
28943     el.on("keydown", this.onKeyDown, this);
28944     el.on("mousedown", this.toFront, this);
28945     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28946     this.el.hide();
28947     Roo.DialogManager.register(this);
28948     Roo.BasicDialog.superclass.constructor.call(this);
28949 };
28950
28951 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28952     shadowOffset: Roo.isIE ? 6 : 5,
28953     minHeight: 80,
28954     minWidth: 200,
28955     minButtonWidth: 75,
28956     defaultButton: null,
28957     buttonAlign: "right",
28958     tabTag: 'div',
28959     firstShow: true,
28960
28961     /**
28962      * Sets the dialog title text
28963      * @param {String} text The title text to display
28964      * @return {Roo.BasicDialog} this
28965      */
28966     setTitle : function(text){
28967         this.header.update(text);
28968         return this;
28969     },
28970
28971     // private
28972     closeClick : function(){
28973         this.hide();
28974     },
28975
28976     // private
28977     collapseClick : function(){
28978         this[this.collapsed ? "expand" : "collapse"]();
28979     },
28980
28981     /**
28982      * Collapses the dialog to its minimized state (only the title bar is visible).
28983      * Equivalent to the user clicking the collapse dialog button.
28984      */
28985     collapse : function(){
28986         if(!this.collapsed){
28987             this.collapsed = true;
28988             this.el.addClass("x-dlg-collapsed");
28989             this.restoreHeight = this.el.getHeight();
28990             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28991         }
28992     },
28993
28994     /**
28995      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28996      * clicking the expand dialog button.
28997      */
28998     expand : function(){
28999         if(this.collapsed){
29000             this.collapsed = false;
29001             this.el.removeClass("x-dlg-collapsed");
29002             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29003         }
29004     },
29005
29006     /**
29007      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29008      * @return {Roo.TabPanel} The tabs component
29009      */
29010     initTabs : function(){
29011         var tabs = this.getTabs();
29012         while(tabs.getTab(0)){
29013             tabs.removeTab(0);
29014         }
29015         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29016             var dom = el.dom;
29017             tabs.addTab(Roo.id(dom), dom.title);
29018             dom.title = "";
29019         });
29020         tabs.activate(0);
29021         return tabs;
29022     },
29023
29024     // private
29025     beforeResize : function(){
29026         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29027     },
29028
29029     // private
29030     onResize : function(){
29031         this.refreshSize();
29032         this.syncBodyHeight();
29033         this.adjustAssets();
29034         this.focus();
29035         this.fireEvent("resize", this, this.size.width, this.size.height);
29036     },
29037
29038     // private
29039     onKeyDown : function(e){
29040         if(this.isVisible()){
29041             this.fireEvent("keydown", this, e);
29042         }
29043     },
29044
29045     /**
29046      * Resizes the dialog.
29047      * @param {Number} width
29048      * @param {Number} height
29049      * @return {Roo.BasicDialog} this
29050      */
29051     resizeTo : function(width, height){
29052         this.el.setSize(width, height);
29053         this.size = {width: width, height: height};
29054         this.syncBodyHeight();
29055         if(this.fixedcenter){
29056             this.center();
29057         }
29058         if(this.isVisible()){
29059             this.constrainXY();
29060             this.adjustAssets();
29061         }
29062         this.fireEvent("resize", this, width, height);
29063         return this;
29064     },
29065
29066
29067     /**
29068      * Resizes the dialog to fit the specified content size.
29069      * @param {Number} width
29070      * @param {Number} height
29071      * @return {Roo.BasicDialog} this
29072      */
29073     setContentSize : function(w, h){
29074         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29075         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29076         //if(!this.el.isBorderBox()){
29077             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29078             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29079         //}
29080         if(this.tabs){
29081             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29082             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29083         }
29084         this.resizeTo(w, h);
29085         return this;
29086     },
29087
29088     /**
29089      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29090      * executed in response to a particular key being pressed while the dialog is active.
29091      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29092      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29093      * @param {Function} fn The function to call
29094      * @param {Object} scope (optional) The scope of the function
29095      * @return {Roo.BasicDialog} this
29096      */
29097     addKeyListener : function(key, fn, scope){
29098         var keyCode, shift, ctrl, alt;
29099         if(typeof key == "object" && !(key instanceof Array)){
29100             keyCode = key["key"];
29101             shift = key["shift"];
29102             ctrl = key["ctrl"];
29103             alt = key["alt"];
29104         }else{
29105             keyCode = key;
29106         }
29107         var handler = function(dlg, e){
29108             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29109                 var k = e.getKey();
29110                 if(keyCode instanceof Array){
29111                     for(var i = 0, len = keyCode.length; i < len; i++){
29112                         if(keyCode[i] == k){
29113                           fn.call(scope || window, dlg, k, e);
29114                           return;
29115                         }
29116                     }
29117                 }else{
29118                     if(k == keyCode){
29119                         fn.call(scope || window, dlg, k, e);
29120                     }
29121                 }
29122             }
29123         };
29124         this.on("keydown", handler);
29125         return this;
29126     },
29127
29128     /**
29129      * Returns the TabPanel component (creates it if it doesn't exist).
29130      * Note: If you wish to simply check for the existence of tabs without creating them,
29131      * check for a null 'tabs' property.
29132      * @return {Roo.TabPanel} The tabs component
29133      */
29134     getTabs : function(){
29135         if(!this.tabs){
29136             this.el.addClass("x-dlg-auto-tabs");
29137             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29138             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29139         }
29140         return this.tabs;
29141     },
29142
29143     /**
29144      * Adds a button to the footer section of the dialog.
29145      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29146      * object or a valid Roo.DomHelper element config
29147      * @param {Function} handler The function called when the button is clicked
29148      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29149      * @return {Roo.Button} The new button
29150      */
29151     addButton : function(config, handler, scope){
29152         var dh = Roo.DomHelper;
29153         if(!this.footer){
29154             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29155         }
29156         if(!this.btnContainer){
29157             var tb = this.footer.createChild({
29158
29159                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29160                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29161             }, null, true);
29162             this.btnContainer = tb.firstChild.firstChild.firstChild;
29163         }
29164         var bconfig = {
29165             handler: handler,
29166             scope: scope,
29167             minWidth: this.minButtonWidth,
29168             hideParent:true
29169         };
29170         if(typeof config == "string"){
29171             bconfig.text = config;
29172         }else{
29173             if(config.tag){
29174                 bconfig.dhconfig = config;
29175             }else{
29176                 Roo.apply(bconfig, config);
29177             }
29178         }
29179         var fc = false;
29180         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29181             bconfig.position = Math.max(0, bconfig.position);
29182             fc = this.btnContainer.childNodes[bconfig.position];
29183         }
29184          
29185         var btn = new Roo.Button(
29186             fc ? 
29187                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29188                 : this.btnContainer.appendChild(document.createElement("td")),
29189             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29190             bconfig
29191         );
29192         this.syncBodyHeight();
29193         if(!this.buttons){
29194             /**
29195              * Array of all the buttons that have been added to this dialog via addButton
29196              * @type Array
29197              */
29198             this.buttons = [];
29199         }
29200         this.buttons.push(btn);
29201         return btn;
29202     },
29203
29204     /**
29205      * Sets the default button to be focused when the dialog is displayed.
29206      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29207      * @return {Roo.BasicDialog} this
29208      */
29209     setDefaultButton : function(btn){
29210         this.defaultButton = btn;
29211         return this;
29212     },
29213
29214     // private
29215     getHeaderFooterHeight : function(safe){
29216         var height = 0;
29217         if(this.header){
29218            height += this.header.getHeight();
29219         }
29220         if(this.footer){
29221            var fm = this.footer.getMargins();
29222             height += (this.footer.getHeight()+fm.top+fm.bottom);
29223         }
29224         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29225         height += this.centerBg.getPadding("tb");
29226         return height;
29227     },
29228
29229     // private
29230     syncBodyHeight : function(){
29231         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29232         var height = this.size.height - this.getHeaderFooterHeight(false);
29233         bd.setHeight(height-bd.getMargins("tb"));
29234         var hh = this.header.getHeight();
29235         var h = this.size.height-hh;
29236         cb.setHeight(h);
29237         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29238         bw.setHeight(h-cb.getPadding("tb"));
29239         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29240         bd.setWidth(bw.getWidth(true));
29241         if(this.tabs){
29242             this.tabs.syncHeight();
29243             if(Roo.isIE){
29244                 this.tabs.el.repaint();
29245             }
29246         }
29247     },
29248
29249     /**
29250      * Restores the previous state of the dialog if Roo.state is configured.
29251      * @return {Roo.BasicDialog} this
29252      */
29253     restoreState : function(){
29254         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29255         if(box && box.width){
29256             this.xy = [box.x, box.y];
29257             this.resizeTo(box.width, box.height);
29258         }
29259         return this;
29260     },
29261
29262     // private
29263     beforeShow : function(){
29264         this.expand();
29265         if(this.fixedcenter){
29266             this.xy = this.el.getCenterXY(true);
29267         }
29268         if(this.modal){
29269             Roo.get(document.body).addClass("x-body-masked");
29270             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29271             this.mask.show();
29272         }
29273         this.constrainXY();
29274     },
29275
29276     // private
29277     animShow : function(){
29278         var b = Roo.get(this.animateTarget).getBox();
29279         this.proxy.setSize(b.width, b.height);
29280         this.proxy.setLocation(b.x, b.y);
29281         this.proxy.show();
29282         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29283                     true, .35, this.showEl.createDelegate(this));
29284     },
29285
29286     /**
29287      * Shows the dialog.
29288      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29289      * @return {Roo.BasicDialog} this
29290      */
29291     show : function(animateTarget){
29292         if (this.fireEvent("beforeshow", this) === false){
29293             return;
29294         }
29295         if(this.syncHeightBeforeShow){
29296             this.syncBodyHeight();
29297         }else if(this.firstShow){
29298             this.firstShow = false;
29299             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29300         }
29301         this.animateTarget = animateTarget || this.animateTarget;
29302         if(!this.el.isVisible()){
29303             this.beforeShow();
29304             if(this.animateTarget && Roo.get(this.animateTarget)){
29305                 this.animShow();
29306             }else{
29307                 this.showEl();
29308             }
29309         }
29310         return this;
29311     },
29312
29313     // private
29314     showEl : function(){
29315         this.proxy.hide();
29316         this.el.setXY(this.xy);
29317         this.el.show();
29318         this.adjustAssets(true);
29319         this.toFront();
29320         this.focus();
29321         // IE peekaboo bug - fix found by Dave Fenwick
29322         if(Roo.isIE){
29323             this.el.repaint();
29324         }
29325         this.fireEvent("show", this);
29326     },
29327
29328     /**
29329      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29330      * dialog itself will receive focus.
29331      */
29332     focus : function(){
29333         if(this.defaultButton){
29334             this.defaultButton.focus();
29335         }else{
29336             this.focusEl.focus();
29337         }
29338     },
29339
29340     // private
29341     constrainXY : function(){
29342         if(this.constraintoviewport !== false){
29343             if(!this.viewSize){
29344                 if(this.container){
29345                     var s = this.container.getSize();
29346                     this.viewSize = [s.width, s.height];
29347                 }else{
29348                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29349                 }
29350             }
29351             var s = Roo.get(this.container||document).getScroll();
29352
29353             var x = this.xy[0], y = this.xy[1];
29354             var w = this.size.width, h = this.size.height;
29355             var vw = this.viewSize[0], vh = this.viewSize[1];
29356             // only move it if it needs it
29357             var moved = false;
29358             // first validate right/bottom
29359             if(x + w > vw+s.left){
29360                 x = vw - w;
29361                 moved = true;
29362             }
29363             if(y + h > vh+s.top){
29364                 y = vh - h;
29365                 moved = true;
29366             }
29367             // then make sure top/left isn't negative
29368             if(x < s.left){
29369                 x = s.left;
29370                 moved = true;
29371             }
29372             if(y < s.top){
29373                 y = s.top;
29374                 moved = true;
29375             }
29376             if(moved){
29377                 // cache xy
29378                 this.xy = [x, y];
29379                 if(this.isVisible()){
29380                     this.el.setLocation(x, y);
29381                     this.adjustAssets();
29382                 }
29383             }
29384         }
29385     },
29386
29387     // private
29388     onDrag : function(){
29389         if(!this.proxyDrag){
29390             this.xy = this.el.getXY();
29391             this.adjustAssets();
29392         }
29393     },
29394
29395     // private
29396     adjustAssets : function(doShow){
29397         var x = this.xy[0], y = this.xy[1];
29398         var w = this.size.width, h = this.size.height;
29399         if(doShow === true){
29400             if(this.shadow){
29401                 this.shadow.show(this.el);
29402             }
29403             if(this.shim){
29404                 this.shim.show();
29405             }
29406         }
29407         if(this.shadow && this.shadow.isVisible()){
29408             this.shadow.show(this.el);
29409         }
29410         if(this.shim && this.shim.isVisible()){
29411             this.shim.setBounds(x, y, w, h);
29412         }
29413     },
29414
29415     // private
29416     adjustViewport : function(w, h){
29417         if(!w || !h){
29418             w = Roo.lib.Dom.getViewWidth();
29419             h = Roo.lib.Dom.getViewHeight();
29420         }
29421         // cache the size
29422         this.viewSize = [w, h];
29423         if(this.modal && this.mask.isVisible()){
29424             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29425             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29426         }
29427         if(this.isVisible()){
29428             this.constrainXY();
29429         }
29430     },
29431
29432     /**
29433      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29434      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29435      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29436      */
29437     destroy : function(removeEl){
29438         if(this.isVisible()){
29439             this.animateTarget = null;
29440             this.hide();
29441         }
29442         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29443         if(this.tabs){
29444             this.tabs.destroy(removeEl);
29445         }
29446         Roo.destroy(
29447              this.shim,
29448              this.proxy,
29449              this.resizer,
29450              this.close,
29451              this.mask
29452         );
29453         if(this.dd){
29454             this.dd.unreg();
29455         }
29456         if(this.buttons){
29457            for(var i = 0, len = this.buttons.length; i < len; i++){
29458                this.buttons[i].destroy();
29459            }
29460         }
29461         this.el.removeAllListeners();
29462         if(removeEl === true){
29463             this.el.update("");
29464             this.el.remove();
29465         }
29466         Roo.DialogManager.unregister(this);
29467     },
29468
29469     // private
29470     startMove : function(){
29471         if(this.proxyDrag){
29472             this.proxy.show();
29473         }
29474         if(this.constraintoviewport !== false){
29475             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29476         }
29477     },
29478
29479     // private
29480     endMove : function(){
29481         if(!this.proxyDrag){
29482             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29483         }else{
29484             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29485             this.proxy.hide();
29486         }
29487         this.refreshSize();
29488         this.adjustAssets();
29489         this.focus();
29490         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29491     },
29492
29493     /**
29494      * Brings this dialog to the front of any other visible dialogs
29495      * @return {Roo.BasicDialog} this
29496      */
29497     toFront : function(){
29498         Roo.DialogManager.bringToFront(this);
29499         return this;
29500     },
29501
29502     /**
29503      * Sends this dialog to the back (under) of any other visible dialogs
29504      * @return {Roo.BasicDialog} this
29505      */
29506     toBack : function(){
29507         Roo.DialogManager.sendToBack(this);
29508         return this;
29509     },
29510
29511     /**
29512      * Centers this dialog in the viewport
29513      * @return {Roo.BasicDialog} this
29514      */
29515     center : function(){
29516         var xy = this.el.getCenterXY(true);
29517         this.moveTo(xy[0], xy[1]);
29518         return this;
29519     },
29520
29521     /**
29522      * Moves the dialog's top-left corner to the specified point
29523      * @param {Number} x
29524      * @param {Number} y
29525      * @return {Roo.BasicDialog} this
29526      */
29527     moveTo : function(x, y){
29528         this.xy = [x,y];
29529         if(this.isVisible()){
29530             this.el.setXY(this.xy);
29531             this.adjustAssets();
29532         }
29533         return this;
29534     },
29535
29536     /**
29537      * Aligns the dialog to the specified element
29538      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29539      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29540      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29541      * @return {Roo.BasicDialog} this
29542      */
29543     alignTo : function(element, position, offsets){
29544         this.xy = this.el.getAlignToXY(element, position, offsets);
29545         if(this.isVisible()){
29546             this.el.setXY(this.xy);
29547             this.adjustAssets();
29548         }
29549         return this;
29550     },
29551
29552     /**
29553      * Anchors an element to another element and realigns it when the window is resized.
29554      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29555      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29556      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29557      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29558      * is a number, it is used as the buffer delay (defaults to 50ms).
29559      * @return {Roo.BasicDialog} this
29560      */
29561     anchorTo : function(el, alignment, offsets, monitorScroll){
29562         var action = function(){
29563             this.alignTo(el, alignment, offsets);
29564         };
29565         Roo.EventManager.onWindowResize(action, this);
29566         var tm = typeof monitorScroll;
29567         if(tm != 'undefined'){
29568             Roo.EventManager.on(window, 'scroll', action, this,
29569                 {buffer: tm == 'number' ? monitorScroll : 50});
29570         }
29571         action.call(this);
29572         return this;
29573     },
29574
29575     /**
29576      * Returns true if the dialog is visible
29577      * @return {Boolean}
29578      */
29579     isVisible : function(){
29580         return this.el.isVisible();
29581     },
29582
29583     // private
29584     animHide : function(callback){
29585         var b = Roo.get(this.animateTarget).getBox();
29586         this.proxy.show();
29587         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29588         this.el.hide();
29589         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29590                     this.hideEl.createDelegate(this, [callback]));
29591     },
29592
29593     /**
29594      * Hides the dialog.
29595      * @param {Function} callback (optional) Function to call when the dialog is hidden
29596      * @return {Roo.BasicDialog} this
29597      */
29598     hide : function(callback){
29599         if (this.fireEvent("beforehide", this) === false){
29600             return;
29601         }
29602         if(this.shadow){
29603             this.shadow.hide();
29604         }
29605         if(this.shim) {
29606           this.shim.hide();
29607         }
29608         // sometimes animateTarget seems to get set.. causing problems...
29609         // this just double checks..
29610         if(this.animateTarget && Roo.get(this.animateTarget)) {
29611            this.animHide(callback);
29612         }else{
29613             this.el.hide();
29614             this.hideEl(callback);
29615         }
29616         return this;
29617     },
29618
29619     // private
29620     hideEl : function(callback){
29621         this.proxy.hide();
29622         if(this.modal){
29623             this.mask.hide();
29624             Roo.get(document.body).removeClass("x-body-masked");
29625         }
29626         this.fireEvent("hide", this);
29627         if(typeof callback == "function"){
29628             callback();
29629         }
29630     },
29631
29632     // private
29633     hideAction : function(){
29634         this.setLeft("-10000px");
29635         this.setTop("-10000px");
29636         this.setStyle("visibility", "hidden");
29637     },
29638
29639     // private
29640     refreshSize : function(){
29641         this.size = this.el.getSize();
29642         this.xy = this.el.getXY();
29643         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29644     },
29645
29646     // private
29647     // z-index is managed by the DialogManager and may be overwritten at any time
29648     setZIndex : function(index){
29649         if(this.modal){
29650             this.mask.setStyle("z-index", index);
29651         }
29652         if(this.shim){
29653             this.shim.setStyle("z-index", ++index);
29654         }
29655         if(this.shadow){
29656             this.shadow.setZIndex(++index);
29657         }
29658         this.el.setStyle("z-index", ++index);
29659         if(this.proxy){
29660             this.proxy.setStyle("z-index", ++index);
29661         }
29662         if(this.resizer){
29663             this.resizer.proxy.setStyle("z-index", ++index);
29664         }
29665
29666         this.lastZIndex = index;
29667     },
29668
29669     /**
29670      * Returns the element for this dialog
29671      * @return {Roo.Element} The underlying dialog Element
29672      */
29673     getEl : function(){
29674         return this.el;
29675     }
29676 });
29677
29678 /**
29679  * @class Roo.DialogManager
29680  * Provides global access to BasicDialogs that have been created and
29681  * support for z-indexing (layering) multiple open dialogs.
29682  */
29683 Roo.DialogManager = function(){
29684     var list = {};
29685     var accessList = [];
29686     var front = null;
29687
29688     // private
29689     var sortDialogs = function(d1, d2){
29690         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29691     };
29692
29693     // private
29694     var orderDialogs = function(){
29695         accessList.sort(sortDialogs);
29696         var seed = Roo.DialogManager.zseed;
29697         for(var i = 0, len = accessList.length; i < len; i++){
29698             var dlg = accessList[i];
29699             if(dlg){
29700                 dlg.setZIndex(seed + (i*10));
29701             }
29702         }
29703     };
29704
29705     return {
29706         /**
29707          * The starting z-index for BasicDialogs (defaults to 9000)
29708          * @type Number The z-index value
29709          */
29710         zseed : 9000,
29711
29712         // private
29713         register : function(dlg){
29714             list[dlg.id] = dlg;
29715             accessList.push(dlg);
29716         },
29717
29718         // private
29719         unregister : function(dlg){
29720             delete list[dlg.id];
29721             var i=0;
29722             var len=0;
29723             if(!accessList.indexOf){
29724                 for(  i = 0, len = accessList.length; i < len; i++){
29725                     if(accessList[i] == dlg){
29726                         accessList.splice(i, 1);
29727                         return;
29728                     }
29729                 }
29730             }else{
29731                  i = accessList.indexOf(dlg);
29732                 if(i != -1){
29733                     accessList.splice(i, 1);
29734                 }
29735             }
29736         },
29737
29738         /**
29739          * Gets a registered dialog by id
29740          * @param {String/Object} id The id of the dialog or a dialog
29741          * @return {Roo.BasicDialog} this
29742          */
29743         get : function(id){
29744             return typeof id == "object" ? id : list[id];
29745         },
29746
29747         /**
29748          * Brings the specified dialog to the front
29749          * @param {String/Object} dlg The id of the dialog or a dialog
29750          * @return {Roo.BasicDialog} this
29751          */
29752         bringToFront : function(dlg){
29753             dlg = this.get(dlg);
29754             if(dlg != front){
29755                 front = dlg;
29756                 dlg._lastAccess = new Date().getTime();
29757                 orderDialogs();
29758             }
29759             return dlg;
29760         },
29761
29762         /**
29763          * Sends the specified dialog to the back
29764          * @param {String/Object} dlg The id of the dialog or a dialog
29765          * @return {Roo.BasicDialog} this
29766          */
29767         sendToBack : function(dlg){
29768             dlg = this.get(dlg);
29769             dlg._lastAccess = -(new Date().getTime());
29770             orderDialogs();
29771             return dlg;
29772         },
29773
29774         /**
29775          * Hides all dialogs
29776          */
29777         hideAll : function(){
29778             for(var id in list){
29779                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29780                     list[id].hide();
29781                 }
29782             }
29783         }
29784     };
29785 }();
29786
29787 /**
29788  * @class Roo.LayoutDialog
29789  * @extends Roo.BasicDialog
29790  * Dialog which provides adjustments for working with a layout in a Dialog.
29791  * Add your necessary layout config options to the dialog's config.<br>
29792  * Example usage (including a nested layout):
29793  * <pre><code>
29794 if(!dialog){
29795     dialog = new Roo.LayoutDialog("download-dlg", {
29796         modal: true,
29797         width:600,
29798         height:450,
29799         shadow:true,
29800         minWidth:500,
29801         minHeight:350,
29802         autoTabs:true,
29803         proxyDrag:true,
29804         // layout config merges with the dialog config
29805         center:{
29806             tabPosition: "top",
29807             alwaysShowTabs: true
29808         }
29809     });
29810     dialog.addKeyListener(27, dialog.hide, dialog);
29811     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29812     dialog.addButton("Build It!", this.getDownload, this);
29813
29814     // we can even add nested layouts
29815     var innerLayout = new Roo.BorderLayout("dl-inner", {
29816         east: {
29817             initialSize: 200,
29818             autoScroll:true,
29819             split:true
29820         },
29821         center: {
29822             autoScroll:true
29823         }
29824     });
29825     innerLayout.beginUpdate();
29826     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29827     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29828     innerLayout.endUpdate(true);
29829
29830     var layout = dialog.getLayout();
29831     layout.beginUpdate();
29832     layout.add("center", new Roo.ContentPanel("standard-panel",
29833                         {title: "Download the Source", fitToFrame:true}));
29834     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29835                {title: "Build your own roo.js"}));
29836     layout.getRegion("center").showPanel(sp);
29837     layout.endUpdate();
29838 }
29839 </code></pre>
29840     * @constructor
29841     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29842     * @param {Object} config configuration options
29843   */
29844 Roo.LayoutDialog = function(el, cfg){
29845     
29846     var config=  cfg;
29847     if (typeof(cfg) == 'undefined') {
29848         config = Roo.apply({}, el);
29849         // not sure why we use documentElement here.. - it should always be body.
29850         // IE7 borks horribly if we use documentElement.
29851         // webkit also does not like documentElement - it creates a body element...
29852         el = Roo.get( document.body || document.documentElement ).createChild();
29853         //config.autoCreate = true;
29854     }
29855     
29856     
29857     config.autoTabs = false;
29858     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29859     this.body.setStyle({overflow:"hidden", position:"relative"});
29860     this.layout = new Roo.BorderLayout(this.body.dom, config);
29861     this.layout.monitorWindowResize = false;
29862     this.el.addClass("x-dlg-auto-layout");
29863     // fix case when center region overwrites center function
29864     this.center = Roo.BasicDialog.prototype.center;
29865     this.on("show", this.layout.layout, this.layout, true);
29866     if (config.items) {
29867         var xitems = config.items;
29868         delete config.items;
29869         Roo.each(xitems, this.addxtype, this);
29870     }
29871     
29872     
29873 };
29874 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29875     /**
29876      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29877      * @deprecated
29878      */
29879     endUpdate : function(){
29880         this.layout.endUpdate();
29881     },
29882
29883     /**
29884      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29885      *  @deprecated
29886      */
29887     beginUpdate : function(){
29888         this.layout.beginUpdate();
29889     },
29890
29891     /**
29892      * Get the BorderLayout for this dialog
29893      * @return {Roo.BorderLayout}
29894      */
29895     getLayout : function(){
29896         return this.layout;
29897     },
29898
29899     showEl : function(){
29900         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29901         if(Roo.isIE7){
29902             this.layout.layout();
29903         }
29904     },
29905
29906     // private
29907     // Use the syncHeightBeforeShow config option to control this automatically
29908     syncBodyHeight : function(){
29909         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29910         if(this.layout){this.layout.layout();}
29911     },
29912     
29913       /**
29914      * Add an xtype element (actually adds to the layout.)
29915      * @return {Object} xdata xtype object data.
29916      */
29917     
29918     addxtype : function(c) {
29919         return this.layout.addxtype(c);
29920     }
29921 });/*
29922  * Based on:
29923  * Ext JS Library 1.1.1
29924  * Copyright(c) 2006-2007, Ext JS, LLC.
29925  *
29926  * Originally Released Under LGPL - original licence link has changed is not relivant.
29927  *
29928  * Fork - LGPL
29929  * <script type="text/javascript">
29930  */
29931  
29932 /**
29933  * @class Roo.MessageBox
29934  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29935  * Example usage:
29936  *<pre><code>
29937 // Basic alert:
29938 Roo.Msg.alert('Status', 'Changes saved successfully.');
29939
29940 // Prompt for user data:
29941 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29942     if (btn == 'ok'){
29943         // process text value...
29944     }
29945 });
29946
29947 // Show a dialog using config options:
29948 Roo.Msg.show({
29949    title:'Save Changes?',
29950    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29951    buttons: Roo.Msg.YESNOCANCEL,
29952    fn: processResult,
29953    animEl: 'elId'
29954 });
29955 </code></pre>
29956  * @singleton
29957  */
29958 Roo.MessageBox = function(){
29959     var dlg, opt, mask, waitTimer;
29960     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29961     var buttons, activeTextEl, bwidth;
29962
29963     // private
29964     var handleButton = function(button){
29965         dlg.hide();
29966         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29967     };
29968
29969     // private
29970     var handleHide = function(){
29971         if(opt && opt.cls){
29972             dlg.el.removeClass(opt.cls);
29973         }
29974         if(waitTimer){
29975             Roo.TaskMgr.stop(waitTimer);
29976             waitTimer = null;
29977         }
29978     };
29979
29980     // private
29981     var updateButtons = function(b){
29982         var width = 0;
29983         if(!b){
29984             buttons["ok"].hide();
29985             buttons["cancel"].hide();
29986             buttons["yes"].hide();
29987             buttons["no"].hide();
29988             dlg.footer.dom.style.display = 'none';
29989             return width;
29990         }
29991         dlg.footer.dom.style.display = '';
29992         for(var k in buttons){
29993             if(typeof buttons[k] != "function"){
29994                 if(b[k]){
29995                     buttons[k].show();
29996                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29997                     width += buttons[k].el.getWidth()+15;
29998                 }else{
29999                     buttons[k].hide();
30000                 }
30001             }
30002         }
30003         return width;
30004     };
30005
30006     // private
30007     var handleEsc = function(d, k, e){
30008         if(opt && opt.closable !== false){
30009             dlg.hide();
30010         }
30011         if(e){
30012             e.stopEvent();
30013         }
30014     };
30015
30016     return {
30017         /**
30018          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30019          * @return {Roo.BasicDialog} The BasicDialog element
30020          */
30021         getDialog : function(){
30022            if(!dlg){
30023                 dlg = new Roo.BasicDialog("x-msg-box", {
30024                     autoCreate : true,
30025                     shadow: true,
30026                     draggable: true,
30027                     resizable:false,
30028                     constraintoviewport:false,
30029                     fixedcenter:true,
30030                     collapsible : false,
30031                     shim:true,
30032                     modal: true,
30033                     width:400, height:100,
30034                     buttonAlign:"center",
30035                     closeClick : function(){
30036                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30037                             handleButton("no");
30038                         }else{
30039                             handleButton("cancel");
30040                         }
30041                     }
30042                 });
30043                 dlg.on("hide", handleHide);
30044                 mask = dlg.mask;
30045                 dlg.addKeyListener(27, handleEsc);
30046                 buttons = {};
30047                 var bt = this.buttonText;
30048                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30049                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30050                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30051                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30052                 bodyEl = dlg.body.createChild({
30053
30054                     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>'
30055                 });
30056                 msgEl = bodyEl.dom.firstChild;
30057                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30058                 textboxEl.enableDisplayMode();
30059                 textboxEl.addKeyListener([10,13], function(){
30060                     if(dlg.isVisible() && opt && opt.buttons){
30061                         if(opt.buttons.ok){
30062                             handleButton("ok");
30063                         }else if(opt.buttons.yes){
30064                             handleButton("yes");
30065                         }
30066                     }
30067                 });
30068                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30069                 textareaEl.enableDisplayMode();
30070                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30071                 progressEl.enableDisplayMode();
30072                 var pf = progressEl.dom.firstChild;
30073                 if (pf) {
30074                     pp = Roo.get(pf.firstChild);
30075                     pp.setHeight(pf.offsetHeight);
30076                 }
30077                 
30078             }
30079             return dlg;
30080         },
30081
30082         /**
30083          * Updates the message box body text
30084          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30085          * the XHTML-compliant non-breaking space character '&amp;#160;')
30086          * @return {Roo.MessageBox} This message box
30087          */
30088         updateText : function(text){
30089             if(!dlg.isVisible() && !opt.width){
30090                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30091             }
30092             msgEl.innerHTML = text || '&#160;';
30093             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30094                         Math.max(opt.minWidth || this.minWidth, bwidth));
30095             if(opt.prompt){
30096                 activeTextEl.setWidth(w);
30097             }
30098             if(dlg.isVisible()){
30099                 dlg.fixedcenter = false;
30100             }
30101             // to big, make it scoll.
30102             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30103                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30104                 bodyEl.dom.style.overflowY = 'auto';
30105             } else {
30106                 bodyEl.dom.style.height = '';
30107                 bodyEl.dom.style.overflowY = '';
30108             }
30109             
30110             dlg.setContentSize(w, bodyEl.getHeight());
30111             if(dlg.isVisible()){
30112                 dlg.fixedcenter = true;
30113             }
30114             return this;
30115         },
30116
30117         /**
30118          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30119          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30120          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30121          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30122          * @return {Roo.MessageBox} This message box
30123          */
30124         updateProgress : function(value, text){
30125             if(text){
30126                 this.updateText(text);
30127             }
30128             if (pp) { // weird bug on my firefox - for some reason this is not defined
30129                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30130             }
30131             return this;
30132         },        
30133
30134         /**
30135          * Returns true if the message box is currently displayed
30136          * @return {Boolean} True if the message box is visible, else false
30137          */
30138         isVisible : function(){
30139             return dlg && dlg.isVisible();  
30140         },
30141
30142         /**
30143          * Hides the message box if it is displayed
30144          */
30145         hide : function(){
30146             if(this.isVisible()){
30147                 dlg.hide();
30148             }  
30149         },
30150
30151         /**
30152          * Displays a new message box, or reinitializes an existing message box, based on the config options
30153          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30154          * The following config object properties are supported:
30155          * <pre>
30156 Property    Type             Description
30157 ----------  ---------------  ------------------------------------------------------------------------------------
30158 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30159                                    closes (defaults to undefined)
30160 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30161                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30162 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30163                                    progress and wait dialogs will ignore this property and always hide the
30164                                    close button as they can only be closed programmatically.
30165 cls               String           A custom CSS class to apply to the message box element
30166 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30167                                    displayed (defaults to 75)
30168 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30169                                    function will be btn (the name of the button that was clicked, if applicable,
30170                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30171                                    Progress and wait dialogs will ignore this option since they do not respond to
30172                                    user actions and can only be closed programmatically, so any required function
30173                                    should be called by the same code after it closes the dialog.
30174 icon              String           A CSS class that provides a background image to be used as an icon for
30175                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30176 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30177 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30178 modal             Boolean          False to allow user interaction with the page while the message box is
30179                                    displayed (defaults to true)
30180 msg               String           A string that will replace the existing message box body text (defaults
30181                                    to the XHTML-compliant non-breaking space character '&#160;')
30182 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30183 progress          Boolean          True to display a progress bar (defaults to false)
30184 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30185 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30186 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30187 title             String           The title text
30188 value             String           The string value to set into the active textbox element if displayed
30189 wait              Boolean          True to display a progress bar (defaults to false)
30190 width             Number           The width of the dialog in pixels
30191 </pre>
30192          *
30193          * Example usage:
30194          * <pre><code>
30195 Roo.Msg.show({
30196    title: 'Address',
30197    msg: 'Please enter your address:',
30198    width: 300,
30199    buttons: Roo.MessageBox.OKCANCEL,
30200    multiline: true,
30201    fn: saveAddress,
30202    animEl: 'addAddressBtn'
30203 });
30204 </code></pre>
30205          * @param {Object} config Configuration options
30206          * @return {Roo.MessageBox} This message box
30207          */
30208         show : function(options)
30209         {
30210             
30211             // this causes nightmares if you show one dialog after another
30212             // especially on callbacks..
30213              
30214             if(this.isVisible()){
30215                 
30216                 this.hide();
30217                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30218                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30219                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30220                 
30221             }
30222             var d = this.getDialog();
30223             opt = options;
30224             d.setTitle(opt.title || "&#160;");
30225             d.close.setDisplayed(opt.closable !== false);
30226             activeTextEl = textboxEl;
30227             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30228             if(opt.prompt){
30229                 if(opt.multiline){
30230                     textboxEl.hide();
30231                     textareaEl.show();
30232                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30233                         opt.multiline : this.defaultTextHeight);
30234                     activeTextEl = textareaEl;
30235                 }else{
30236                     textboxEl.show();
30237                     textareaEl.hide();
30238                 }
30239             }else{
30240                 textboxEl.hide();
30241                 textareaEl.hide();
30242             }
30243             progressEl.setDisplayed(opt.progress === true);
30244             this.updateProgress(0);
30245             activeTextEl.dom.value = opt.value || "";
30246             if(opt.prompt){
30247                 dlg.setDefaultButton(activeTextEl);
30248             }else{
30249                 var bs = opt.buttons;
30250                 var db = null;
30251                 if(bs && bs.ok){
30252                     db = buttons["ok"];
30253                 }else if(bs && bs.yes){
30254                     db = buttons["yes"];
30255                 }
30256                 dlg.setDefaultButton(db);
30257             }
30258             bwidth = updateButtons(opt.buttons);
30259             this.updateText(opt.msg);
30260             if(opt.cls){
30261                 d.el.addClass(opt.cls);
30262             }
30263             d.proxyDrag = opt.proxyDrag === true;
30264             d.modal = opt.modal !== false;
30265             d.mask = opt.modal !== false ? mask : false;
30266             if(!d.isVisible()){
30267                 // force it to the end of the z-index stack so it gets a cursor in FF
30268                 document.body.appendChild(dlg.el.dom);
30269                 d.animateTarget = null;
30270                 d.show(options.animEl);
30271             }
30272             return this;
30273         },
30274
30275         /**
30276          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30277          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30278          * and closing the message box when the process is complete.
30279          * @param {String} title The title bar text
30280          * @param {String} msg The message box body text
30281          * @return {Roo.MessageBox} This message box
30282          */
30283         progress : function(title, msg){
30284             this.show({
30285                 title : title,
30286                 msg : msg,
30287                 buttons: false,
30288                 progress:true,
30289                 closable:false,
30290                 minWidth: this.minProgressWidth,
30291                 modal : true
30292             });
30293             return this;
30294         },
30295
30296         /**
30297          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30298          * If a callback function is passed it will be called after the user clicks the button, and the
30299          * id of the button that was clicked will be passed as the only parameter to the callback
30300          * (could also be the top-right close button).
30301          * @param {String} title The title bar text
30302          * @param {String} msg The message box body text
30303          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30304          * @param {Object} scope (optional) The scope of the callback function
30305          * @return {Roo.MessageBox} This message box
30306          */
30307         alert : function(title, msg, fn, scope){
30308             this.show({
30309                 title : title,
30310                 msg : msg,
30311                 buttons: this.OK,
30312                 fn: fn,
30313                 scope : scope,
30314                 modal : true
30315             });
30316             return this;
30317         },
30318
30319         /**
30320          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30321          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30322          * You are responsible for closing the message box when the process is complete.
30323          * @param {String} msg The message box body text
30324          * @param {String} title (optional) The title bar text
30325          * @return {Roo.MessageBox} This message box
30326          */
30327         wait : function(msg, title){
30328             this.show({
30329                 title : title,
30330                 msg : msg,
30331                 buttons: false,
30332                 closable:false,
30333                 progress:true,
30334                 modal:true,
30335                 width:300,
30336                 wait:true
30337             });
30338             waitTimer = Roo.TaskMgr.start({
30339                 run: function(i){
30340                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30341                 },
30342                 interval: 1000
30343             });
30344             return this;
30345         },
30346
30347         /**
30348          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30349          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30350          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30351          * @param {String} title The title bar text
30352          * @param {String} msg The message box body text
30353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30354          * @param {Object} scope (optional) The scope of the callback function
30355          * @return {Roo.MessageBox} This message box
30356          */
30357         confirm : function(title, msg, fn, scope){
30358             this.show({
30359                 title : title,
30360                 msg : msg,
30361                 buttons: this.YESNO,
30362                 fn: fn,
30363                 scope : scope,
30364                 modal : true
30365             });
30366             return this;
30367         },
30368
30369         /**
30370          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30371          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30372          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30373          * (could also be the top-right close button) and the text that was entered will be passed as the two
30374          * parameters to the callback.
30375          * @param {String} title The title bar text
30376          * @param {String} msg The message box body text
30377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30378          * @param {Object} scope (optional) The scope of the callback function
30379          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30380          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30381          * @return {Roo.MessageBox} This message box
30382          */
30383         prompt : function(title, msg, fn, scope, multiline){
30384             this.show({
30385                 title : title,
30386                 msg : msg,
30387                 buttons: this.OKCANCEL,
30388                 fn: fn,
30389                 minWidth:250,
30390                 scope : scope,
30391                 prompt:true,
30392                 multiline: multiline,
30393                 modal : true
30394             });
30395             return this;
30396         },
30397
30398         /**
30399          * Button config that displays a single OK button
30400          * @type Object
30401          */
30402         OK : {ok:true},
30403         /**
30404          * Button config that displays Yes and No buttons
30405          * @type Object
30406          */
30407         YESNO : {yes:true, no:true},
30408         /**
30409          * Button config that displays OK and Cancel buttons
30410          * @type Object
30411          */
30412         OKCANCEL : {ok:true, cancel:true},
30413         /**
30414          * Button config that displays Yes, No and Cancel buttons
30415          * @type Object
30416          */
30417         YESNOCANCEL : {yes:true, no:true, cancel:true},
30418
30419         /**
30420          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30421          * @type Number
30422          */
30423         defaultTextHeight : 75,
30424         /**
30425          * The maximum width in pixels of the message box (defaults to 600)
30426          * @type Number
30427          */
30428         maxWidth : 600,
30429         /**
30430          * The minimum width in pixels of the message box (defaults to 100)
30431          * @type Number
30432          */
30433         minWidth : 100,
30434         /**
30435          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30436          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30437          * @type Number
30438          */
30439         minProgressWidth : 250,
30440         /**
30441          * An object containing the default button text strings that can be overriden for localized language support.
30442          * Supported properties are: ok, cancel, yes and no.
30443          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30444          * @type Object
30445          */
30446         buttonText : {
30447             ok : "OK",
30448             cancel : "Cancel",
30449             yes : "Yes",
30450             no : "No"
30451         }
30452     };
30453 }();
30454
30455 /**
30456  * Shorthand for {@link Roo.MessageBox}
30457  */
30458 Roo.Msg = Roo.MessageBox;/*
30459  * Based on:
30460  * Ext JS Library 1.1.1
30461  * Copyright(c) 2006-2007, Ext JS, LLC.
30462  *
30463  * Originally Released Under LGPL - original licence link has changed is not relivant.
30464  *
30465  * Fork - LGPL
30466  * <script type="text/javascript">
30467  */
30468 /**
30469  * @class Roo.QuickTips
30470  * Provides attractive and customizable tooltips for any element.
30471  * @singleton
30472  */
30473 Roo.QuickTips = function(){
30474     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30475     var ce, bd, xy, dd;
30476     var visible = false, disabled = true, inited = false;
30477     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30478     
30479     var onOver = function(e){
30480         if(disabled){
30481             return;
30482         }
30483         var t = e.getTarget();
30484         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30485             return;
30486         }
30487         if(ce && t == ce.el){
30488             clearTimeout(hideProc);
30489             return;
30490         }
30491         if(t && tagEls[t.id]){
30492             tagEls[t.id].el = t;
30493             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30494             return;
30495         }
30496         var ttp, et = Roo.fly(t);
30497         var ns = cfg.namespace;
30498         if(tm.interceptTitles && t.title){
30499             ttp = t.title;
30500             t.qtip = ttp;
30501             t.removeAttribute("title");
30502             e.preventDefault();
30503         }else{
30504             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30505         }
30506         if(ttp){
30507             showProc = show.defer(tm.showDelay, tm, [{
30508                 el: t, 
30509                 text: ttp, 
30510                 width: et.getAttributeNS(ns, cfg.width),
30511                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30512                 title: et.getAttributeNS(ns, cfg.title),
30513                     cls: et.getAttributeNS(ns, cfg.cls)
30514             }]);
30515         }
30516     };
30517     
30518     var onOut = function(e){
30519         clearTimeout(showProc);
30520         var t = e.getTarget();
30521         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30522             hideProc = setTimeout(hide, tm.hideDelay);
30523         }
30524     };
30525     
30526     var onMove = function(e){
30527         if(disabled){
30528             return;
30529         }
30530         xy = e.getXY();
30531         xy[1] += 18;
30532         if(tm.trackMouse && ce){
30533             el.setXY(xy);
30534         }
30535     };
30536     
30537     var onDown = function(e){
30538         clearTimeout(showProc);
30539         clearTimeout(hideProc);
30540         if(!e.within(el)){
30541             if(tm.hideOnClick){
30542                 hide();
30543                 tm.disable();
30544                 tm.enable.defer(100, tm);
30545             }
30546         }
30547     };
30548     
30549     var getPad = function(){
30550         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30551     };
30552
30553     var show = function(o){
30554         if(disabled){
30555             return;
30556         }
30557         clearTimeout(dismissProc);
30558         ce = o;
30559         if(removeCls){ // in case manually hidden
30560             el.removeClass(removeCls);
30561             removeCls = null;
30562         }
30563         if(ce.cls){
30564             el.addClass(ce.cls);
30565             removeCls = ce.cls;
30566         }
30567         if(ce.title){
30568             tipTitle.update(ce.title);
30569             tipTitle.show();
30570         }else{
30571             tipTitle.update('');
30572             tipTitle.hide();
30573         }
30574         el.dom.style.width  = tm.maxWidth+'px';
30575         //tipBody.dom.style.width = '';
30576         tipBodyText.update(o.text);
30577         var p = getPad(), w = ce.width;
30578         if(!w){
30579             var td = tipBodyText.dom;
30580             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30581             if(aw > tm.maxWidth){
30582                 w = tm.maxWidth;
30583             }else if(aw < tm.minWidth){
30584                 w = tm.minWidth;
30585             }else{
30586                 w = aw;
30587             }
30588         }
30589         //tipBody.setWidth(w);
30590         el.setWidth(parseInt(w, 10) + p);
30591         if(ce.autoHide === false){
30592             close.setDisplayed(true);
30593             if(dd){
30594                 dd.unlock();
30595             }
30596         }else{
30597             close.setDisplayed(false);
30598             if(dd){
30599                 dd.lock();
30600             }
30601         }
30602         if(xy){
30603             el.avoidY = xy[1]-18;
30604             el.setXY(xy);
30605         }
30606         if(tm.animate){
30607             el.setOpacity(.1);
30608             el.setStyle("visibility", "visible");
30609             el.fadeIn({callback: afterShow});
30610         }else{
30611             afterShow();
30612         }
30613     };
30614     
30615     var afterShow = function(){
30616         if(ce){
30617             el.show();
30618             esc.enable();
30619             if(tm.autoDismiss && ce.autoHide !== false){
30620                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30621             }
30622         }
30623     };
30624     
30625     var hide = function(noanim){
30626         clearTimeout(dismissProc);
30627         clearTimeout(hideProc);
30628         ce = null;
30629         if(el.isVisible()){
30630             esc.disable();
30631             if(noanim !== true && tm.animate){
30632                 el.fadeOut({callback: afterHide});
30633             }else{
30634                 afterHide();
30635             } 
30636         }
30637     };
30638     
30639     var afterHide = function(){
30640         el.hide();
30641         if(removeCls){
30642             el.removeClass(removeCls);
30643             removeCls = null;
30644         }
30645     };
30646     
30647     return {
30648         /**
30649         * @cfg {Number} minWidth
30650         * The minimum width of the quick tip (defaults to 40)
30651         */
30652        minWidth : 40,
30653         /**
30654         * @cfg {Number} maxWidth
30655         * The maximum width of the quick tip (defaults to 300)
30656         */
30657        maxWidth : 300,
30658         /**
30659         * @cfg {Boolean} interceptTitles
30660         * True to automatically use the element's DOM title value if available (defaults to false)
30661         */
30662        interceptTitles : false,
30663         /**
30664         * @cfg {Boolean} trackMouse
30665         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30666         */
30667        trackMouse : false,
30668         /**
30669         * @cfg {Boolean} hideOnClick
30670         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30671         */
30672        hideOnClick : true,
30673         /**
30674         * @cfg {Number} showDelay
30675         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30676         */
30677        showDelay : 500,
30678         /**
30679         * @cfg {Number} hideDelay
30680         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30681         */
30682        hideDelay : 200,
30683         /**
30684         * @cfg {Boolean} autoHide
30685         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30686         * Used in conjunction with hideDelay.
30687         */
30688        autoHide : true,
30689         /**
30690         * @cfg {Boolean}
30691         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30692         * (defaults to true).  Used in conjunction with autoDismissDelay.
30693         */
30694        autoDismiss : true,
30695         /**
30696         * @cfg {Number}
30697         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30698         */
30699        autoDismissDelay : 5000,
30700        /**
30701         * @cfg {Boolean} animate
30702         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30703         */
30704        animate : false,
30705
30706        /**
30707         * @cfg {String} title
30708         * Title text to display (defaults to '').  This can be any valid HTML markup.
30709         */
30710         title: '',
30711        /**
30712         * @cfg {String} text
30713         * Body text to display (defaults to '').  This can be any valid HTML markup.
30714         */
30715         text : '',
30716        /**
30717         * @cfg {String} cls
30718         * A CSS class to apply to the base quick tip element (defaults to '').
30719         */
30720         cls : '',
30721        /**
30722         * @cfg {Number} width
30723         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30724         * minWidth or maxWidth.
30725         */
30726         width : null,
30727
30728     /**
30729      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30730      * or display QuickTips in a page.
30731      */
30732        init : function(){
30733           tm = Roo.QuickTips;
30734           cfg = tm.tagConfig;
30735           if(!inited){
30736               if(!Roo.isReady){ // allow calling of init() before onReady
30737                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30738                   return;
30739               }
30740               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30741               el.fxDefaults = {stopFx: true};
30742               // maximum custom styling
30743               //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>');
30744               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>');              
30745               tipTitle = el.child('h3');
30746               tipTitle.enableDisplayMode("block");
30747               tipBody = el.child('div.x-tip-bd');
30748               tipBodyText = el.child('div.x-tip-bd-inner');
30749               //bdLeft = el.child('div.x-tip-bd-left');
30750               //bdRight = el.child('div.x-tip-bd-right');
30751               close = el.child('div.x-tip-close');
30752               close.enableDisplayMode("block");
30753               close.on("click", hide);
30754               var d = Roo.get(document);
30755               d.on("mousedown", onDown);
30756               d.on("mouseover", onOver);
30757               d.on("mouseout", onOut);
30758               d.on("mousemove", onMove);
30759               esc = d.addKeyListener(27, hide);
30760               esc.disable();
30761               if(Roo.dd.DD){
30762                   dd = el.initDD("default", null, {
30763                       onDrag : function(){
30764                           el.sync();  
30765                       }
30766                   });
30767                   dd.setHandleElId(tipTitle.id);
30768                   dd.lock();
30769               }
30770               inited = true;
30771           }
30772           this.enable(); 
30773        },
30774
30775     /**
30776      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30777      * are supported:
30778      * <pre>
30779 Property    Type                   Description
30780 ----------  ---------------------  ------------------------------------------------------------------------
30781 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30782      * </ul>
30783      * @param {Object} config The config object
30784      */
30785        register : function(config){
30786            var cs = config instanceof Array ? config : arguments;
30787            for(var i = 0, len = cs.length; i < len; i++) {
30788                var c = cs[i];
30789                var target = c.target;
30790                if(target){
30791                    if(target instanceof Array){
30792                        for(var j = 0, jlen = target.length; j < jlen; j++){
30793                            tagEls[target[j]] = c;
30794                        }
30795                    }else{
30796                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30797                    }
30798                }
30799            }
30800        },
30801
30802     /**
30803      * Removes this quick tip from its element and destroys it.
30804      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30805      */
30806        unregister : function(el){
30807            delete tagEls[Roo.id(el)];
30808        },
30809
30810     /**
30811      * Enable this quick tip.
30812      */
30813        enable : function(){
30814            if(inited && disabled){
30815                locks.pop();
30816                if(locks.length < 1){
30817                    disabled = false;
30818                }
30819            }
30820        },
30821
30822     /**
30823      * Disable this quick tip.
30824      */
30825        disable : function(){
30826           disabled = true;
30827           clearTimeout(showProc);
30828           clearTimeout(hideProc);
30829           clearTimeout(dismissProc);
30830           if(ce){
30831               hide(true);
30832           }
30833           locks.push(1);
30834        },
30835
30836     /**
30837      * Returns true if the quick tip is enabled, else false.
30838      */
30839        isEnabled : function(){
30840             return !disabled;
30841        },
30842
30843         // private
30844        tagConfig : {
30845            namespace : "ext",
30846            attribute : "qtip",
30847            width : "width",
30848            target : "target",
30849            title : "qtitle",
30850            hide : "hide",
30851            cls : "qclass"
30852        }
30853    };
30854 }();
30855
30856 // backwards compat
30857 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30858  * Based on:
30859  * Ext JS Library 1.1.1
30860  * Copyright(c) 2006-2007, Ext JS, LLC.
30861  *
30862  * Originally Released Under LGPL - original licence link has changed is not relivant.
30863  *
30864  * Fork - LGPL
30865  * <script type="text/javascript">
30866  */
30867  
30868
30869 /**
30870  * @class Roo.tree.TreePanel
30871  * @extends Roo.data.Tree
30872
30873  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30874  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30875  * @cfg {Boolean} enableDD true to enable drag and drop
30876  * @cfg {Boolean} enableDrag true to enable just drag
30877  * @cfg {Boolean} enableDrop true to enable just drop
30878  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30879  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30880  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30881  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30882  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30883  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30884  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30885  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30886  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30887  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30888  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30889  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30890  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30891  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30892  * @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>
30893  * @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>
30894  * 
30895  * @constructor
30896  * @param {String/HTMLElement/Element} el The container element
30897  * @param {Object} config
30898  */
30899 Roo.tree.TreePanel = function(el, config){
30900     var root = false;
30901     var loader = false;
30902     if (config.root) {
30903         root = config.root;
30904         delete config.root;
30905     }
30906     if (config.loader) {
30907         loader = config.loader;
30908         delete config.loader;
30909     }
30910     
30911     Roo.apply(this, config);
30912     Roo.tree.TreePanel.superclass.constructor.call(this);
30913     this.el = Roo.get(el);
30914     this.el.addClass('x-tree');
30915     //console.log(root);
30916     if (root) {
30917         this.setRootNode( Roo.factory(root, Roo.tree));
30918     }
30919     if (loader) {
30920         this.loader = Roo.factory(loader, Roo.tree);
30921     }
30922    /**
30923     * Read-only. The id of the container element becomes this TreePanel's id.
30924     */
30925     this.id = this.el.id;
30926     this.addEvents({
30927         /**
30928         * @event beforeload
30929         * Fires before a node is loaded, return false to cancel
30930         * @param {Node} node The node being loaded
30931         */
30932         "beforeload" : true,
30933         /**
30934         * @event load
30935         * Fires when a node is loaded
30936         * @param {Node} node The node that was loaded
30937         */
30938         "load" : true,
30939         /**
30940         * @event textchange
30941         * Fires when the text for a node is changed
30942         * @param {Node} node The node
30943         * @param {String} text The new text
30944         * @param {String} oldText The old text
30945         */
30946         "textchange" : true,
30947         /**
30948         * @event beforeexpand
30949         * Fires before a node is expanded, return false to cancel.
30950         * @param {Node} node The node
30951         * @param {Boolean} deep
30952         * @param {Boolean} anim
30953         */
30954         "beforeexpand" : true,
30955         /**
30956         * @event beforecollapse
30957         * Fires before a node is collapsed, return false to cancel.
30958         * @param {Node} node The node
30959         * @param {Boolean} deep
30960         * @param {Boolean} anim
30961         */
30962         "beforecollapse" : true,
30963         /**
30964         * @event expand
30965         * Fires when a node is expanded
30966         * @param {Node} node The node
30967         */
30968         "expand" : true,
30969         /**
30970         * @event disabledchange
30971         * Fires when the disabled status of a node changes
30972         * @param {Node} node The node
30973         * @param {Boolean} disabled
30974         */
30975         "disabledchange" : true,
30976         /**
30977         * @event collapse
30978         * Fires when a node is collapsed
30979         * @param {Node} node The node
30980         */
30981         "collapse" : true,
30982         /**
30983         * @event beforeclick
30984         * Fires before click processing on a node. Return false to cancel the default action.
30985         * @param {Node} node The node
30986         * @param {Roo.EventObject} e The event object
30987         */
30988         "beforeclick":true,
30989         /**
30990         * @event checkchange
30991         * Fires when a node with a checkbox's checked property changes
30992         * @param {Node} this This node
30993         * @param {Boolean} checked
30994         */
30995         "checkchange":true,
30996         /**
30997         * @event click
30998         * Fires when a node is clicked
30999         * @param {Node} node The node
31000         * @param {Roo.EventObject} e The event object
31001         */
31002         "click":true,
31003         /**
31004         * @event dblclick
31005         * Fires when a node is double clicked
31006         * @param {Node} node The node
31007         * @param {Roo.EventObject} e The event object
31008         */
31009         "dblclick":true,
31010         /**
31011         * @event contextmenu
31012         * Fires when a node is right clicked
31013         * @param {Node} node The node
31014         * @param {Roo.EventObject} e The event object
31015         */
31016         "contextmenu":true,
31017         /**
31018         * @event beforechildrenrendered
31019         * Fires right before the child nodes for a node are rendered
31020         * @param {Node} node The node
31021         */
31022         "beforechildrenrendered":true,
31023         /**
31024         * @event startdrag
31025         * Fires when a node starts being dragged
31026         * @param {Roo.tree.TreePanel} this
31027         * @param {Roo.tree.TreeNode} node
31028         * @param {event} e The raw browser event
31029         */ 
31030        "startdrag" : true,
31031        /**
31032         * @event enddrag
31033         * Fires when a drag operation is complete
31034         * @param {Roo.tree.TreePanel} this
31035         * @param {Roo.tree.TreeNode} node
31036         * @param {event} e The raw browser event
31037         */
31038        "enddrag" : true,
31039        /**
31040         * @event dragdrop
31041         * Fires when a dragged node is dropped on a valid DD target
31042         * @param {Roo.tree.TreePanel} this
31043         * @param {Roo.tree.TreeNode} node
31044         * @param {DD} dd The dd it was dropped on
31045         * @param {event} e The raw browser event
31046         */
31047        "dragdrop" : true,
31048        /**
31049         * @event beforenodedrop
31050         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31051         * passed to handlers has the following properties:<br />
31052         * <ul style="padding:5px;padding-left:16px;">
31053         * <li>tree - The TreePanel</li>
31054         * <li>target - The node being targeted for the drop</li>
31055         * <li>data - The drag data from the drag source</li>
31056         * <li>point - The point of the drop - append, above or below</li>
31057         * <li>source - The drag source</li>
31058         * <li>rawEvent - Raw mouse event</li>
31059         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31060         * to be inserted by setting them on this object.</li>
31061         * <li>cancel - Set this to true to cancel the drop.</li>
31062         * </ul>
31063         * @param {Object} dropEvent
31064         */
31065        "beforenodedrop" : true,
31066        /**
31067         * @event nodedrop
31068         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31069         * passed to handlers has the following properties:<br />
31070         * <ul style="padding:5px;padding-left:16px;">
31071         * <li>tree - The TreePanel</li>
31072         * <li>target - The node being targeted for the drop</li>
31073         * <li>data - The drag data from the drag source</li>
31074         * <li>point - The point of the drop - append, above or below</li>
31075         * <li>source - The drag source</li>
31076         * <li>rawEvent - Raw mouse event</li>
31077         * <li>dropNode - Dropped node(s).</li>
31078         * </ul>
31079         * @param {Object} dropEvent
31080         */
31081        "nodedrop" : true,
31082         /**
31083         * @event nodedragover
31084         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31085         * passed to handlers has the following properties:<br />
31086         * <ul style="padding:5px;padding-left:16px;">
31087         * <li>tree - The TreePanel</li>
31088         * <li>target - The node being targeted for the drop</li>
31089         * <li>data - The drag data from the drag source</li>
31090         * <li>point - The point of the drop - append, above or below</li>
31091         * <li>source - The drag source</li>
31092         * <li>rawEvent - Raw mouse event</li>
31093         * <li>dropNode - Drop node(s) provided by the source.</li>
31094         * <li>cancel - Set this to true to signal drop not allowed.</li>
31095         * </ul>
31096         * @param {Object} dragOverEvent
31097         */
31098        "nodedragover" : true
31099         
31100     });
31101     if(this.singleExpand){
31102        this.on("beforeexpand", this.restrictExpand, this);
31103     }
31104     if (this.editor) {
31105         this.editor.tree = this;
31106         this.editor = Roo.factory(this.editor, Roo.tree);
31107     }
31108     
31109     if (this.selModel) {
31110         this.selModel = Roo.factory(this.selModel, Roo.tree);
31111     }
31112    
31113 };
31114 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31115     rootVisible : true,
31116     animate: Roo.enableFx,
31117     lines : true,
31118     enableDD : false,
31119     hlDrop : Roo.enableFx,
31120   
31121     renderer: false,
31122     
31123     rendererTip: false,
31124     // private
31125     restrictExpand : function(node){
31126         var p = node.parentNode;
31127         if(p){
31128             if(p.expandedChild && p.expandedChild.parentNode == p){
31129                 p.expandedChild.collapse();
31130             }
31131             p.expandedChild = node;
31132         }
31133     },
31134
31135     // private override
31136     setRootNode : function(node){
31137         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31138         if(!this.rootVisible){
31139             node.ui = new Roo.tree.RootTreeNodeUI(node);
31140         }
31141         return node;
31142     },
31143
31144     /**
31145      * Returns the container element for this TreePanel
31146      */
31147     getEl : function(){
31148         return this.el;
31149     },
31150
31151     /**
31152      * Returns the default TreeLoader for this TreePanel
31153      */
31154     getLoader : function(){
31155         return this.loader;
31156     },
31157
31158     /**
31159      * Expand all nodes
31160      */
31161     expandAll : function(){
31162         this.root.expand(true);
31163     },
31164
31165     /**
31166      * Collapse all nodes
31167      */
31168     collapseAll : function(){
31169         this.root.collapse(true);
31170     },
31171
31172     /**
31173      * Returns the selection model used by this TreePanel
31174      */
31175     getSelectionModel : function(){
31176         if(!this.selModel){
31177             this.selModel = new Roo.tree.DefaultSelectionModel();
31178         }
31179         return this.selModel;
31180     },
31181
31182     /**
31183      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31184      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31185      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31186      * @return {Array}
31187      */
31188     getChecked : function(a, startNode){
31189         startNode = startNode || this.root;
31190         var r = [];
31191         var f = function(){
31192             if(this.attributes.checked){
31193                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31194             }
31195         }
31196         startNode.cascade(f);
31197         return r;
31198     },
31199
31200     /**
31201      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31202      * @param {String} path
31203      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31204      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31205      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31206      */
31207     expandPath : function(path, attr, callback){
31208         attr = attr || "id";
31209         var keys = path.split(this.pathSeparator);
31210         var curNode = this.root;
31211         if(curNode.attributes[attr] != keys[1]){ // invalid root
31212             if(callback){
31213                 callback(false, null);
31214             }
31215             return;
31216         }
31217         var index = 1;
31218         var f = function(){
31219             if(++index == keys.length){
31220                 if(callback){
31221                     callback(true, curNode);
31222                 }
31223                 return;
31224             }
31225             var c = curNode.findChild(attr, keys[index]);
31226             if(!c){
31227                 if(callback){
31228                     callback(false, curNode);
31229                 }
31230                 return;
31231             }
31232             curNode = c;
31233             c.expand(false, false, f);
31234         };
31235         curNode.expand(false, false, f);
31236     },
31237
31238     /**
31239      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31240      * @param {String} path
31241      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31242      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31243      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31244      */
31245     selectPath : function(path, attr, callback){
31246         attr = attr || "id";
31247         var keys = path.split(this.pathSeparator);
31248         var v = keys.pop();
31249         if(keys.length > 0){
31250             var f = function(success, node){
31251                 if(success && node){
31252                     var n = node.findChild(attr, v);
31253                     if(n){
31254                         n.select();
31255                         if(callback){
31256                             callback(true, n);
31257                         }
31258                     }else if(callback){
31259                         callback(false, n);
31260                     }
31261                 }else{
31262                     if(callback){
31263                         callback(false, n);
31264                     }
31265                 }
31266             };
31267             this.expandPath(keys.join(this.pathSeparator), attr, f);
31268         }else{
31269             this.root.select();
31270             if(callback){
31271                 callback(true, this.root);
31272             }
31273         }
31274     },
31275
31276     getTreeEl : function(){
31277         return this.el;
31278     },
31279
31280     /**
31281      * Trigger rendering of this TreePanel
31282      */
31283     render : function(){
31284         if (this.innerCt) {
31285             return this; // stop it rendering more than once!!
31286         }
31287         
31288         this.innerCt = this.el.createChild({tag:"ul",
31289                cls:"x-tree-root-ct " +
31290                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31291
31292         if(this.containerScroll){
31293             Roo.dd.ScrollManager.register(this.el);
31294         }
31295         if((this.enableDD || this.enableDrop) && !this.dropZone){
31296            /**
31297             * The dropZone used by this tree if drop is enabled
31298             * @type Roo.tree.TreeDropZone
31299             */
31300              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31301                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31302            });
31303         }
31304         if((this.enableDD || this.enableDrag) && !this.dragZone){
31305            /**
31306             * The dragZone used by this tree if drag is enabled
31307             * @type Roo.tree.TreeDragZone
31308             */
31309             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31310                ddGroup: this.ddGroup || "TreeDD",
31311                scroll: this.ddScroll
31312            });
31313         }
31314         this.getSelectionModel().init(this);
31315         if (!this.root) {
31316             console.log("ROOT not set in tree");
31317             return;
31318         }
31319         this.root.render();
31320         if(!this.rootVisible){
31321             this.root.renderChildren();
31322         }
31323         return this;
31324     }
31325 });/*
31326  * Based on:
31327  * Ext JS Library 1.1.1
31328  * Copyright(c) 2006-2007, Ext JS, LLC.
31329  *
31330  * Originally Released Under LGPL - original licence link has changed is not relivant.
31331  *
31332  * Fork - LGPL
31333  * <script type="text/javascript">
31334  */
31335  
31336
31337 /**
31338  * @class Roo.tree.DefaultSelectionModel
31339  * @extends Roo.util.Observable
31340  * The default single selection for a TreePanel.
31341  * @param {Object} cfg Configuration
31342  */
31343 Roo.tree.DefaultSelectionModel = function(cfg){
31344    this.selNode = null;
31345    
31346    
31347    
31348    this.addEvents({
31349        /**
31350         * @event selectionchange
31351         * Fires when the selected node changes
31352         * @param {DefaultSelectionModel} this
31353         * @param {TreeNode} node the new selection
31354         */
31355        "selectionchange" : true,
31356
31357        /**
31358         * @event beforeselect
31359         * Fires before the selected node changes, return false to cancel the change
31360         * @param {DefaultSelectionModel} this
31361         * @param {TreeNode} node the new selection
31362         * @param {TreeNode} node the old selection
31363         */
31364        "beforeselect" : true
31365    });
31366    
31367     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31368 };
31369
31370 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31371     init : function(tree){
31372         this.tree = tree;
31373         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31374         tree.on("click", this.onNodeClick, this);
31375     },
31376     
31377     onNodeClick : function(node, e){
31378         if (e.ctrlKey && this.selNode == node)  {
31379             this.unselect(node);
31380             return;
31381         }
31382         this.select(node);
31383     },
31384     
31385     /**
31386      * Select a node.
31387      * @param {TreeNode} node The node to select
31388      * @return {TreeNode} The selected node
31389      */
31390     select : function(node){
31391         var last = this.selNode;
31392         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31393             if(last){
31394                 last.ui.onSelectedChange(false);
31395             }
31396             this.selNode = node;
31397             node.ui.onSelectedChange(true);
31398             this.fireEvent("selectionchange", this, node, last);
31399         }
31400         return node;
31401     },
31402     
31403     /**
31404      * Deselect a node.
31405      * @param {TreeNode} node The node to unselect
31406      */
31407     unselect : function(node){
31408         if(this.selNode == node){
31409             this.clearSelections();
31410         }    
31411     },
31412     
31413     /**
31414      * Clear all selections
31415      */
31416     clearSelections : function(){
31417         var n = this.selNode;
31418         if(n){
31419             n.ui.onSelectedChange(false);
31420             this.selNode = null;
31421             this.fireEvent("selectionchange", this, null);
31422         }
31423         return n;
31424     },
31425     
31426     /**
31427      * Get the selected node
31428      * @return {TreeNode} The selected node
31429      */
31430     getSelectedNode : function(){
31431         return this.selNode;    
31432     },
31433     
31434     /**
31435      * Returns true if the node is selected
31436      * @param {TreeNode} node The node to check
31437      * @return {Boolean}
31438      */
31439     isSelected : function(node){
31440         return this.selNode == node;  
31441     },
31442
31443     /**
31444      * Selects the node above the selected node in the tree, intelligently walking the nodes
31445      * @return TreeNode The new selection
31446      */
31447     selectPrevious : function(){
31448         var s = this.selNode || this.lastSelNode;
31449         if(!s){
31450             return null;
31451         }
31452         var ps = s.previousSibling;
31453         if(ps){
31454             if(!ps.isExpanded() || ps.childNodes.length < 1){
31455                 return this.select(ps);
31456             } else{
31457                 var lc = ps.lastChild;
31458                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31459                     lc = lc.lastChild;
31460                 }
31461                 return this.select(lc);
31462             }
31463         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31464             return this.select(s.parentNode);
31465         }
31466         return null;
31467     },
31468
31469     /**
31470      * Selects the node above the selected node in the tree, intelligently walking the nodes
31471      * @return TreeNode The new selection
31472      */
31473     selectNext : function(){
31474         var s = this.selNode || this.lastSelNode;
31475         if(!s){
31476             return null;
31477         }
31478         if(s.firstChild && s.isExpanded()){
31479              return this.select(s.firstChild);
31480          }else if(s.nextSibling){
31481              return this.select(s.nextSibling);
31482          }else if(s.parentNode){
31483             var newS = null;
31484             s.parentNode.bubble(function(){
31485                 if(this.nextSibling){
31486                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31487                     return false;
31488                 }
31489             });
31490             return newS;
31491          }
31492         return null;
31493     },
31494
31495     onKeyDown : function(e){
31496         var s = this.selNode || this.lastSelNode;
31497         // undesirable, but required
31498         var sm = this;
31499         if(!s){
31500             return;
31501         }
31502         var k = e.getKey();
31503         switch(k){
31504              case e.DOWN:
31505                  e.stopEvent();
31506                  this.selectNext();
31507              break;
31508              case e.UP:
31509                  e.stopEvent();
31510                  this.selectPrevious();
31511              break;
31512              case e.RIGHT:
31513                  e.preventDefault();
31514                  if(s.hasChildNodes()){
31515                      if(!s.isExpanded()){
31516                          s.expand();
31517                      }else if(s.firstChild){
31518                          this.select(s.firstChild, e);
31519                      }
31520                  }
31521              break;
31522              case e.LEFT:
31523                  e.preventDefault();
31524                  if(s.hasChildNodes() && s.isExpanded()){
31525                      s.collapse();
31526                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31527                      this.select(s.parentNode, e);
31528                  }
31529              break;
31530         };
31531     }
31532 });
31533
31534 /**
31535  * @class Roo.tree.MultiSelectionModel
31536  * @extends Roo.util.Observable
31537  * Multi selection for a TreePanel.
31538  * @param {Object} cfg Configuration
31539  */
31540 Roo.tree.MultiSelectionModel = function(){
31541    this.selNodes = [];
31542    this.selMap = {};
31543    this.addEvents({
31544        /**
31545         * @event selectionchange
31546         * Fires when the selected nodes change
31547         * @param {MultiSelectionModel} this
31548         * @param {Array} nodes Array of the selected nodes
31549         */
31550        "selectionchange" : true
31551    });
31552    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31553    
31554 };
31555
31556 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31557     init : function(tree){
31558         this.tree = tree;
31559         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31560         tree.on("click", this.onNodeClick, this);
31561     },
31562     
31563     onNodeClick : function(node, e){
31564         this.select(node, e, e.ctrlKey);
31565     },
31566     
31567     /**
31568      * Select a node.
31569      * @param {TreeNode} node The node to select
31570      * @param {EventObject} e (optional) An event associated with the selection
31571      * @param {Boolean} keepExisting True to retain existing selections
31572      * @return {TreeNode} The selected node
31573      */
31574     select : function(node, e, keepExisting){
31575         if(keepExisting !== true){
31576             this.clearSelections(true);
31577         }
31578         if(this.isSelected(node)){
31579             this.lastSelNode = node;
31580             return node;
31581         }
31582         this.selNodes.push(node);
31583         this.selMap[node.id] = node;
31584         this.lastSelNode = node;
31585         node.ui.onSelectedChange(true);
31586         this.fireEvent("selectionchange", this, this.selNodes);
31587         return node;
31588     },
31589     
31590     /**
31591      * Deselect a node.
31592      * @param {TreeNode} node The node to unselect
31593      */
31594     unselect : function(node){
31595         if(this.selMap[node.id]){
31596             node.ui.onSelectedChange(false);
31597             var sn = this.selNodes;
31598             var index = -1;
31599             if(sn.indexOf){
31600                 index = sn.indexOf(node);
31601             }else{
31602                 for(var i = 0, len = sn.length; i < len; i++){
31603                     if(sn[i] == node){
31604                         index = i;
31605                         break;
31606                     }
31607                 }
31608             }
31609             if(index != -1){
31610                 this.selNodes.splice(index, 1);
31611             }
31612             delete this.selMap[node.id];
31613             this.fireEvent("selectionchange", this, this.selNodes);
31614         }
31615     },
31616     
31617     /**
31618      * Clear all selections
31619      */
31620     clearSelections : function(suppressEvent){
31621         var sn = this.selNodes;
31622         if(sn.length > 0){
31623             for(var i = 0, len = sn.length; i < len; i++){
31624                 sn[i].ui.onSelectedChange(false);
31625             }
31626             this.selNodes = [];
31627             this.selMap = {};
31628             if(suppressEvent !== true){
31629                 this.fireEvent("selectionchange", this, this.selNodes);
31630             }
31631         }
31632     },
31633     
31634     /**
31635      * Returns true if the node is selected
31636      * @param {TreeNode} node The node to check
31637      * @return {Boolean}
31638      */
31639     isSelected : function(node){
31640         return this.selMap[node.id] ? true : false;  
31641     },
31642     
31643     /**
31644      * Returns an array of the selected nodes
31645      * @return {Array}
31646      */
31647     getSelectedNodes : function(){
31648         return this.selNodes;    
31649     },
31650
31651     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31652
31653     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31654
31655     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31656 });/*
31657  * Based on:
31658  * Ext JS Library 1.1.1
31659  * Copyright(c) 2006-2007, Ext JS, LLC.
31660  *
31661  * Originally Released Under LGPL - original licence link has changed is not relivant.
31662  *
31663  * Fork - LGPL
31664  * <script type="text/javascript">
31665  */
31666  
31667 /**
31668  * @class Roo.tree.TreeNode
31669  * @extends Roo.data.Node
31670  * @cfg {String} text The text for this node
31671  * @cfg {Boolean} expanded true to start the node expanded
31672  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31673  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31674  * @cfg {Boolean} disabled true to start the node disabled
31675  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31676  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31677  * @cfg {String} cls A css class to be added to the node
31678  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31679  * @cfg {String} href URL of the link used for the node (defaults to #)
31680  * @cfg {String} hrefTarget target frame for the link
31681  * @cfg {String} qtip An Ext QuickTip for the node
31682  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31683  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31684  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31685  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31686  * (defaults to undefined with no checkbox rendered)
31687  * @constructor
31688  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31689  */
31690 Roo.tree.TreeNode = function(attributes){
31691     attributes = attributes || {};
31692     if(typeof attributes == "string"){
31693         attributes = {text: attributes};
31694     }
31695     this.childrenRendered = false;
31696     this.rendered = false;
31697     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31698     this.expanded = attributes.expanded === true;
31699     this.isTarget = attributes.isTarget !== false;
31700     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31701     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31702
31703     /**
31704      * Read-only. The text for this node. To change it use setText().
31705      * @type String
31706      */
31707     this.text = attributes.text;
31708     /**
31709      * True if this node is disabled.
31710      * @type Boolean
31711      */
31712     this.disabled = attributes.disabled === true;
31713
31714     this.addEvents({
31715         /**
31716         * @event textchange
31717         * Fires when the text for this node is changed
31718         * @param {Node} this This node
31719         * @param {String} text The new text
31720         * @param {String} oldText The old text
31721         */
31722         "textchange" : true,
31723         /**
31724         * @event beforeexpand
31725         * Fires before this node is expanded, return false to cancel.
31726         * @param {Node} this This node
31727         * @param {Boolean} deep
31728         * @param {Boolean} anim
31729         */
31730         "beforeexpand" : true,
31731         /**
31732         * @event beforecollapse
31733         * Fires before this node is collapsed, return false to cancel.
31734         * @param {Node} this This node
31735         * @param {Boolean} deep
31736         * @param {Boolean} anim
31737         */
31738         "beforecollapse" : true,
31739         /**
31740         * @event expand
31741         * Fires when this node is expanded
31742         * @param {Node} this This node
31743         */
31744         "expand" : true,
31745         /**
31746         * @event disabledchange
31747         * Fires when the disabled status of this node changes
31748         * @param {Node} this This node
31749         * @param {Boolean} disabled
31750         */
31751         "disabledchange" : true,
31752         /**
31753         * @event collapse
31754         * Fires when this node is collapsed
31755         * @param {Node} this This node
31756         */
31757         "collapse" : true,
31758         /**
31759         * @event beforeclick
31760         * Fires before click processing. Return false to cancel the default action.
31761         * @param {Node} this This node
31762         * @param {Roo.EventObject} e The event object
31763         */
31764         "beforeclick":true,
31765         /**
31766         * @event checkchange
31767         * Fires when a node with a checkbox's checked property changes
31768         * @param {Node} this This node
31769         * @param {Boolean} checked
31770         */
31771         "checkchange":true,
31772         /**
31773         * @event click
31774         * Fires when this node is clicked
31775         * @param {Node} this This node
31776         * @param {Roo.EventObject} e The event object
31777         */
31778         "click":true,
31779         /**
31780         * @event dblclick
31781         * Fires when this node is double clicked
31782         * @param {Node} this This node
31783         * @param {Roo.EventObject} e The event object
31784         */
31785         "dblclick":true,
31786         /**
31787         * @event contextmenu
31788         * Fires when this node is right clicked
31789         * @param {Node} this This node
31790         * @param {Roo.EventObject} e The event object
31791         */
31792         "contextmenu":true,
31793         /**
31794         * @event beforechildrenrendered
31795         * Fires right before the child nodes for this node are rendered
31796         * @param {Node} this This node
31797         */
31798         "beforechildrenrendered":true
31799     });
31800
31801     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31802
31803     /**
31804      * Read-only. The UI for this node
31805      * @type TreeNodeUI
31806      */
31807     this.ui = new uiClass(this);
31808 };
31809 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31810     preventHScroll: true,
31811     /**
31812      * Returns true if this node is expanded
31813      * @return {Boolean}
31814      */
31815     isExpanded : function(){
31816         return this.expanded;
31817     },
31818
31819     /**
31820      * Returns the UI object for this node
31821      * @return {TreeNodeUI}
31822      */
31823     getUI : function(){
31824         return this.ui;
31825     },
31826
31827     // private override
31828     setFirstChild : function(node){
31829         var of = this.firstChild;
31830         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31831         if(this.childrenRendered && of && node != of){
31832             of.renderIndent(true, true);
31833         }
31834         if(this.rendered){
31835             this.renderIndent(true, true);
31836         }
31837     },
31838
31839     // private override
31840     setLastChild : function(node){
31841         var ol = this.lastChild;
31842         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31843         if(this.childrenRendered && ol && node != ol){
31844             ol.renderIndent(true, true);
31845         }
31846         if(this.rendered){
31847             this.renderIndent(true, true);
31848         }
31849     },
31850
31851     // these methods are overridden to provide lazy rendering support
31852     // private override
31853     appendChild : function(){
31854         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31855         if(node && this.childrenRendered){
31856             node.render();
31857         }
31858         this.ui.updateExpandIcon();
31859         return node;
31860     },
31861
31862     // private override
31863     removeChild : function(node){
31864         this.ownerTree.getSelectionModel().unselect(node);
31865         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31866         // if it's been rendered remove dom node
31867         if(this.childrenRendered){
31868             node.ui.remove();
31869         }
31870         if(this.childNodes.length < 1){
31871             this.collapse(false, false);
31872         }else{
31873             this.ui.updateExpandIcon();
31874         }
31875         if(!this.firstChild) {
31876             this.childrenRendered = false;
31877         }
31878         return node;
31879     },
31880
31881     // private override
31882     insertBefore : function(node, refNode){
31883         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31884         if(newNode && refNode && this.childrenRendered){
31885             node.render();
31886         }
31887         this.ui.updateExpandIcon();
31888         return newNode;
31889     },
31890
31891     /**
31892      * Sets the text for this node
31893      * @param {String} text
31894      */
31895     setText : function(text){
31896         var oldText = this.text;
31897         this.text = text;
31898         this.attributes.text = text;
31899         if(this.rendered){ // event without subscribing
31900             this.ui.onTextChange(this, text, oldText);
31901         }
31902         this.fireEvent("textchange", this, text, oldText);
31903     },
31904
31905     /**
31906      * Triggers selection of this node
31907      */
31908     select : function(){
31909         this.getOwnerTree().getSelectionModel().select(this);
31910     },
31911
31912     /**
31913      * Triggers deselection of this node
31914      */
31915     unselect : function(){
31916         this.getOwnerTree().getSelectionModel().unselect(this);
31917     },
31918
31919     /**
31920      * Returns true if this node is selected
31921      * @return {Boolean}
31922      */
31923     isSelected : function(){
31924         return this.getOwnerTree().getSelectionModel().isSelected(this);
31925     },
31926
31927     /**
31928      * Expand this node.
31929      * @param {Boolean} deep (optional) True to expand all children as well
31930      * @param {Boolean} anim (optional) false to cancel the default animation
31931      * @param {Function} callback (optional) A callback to be called when
31932      * expanding this node completes (does not wait for deep expand to complete).
31933      * Called with 1 parameter, this node.
31934      */
31935     expand : function(deep, anim, callback){
31936         if(!this.expanded){
31937             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31938                 return;
31939             }
31940             if(!this.childrenRendered){
31941                 this.renderChildren();
31942             }
31943             this.expanded = true;
31944             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31945                 this.ui.animExpand(function(){
31946                     this.fireEvent("expand", this);
31947                     if(typeof callback == "function"){
31948                         callback(this);
31949                     }
31950                     if(deep === true){
31951                         this.expandChildNodes(true);
31952                     }
31953                 }.createDelegate(this));
31954                 return;
31955             }else{
31956                 this.ui.expand();
31957                 this.fireEvent("expand", this);
31958                 if(typeof callback == "function"){
31959                     callback(this);
31960                 }
31961             }
31962         }else{
31963            if(typeof callback == "function"){
31964                callback(this);
31965            }
31966         }
31967         if(deep === true){
31968             this.expandChildNodes(true);
31969         }
31970     },
31971
31972     isHiddenRoot : function(){
31973         return this.isRoot && !this.getOwnerTree().rootVisible;
31974     },
31975
31976     /**
31977      * Collapse this node.
31978      * @param {Boolean} deep (optional) True to collapse all children as well
31979      * @param {Boolean} anim (optional) false to cancel the default animation
31980      */
31981     collapse : function(deep, anim){
31982         if(this.expanded && !this.isHiddenRoot()){
31983             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31984                 return;
31985             }
31986             this.expanded = false;
31987             if((this.getOwnerTree().animate && anim !== false) || anim){
31988                 this.ui.animCollapse(function(){
31989                     this.fireEvent("collapse", this);
31990                     if(deep === true){
31991                         this.collapseChildNodes(true);
31992                     }
31993                 }.createDelegate(this));
31994                 return;
31995             }else{
31996                 this.ui.collapse();
31997                 this.fireEvent("collapse", this);
31998             }
31999         }
32000         if(deep === true){
32001             var cs = this.childNodes;
32002             for(var i = 0, len = cs.length; i < len; i++) {
32003                 cs[i].collapse(true, false);
32004             }
32005         }
32006     },
32007
32008     // private
32009     delayedExpand : function(delay){
32010         if(!this.expandProcId){
32011             this.expandProcId = this.expand.defer(delay, this);
32012         }
32013     },
32014
32015     // private
32016     cancelExpand : function(){
32017         if(this.expandProcId){
32018             clearTimeout(this.expandProcId);
32019         }
32020         this.expandProcId = false;
32021     },
32022
32023     /**
32024      * Toggles expanded/collapsed state of the node
32025      */
32026     toggle : function(){
32027         if(this.expanded){
32028             this.collapse();
32029         }else{
32030             this.expand();
32031         }
32032     },
32033
32034     /**
32035      * Ensures all parent nodes are expanded
32036      */
32037     ensureVisible : function(callback){
32038         var tree = this.getOwnerTree();
32039         tree.expandPath(this.parentNode.getPath(), false, function(){
32040             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32041             Roo.callback(callback);
32042         }.createDelegate(this));
32043     },
32044
32045     /**
32046      * Expand all child nodes
32047      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32048      */
32049     expandChildNodes : function(deep){
32050         var cs = this.childNodes;
32051         for(var i = 0, len = cs.length; i < len; i++) {
32052                 cs[i].expand(deep);
32053         }
32054     },
32055
32056     /**
32057      * Collapse all child nodes
32058      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32059      */
32060     collapseChildNodes : function(deep){
32061         var cs = this.childNodes;
32062         for(var i = 0, len = cs.length; i < len; i++) {
32063                 cs[i].collapse(deep);
32064         }
32065     },
32066
32067     /**
32068      * Disables this node
32069      */
32070     disable : function(){
32071         this.disabled = true;
32072         this.unselect();
32073         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32074             this.ui.onDisableChange(this, true);
32075         }
32076         this.fireEvent("disabledchange", this, true);
32077     },
32078
32079     /**
32080      * Enables this node
32081      */
32082     enable : function(){
32083         this.disabled = false;
32084         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32085             this.ui.onDisableChange(this, false);
32086         }
32087         this.fireEvent("disabledchange", this, false);
32088     },
32089
32090     // private
32091     renderChildren : function(suppressEvent){
32092         if(suppressEvent !== false){
32093             this.fireEvent("beforechildrenrendered", this);
32094         }
32095         var cs = this.childNodes;
32096         for(var i = 0, len = cs.length; i < len; i++){
32097             cs[i].render(true);
32098         }
32099         this.childrenRendered = true;
32100     },
32101
32102     // private
32103     sort : function(fn, scope){
32104         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32105         if(this.childrenRendered){
32106             var cs = this.childNodes;
32107             for(var i = 0, len = cs.length; i < len; i++){
32108                 cs[i].render(true);
32109             }
32110         }
32111     },
32112
32113     // private
32114     render : function(bulkRender){
32115         this.ui.render(bulkRender);
32116         if(!this.rendered){
32117             this.rendered = true;
32118             if(this.expanded){
32119                 this.expanded = false;
32120                 this.expand(false, false);
32121             }
32122         }
32123     },
32124
32125     // private
32126     renderIndent : function(deep, refresh){
32127         if(refresh){
32128             this.ui.childIndent = null;
32129         }
32130         this.ui.renderIndent();
32131         if(deep === true && this.childrenRendered){
32132             var cs = this.childNodes;
32133             for(var i = 0, len = cs.length; i < len; i++){
32134                 cs[i].renderIndent(true, refresh);
32135             }
32136         }
32137     }
32138 });/*
32139  * Based on:
32140  * Ext JS Library 1.1.1
32141  * Copyright(c) 2006-2007, Ext JS, LLC.
32142  *
32143  * Originally Released Under LGPL - original licence link has changed is not relivant.
32144  *
32145  * Fork - LGPL
32146  * <script type="text/javascript">
32147  */
32148  
32149 /**
32150  * @class Roo.tree.AsyncTreeNode
32151  * @extends Roo.tree.TreeNode
32152  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32153  * @constructor
32154  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32155  */
32156  Roo.tree.AsyncTreeNode = function(config){
32157     this.loaded = false;
32158     this.loading = false;
32159     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32160     /**
32161     * @event beforeload
32162     * Fires before this node is loaded, return false to cancel
32163     * @param {Node} this This node
32164     */
32165     this.addEvents({'beforeload':true, 'load': true});
32166     /**
32167     * @event load
32168     * Fires when this node is loaded
32169     * @param {Node} this This node
32170     */
32171     /**
32172      * The loader used by this node (defaults to using the tree's defined loader)
32173      * @type TreeLoader
32174      * @property loader
32175      */
32176 };
32177 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32178     expand : function(deep, anim, callback){
32179         if(this.loading){ // if an async load is already running, waiting til it's done
32180             var timer;
32181             var f = function(){
32182                 if(!this.loading){ // done loading
32183                     clearInterval(timer);
32184                     this.expand(deep, anim, callback);
32185                 }
32186             }.createDelegate(this);
32187             timer = setInterval(f, 200);
32188             return;
32189         }
32190         if(!this.loaded){
32191             if(this.fireEvent("beforeload", this) === false){
32192                 return;
32193             }
32194             this.loading = true;
32195             this.ui.beforeLoad(this);
32196             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32197             if(loader){
32198                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32199                 return;
32200             }
32201         }
32202         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32203     },
32204     
32205     /**
32206      * Returns true if this node is currently loading
32207      * @return {Boolean}
32208      */
32209     isLoading : function(){
32210         return this.loading;  
32211     },
32212     
32213     loadComplete : function(deep, anim, callback){
32214         this.loading = false;
32215         this.loaded = true;
32216         this.ui.afterLoad(this);
32217         this.fireEvent("load", this);
32218         this.expand(deep, anim, callback);
32219     },
32220     
32221     /**
32222      * Returns true if this node has been loaded
32223      * @return {Boolean}
32224      */
32225     isLoaded : function(){
32226         return this.loaded;
32227     },
32228     
32229     hasChildNodes : function(){
32230         if(!this.isLeaf() && !this.loaded){
32231             return true;
32232         }else{
32233             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32234         }
32235     },
32236
32237     /**
32238      * Trigger a reload for this node
32239      * @param {Function} callback
32240      */
32241     reload : function(callback){
32242         this.collapse(false, false);
32243         while(this.firstChild){
32244             this.removeChild(this.firstChild);
32245         }
32246         this.childrenRendered = false;
32247         this.loaded = false;
32248         if(this.isHiddenRoot()){
32249             this.expanded = false;
32250         }
32251         this.expand(false, false, callback);
32252     }
32253 });/*
32254  * Based on:
32255  * Ext JS Library 1.1.1
32256  * Copyright(c) 2006-2007, Ext JS, LLC.
32257  *
32258  * Originally Released Under LGPL - original licence link has changed is not relivant.
32259  *
32260  * Fork - LGPL
32261  * <script type="text/javascript">
32262  */
32263  
32264 /**
32265  * @class Roo.tree.TreeNodeUI
32266  * @constructor
32267  * @param {Object} node The node to render
32268  * The TreeNode UI implementation is separate from the
32269  * tree implementation. Unless you are customizing the tree UI,
32270  * you should never have to use this directly.
32271  */
32272 Roo.tree.TreeNodeUI = function(node){
32273     this.node = node;
32274     this.rendered = false;
32275     this.animating = false;
32276     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32277 };
32278
32279 Roo.tree.TreeNodeUI.prototype = {
32280     removeChild : function(node){
32281         if(this.rendered){
32282             this.ctNode.removeChild(node.ui.getEl());
32283         }
32284     },
32285
32286     beforeLoad : function(){
32287          this.addClass("x-tree-node-loading");
32288     },
32289
32290     afterLoad : function(){
32291          this.removeClass("x-tree-node-loading");
32292     },
32293
32294     onTextChange : function(node, text, oldText){
32295         if(this.rendered){
32296             this.textNode.innerHTML = text;
32297         }
32298     },
32299
32300     onDisableChange : function(node, state){
32301         this.disabled = state;
32302         if(state){
32303             this.addClass("x-tree-node-disabled");
32304         }else{
32305             this.removeClass("x-tree-node-disabled");
32306         }
32307     },
32308
32309     onSelectedChange : function(state){
32310         if(state){
32311             this.focus();
32312             this.addClass("x-tree-selected");
32313         }else{
32314             //this.blur();
32315             this.removeClass("x-tree-selected");
32316         }
32317     },
32318
32319     onMove : function(tree, node, oldParent, newParent, index, refNode){
32320         this.childIndent = null;
32321         if(this.rendered){
32322             var targetNode = newParent.ui.getContainer();
32323             if(!targetNode){//target not rendered
32324                 this.holder = document.createElement("div");
32325                 this.holder.appendChild(this.wrap);
32326                 return;
32327             }
32328             var insertBefore = refNode ? refNode.ui.getEl() : null;
32329             if(insertBefore){
32330                 targetNode.insertBefore(this.wrap, insertBefore);
32331             }else{
32332                 targetNode.appendChild(this.wrap);
32333             }
32334             this.node.renderIndent(true);
32335         }
32336     },
32337
32338     addClass : function(cls){
32339         if(this.elNode){
32340             Roo.fly(this.elNode).addClass(cls);
32341         }
32342     },
32343
32344     removeClass : function(cls){
32345         if(this.elNode){
32346             Roo.fly(this.elNode).removeClass(cls);
32347         }
32348     },
32349
32350     remove : function(){
32351         if(this.rendered){
32352             this.holder = document.createElement("div");
32353             this.holder.appendChild(this.wrap);
32354         }
32355     },
32356
32357     fireEvent : function(){
32358         return this.node.fireEvent.apply(this.node, arguments);
32359     },
32360
32361     initEvents : function(){
32362         this.node.on("move", this.onMove, this);
32363         var E = Roo.EventManager;
32364         var a = this.anchor;
32365
32366         var el = Roo.fly(a, '_treeui');
32367
32368         if(Roo.isOpera){ // opera render bug ignores the CSS
32369             el.setStyle("text-decoration", "none");
32370         }
32371
32372         el.on("click", this.onClick, this);
32373         el.on("dblclick", this.onDblClick, this);
32374
32375         if(this.checkbox){
32376             Roo.EventManager.on(this.checkbox,
32377                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32378         }
32379
32380         el.on("contextmenu", this.onContextMenu, this);
32381
32382         var icon = Roo.fly(this.iconNode);
32383         icon.on("click", this.onClick, this);
32384         icon.on("dblclick", this.onDblClick, this);
32385         icon.on("contextmenu", this.onContextMenu, this);
32386         E.on(this.ecNode, "click", this.ecClick, this, true);
32387
32388         if(this.node.disabled){
32389             this.addClass("x-tree-node-disabled");
32390         }
32391         if(this.node.hidden){
32392             this.addClass("x-tree-node-disabled");
32393         }
32394         var ot = this.node.getOwnerTree();
32395         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32396         if(dd && (!this.node.isRoot || ot.rootVisible)){
32397             Roo.dd.Registry.register(this.elNode, {
32398                 node: this.node,
32399                 handles: this.getDDHandles(),
32400                 isHandle: false
32401             });
32402         }
32403     },
32404
32405     getDDHandles : function(){
32406         return [this.iconNode, this.textNode];
32407     },
32408
32409     hide : function(){
32410         if(this.rendered){
32411             this.wrap.style.display = "none";
32412         }
32413     },
32414
32415     show : function(){
32416         if(this.rendered){
32417             this.wrap.style.display = "";
32418         }
32419     },
32420
32421     onContextMenu : function(e){
32422         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32423             e.preventDefault();
32424             this.focus();
32425             this.fireEvent("contextmenu", this.node, e);
32426         }
32427     },
32428
32429     onClick : function(e){
32430         if(this.dropping){
32431             e.stopEvent();
32432             return;
32433         }
32434         if(this.fireEvent("beforeclick", this.node, e) !== false){
32435             if(!this.disabled && this.node.attributes.href){
32436                 this.fireEvent("click", this.node, e);
32437                 return;
32438             }
32439             e.preventDefault();
32440             if(this.disabled){
32441                 return;
32442             }
32443
32444             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32445                 this.node.toggle();
32446             }
32447
32448             this.fireEvent("click", this.node, e);
32449         }else{
32450             e.stopEvent();
32451         }
32452     },
32453
32454     onDblClick : function(e){
32455         e.preventDefault();
32456         if(this.disabled){
32457             return;
32458         }
32459         if(this.checkbox){
32460             this.toggleCheck();
32461         }
32462         if(!this.animating && this.node.hasChildNodes()){
32463             this.node.toggle();
32464         }
32465         this.fireEvent("dblclick", this.node, e);
32466     },
32467
32468     onCheckChange : function(){
32469         var checked = this.checkbox.checked;
32470         this.node.attributes.checked = checked;
32471         this.fireEvent('checkchange', this.node, checked);
32472     },
32473
32474     ecClick : function(e){
32475         if(!this.animating && this.node.hasChildNodes()){
32476             this.node.toggle();
32477         }
32478     },
32479
32480     startDrop : function(){
32481         this.dropping = true;
32482     },
32483
32484     // delayed drop so the click event doesn't get fired on a drop
32485     endDrop : function(){
32486        setTimeout(function(){
32487            this.dropping = false;
32488        }.createDelegate(this), 50);
32489     },
32490
32491     expand : function(){
32492         this.updateExpandIcon();
32493         this.ctNode.style.display = "";
32494     },
32495
32496     focus : function(){
32497         if(!this.node.preventHScroll){
32498             try{this.anchor.focus();
32499             }catch(e){}
32500         }else if(!Roo.isIE){
32501             try{
32502                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32503                 var l = noscroll.scrollLeft;
32504                 this.anchor.focus();
32505                 noscroll.scrollLeft = l;
32506             }catch(e){}
32507         }
32508     },
32509
32510     toggleCheck : function(value){
32511         var cb = this.checkbox;
32512         if(cb){
32513             cb.checked = (value === undefined ? !cb.checked : value);
32514         }
32515     },
32516
32517     blur : function(){
32518         try{
32519             this.anchor.blur();
32520         }catch(e){}
32521     },
32522
32523     animExpand : function(callback){
32524         var ct = Roo.get(this.ctNode);
32525         ct.stopFx();
32526         if(!this.node.hasChildNodes()){
32527             this.updateExpandIcon();
32528             this.ctNode.style.display = "";
32529             Roo.callback(callback);
32530             return;
32531         }
32532         this.animating = true;
32533         this.updateExpandIcon();
32534
32535         ct.slideIn('t', {
32536            callback : function(){
32537                this.animating = false;
32538                Roo.callback(callback);
32539             },
32540             scope: this,
32541             duration: this.node.ownerTree.duration || .25
32542         });
32543     },
32544
32545     highlight : function(){
32546         var tree = this.node.getOwnerTree();
32547         Roo.fly(this.wrap).highlight(
32548             tree.hlColor || "C3DAF9",
32549             {endColor: tree.hlBaseColor}
32550         );
32551     },
32552
32553     collapse : function(){
32554         this.updateExpandIcon();
32555         this.ctNode.style.display = "none";
32556     },
32557
32558     animCollapse : function(callback){
32559         var ct = Roo.get(this.ctNode);
32560         ct.enableDisplayMode('block');
32561         ct.stopFx();
32562
32563         this.animating = true;
32564         this.updateExpandIcon();
32565
32566         ct.slideOut('t', {
32567             callback : function(){
32568                this.animating = false;
32569                Roo.callback(callback);
32570             },
32571             scope: this,
32572             duration: this.node.ownerTree.duration || .25
32573         });
32574     },
32575
32576     getContainer : function(){
32577         return this.ctNode;
32578     },
32579
32580     getEl : function(){
32581         return this.wrap;
32582     },
32583
32584     appendDDGhost : function(ghostNode){
32585         ghostNode.appendChild(this.elNode.cloneNode(true));
32586     },
32587
32588     getDDRepairXY : function(){
32589         return Roo.lib.Dom.getXY(this.iconNode);
32590     },
32591
32592     onRender : function(){
32593         this.render();
32594     },
32595
32596     render : function(bulkRender){
32597         var n = this.node, a = n.attributes;
32598         var targetNode = n.parentNode ?
32599               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32600
32601         if(!this.rendered){
32602             this.rendered = true;
32603
32604             this.renderElements(n, a, targetNode, bulkRender);
32605
32606             if(a.qtip){
32607                if(this.textNode.setAttributeNS){
32608                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32609                    if(a.qtipTitle){
32610                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32611                    }
32612                }else{
32613                    this.textNode.setAttribute("ext:qtip", a.qtip);
32614                    if(a.qtipTitle){
32615                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32616                    }
32617                }
32618             }else if(a.qtipCfg){
32619                 a.qtipCfg.target = Roo.id(this.textNode);
32620                 Roo.QuickTips.register(a.qtipCfg);
32621             }
32622             this.initEvents();
32623             if(!this.node.expanded){
32624                 this.updateExpandIcon();
32625             }
32626         }else{
32627             if(bulkRender === true) {
32628                 targetNode.appendChild(this.wrap);
32629             }
32630         }
32631     },
32632
32633     renderElements : function(n, a, targetNode, bulkRender)
32634     {
32635         // add some indent caching, this helps performance when rendering a large tree
32636         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32637         var t = n.getOwnerTree();
32638         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32639         if (typeof(n.attributes.html) != 'undefined') {
32640             txt = n.attributes.html;
32641         }
32642         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32643         var cb = typeof a.checked == 'boolean';
32644         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32645         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32646             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32647             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32648             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32649             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32650             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32651              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32652                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32653             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32654             "</li>"];
32655
32656         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32657             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32658                                 n.nextSibling.ui.getEl(), buf.join(""));
32659         }else{
32660             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32661         }
32662
32663         this.elNode = this.wrap.childNodes[0];
32664         this.ctNode = this.wrap.childNodes[1];
32665         var cs = this.elNode.childNodes;
32666         this.indentNode = cs[0];
32667         this.ecNode = cs[1];
32668         this.iconNode = cs[2];
32669         var index = 3;
32670         if(cb){
32671             this.checkbox = cs[3];
32672             index++;
32673         }
32674         this.anchor = cs[index];
32675         this.textNode = cs[index].firstChild;
32676     },
32677
32678     getAnchor : function(){
32679         return this.anchor;
32680     },
32681
32682     getTextEl : function(){
32683         return this.textNode;
32684     },
32685
32686     getIconEl : function(){
32687         return this.iconNode;
32688     },
32689
32690     isChecked : function(){
32691         return this.checkbox ? this.checkbox.checked : false;
32692     },
32693
32694     updateExpandIcon : function(){
32695         if(this.rendered){
32696             var n = this.node, c1, c2;
32697             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32698             var hasChild = n.hasChildNodes();
32699             if(hasChild){
32700                 if(n.expanded){
32701                     cls += "-minus";
32702                     c1 = "x-tree-node-collapsed";
32703                     c2 = "x-tree-node-expanded";
32704                 }else{
32705                     cls += "-plus";
32706                     c1 = "x-tree-node-expanded";
32707                     c2 = "x-tree-node-collapsed";
32708                 }
32709                 if(this.wasLeaf){
32710                     this.removeClass("x-tree-node-leaf");
32711                     this.wasLeaf = false;
32712                 }
32713                 if(this.c1 != c1 || this.c2 != c2){
32714                     Roo.fly(this.elNode).replaceClass(c1, c2);
32715                     this.c1 = c1; this.c2 = c2;
32716                 }
32717             }else{
32718                 // this changes non-leafs into leafs if they have no children.
32719                 // it's not very rational behaviour..
32720                 
32721                 if(!this.wasLeaf && this.node.leaf){
32722                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32723                     delete this.c1;
32724                     delete this.c2;
32725                     this.wasLeaf = true;
32726                 }
32727             }
32728             var ecc = "x-tree-ec-icon "+cls;
32729             if(this.ecc != ecc){
32730                 this.ecNode.className = ecc;
32731                 this.ecc = ecc;
32732             }
32733         }
32734     },
32735
32736     getChildIndent : function(){
32737         if(!this.childIndent){
32738             var buf = [];
32739             var p = this.node;
32740             while(p){
32741                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32742                     if(!p.isLast()) {
32743                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32744                     } else {
32745                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32746                     }
32747                 }
32748                 p = p.parentNode;
32749             }
32750             this.childIndent = buf.join("");
32751         }
32752         return this.childIndent;
32753     },
32754
32755     renderIndent : function(){
32756         if(this.rendered){
32757             var indent = "";
32758             var p = this.node.parentNode;
32759             if(p){
32760                 indent = p.ui.getChildIndent();
32761             }
32762             if(this.indentMarkup != indent){ // don't rerender if not required
32763                 this.indentNode.innerHTML = indent;
32764                 this.indentMarkup = indent;
32765             }
32766             this.updateExpandIcon();
32767         }
32768     }
32769 };
32770
32771 Roo.tree.RootTreeNodeUI = function(){
32772     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32773 };
32774 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32775     render : function(){
32776         if(!this.rendered){
32777             var targetNode = this.node.ownerTree.innerCt.dom;
32778             this.node.expanded = true;
32779             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32780             this.wrap = this.ctNode = targetNode.firstChild;
32781         }
32782     },
32783     collapse : function(){
32784     },
32785     expand : function(){
32786     }
32787 });/*
32788  * Based on:
32789  * Ext JS Library 1.1.1
32790  * Copyright(c) 2006-2007, Ext JS, LLC.
32791  *
32792  * Originally Released Under LGPL - original licence link has changed is not relivant.
32793  *
32794  * Fork - LGPL
32795  * <script type="text/javascript">
32796  */
32797 /**
32798  * @class Roo.tree.TreeLoader
32799  * @extends Roo.util.Observable
32800  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32801  * nodes from a specified URL. The response must be a javascript Array definition
32802  * who's elements are node definition objects. eg:
32803  * <pre><code>
32804    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32805     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32806 </code></pre>
32807  * <br><br>
32808  * A server request is sent, and child nodes are loaded only when a node is expanded.
32809  * The loading node's id is passed to the server under the parameter name "node" to
32810  * enable the server to produce the correct child nodes.
32811  * <br><br>
32812  * To pass extra parameters, an event handler may be attached to the "beforeload"
32813  * event, and the parameters specified in the TreeLoader's baseParams property:
32814  * <pre><code>
32815     myTreeLoader.on("beforeload", function(treeLoader, node) {
32816         this.baseParams.category = node.attributes.category;
32817     }, this);
32818 </code></pre><
32819  * This would pass an HTTP parameter called "category" to the server containing
32820  * the value of the Node's "category" attribute.
32821  * @constructor
32822  * Creates a new Treeloader.
32823  * @param {Object} config A config object containing config properties.
32824  */
32825 Roo.tree.TreeLoader = function(config){
32826     this.baseParams = {};
32827     this.requestMethod = "POST";
32828     Roo.apply(this, config);
32829
32830     this.addEvents({
32831     
32832         /**
32833          * @event beforeload
32834          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32835          * @param {Object} This TreeLoader object.
32836          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32837          * @param {Object} callback The callback function specified in the {@link #load} call.
32838          */
32839         beforeload : true,
32840         /**
32841          * @event load
32842          * Fires when the node has been successfuly loaded.
32843          * @param {Object} This TreeLoader object.
32844          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32845          * @param {Object} response The response object containing the data from the server.
32846          */
32847         load : true,
32848         /**
32849          * @event loadexception
32850          * Fires if the network request failed.
32851          * @param {Object} This TreeLoader object.
32852          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32853          * @param {Object} response The response object containing the data from the server.
32854          */
32855         loadexception : true,
32856         /**
32857          * @event create
32858          * Fires before a node is created, enabling you to return custom Node types 
32859          * @param {Object} This TreeLoader object.
32860          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32861          */
32862         create : true
32863     });
32864
32865     Roo.tree.TreeLoader.superclass.constructor.call(this);
32866 };
32867
32868 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32869     /**
32870     * @cfg {String} dataUrl The URL from which to request a Json string which
32871     * specifies an array of node definition object representing the child nodes
32872     * to be loaded.
32873     */
32874     /**
32875     * @cfg {Object} baseParams (optional) An object containing properties which
32876     * specify HTTP parameters to be passed to each request for child nodes.
32877     */
32878     /**
32879     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32880     * created by this loader. If the attributes sent by the server have an attribute in this object,
32881     * they take priority.
32882     */
32883     /**
32884     * @cfg {Object} uiProviders (optional) An object containing properties which
32885     * 
32886     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32887     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32888     * <i>uiProvider</i> attribute of a returned child node is a string rather
32889     * than a reference to a TreeNodeUI implementation, this that string value
32890     * is used as a property name in the uiProviders object. You can define the provider named
32891     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32892     */
32893     uiProviders : {},
32894
32895     /**
32896     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32897     * child nodes before loading.
32898     */
32899     clearOnLoad : true,
32900
32901     /**
32902     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32903     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32904     * Grid query { data : [ .....] }
32905     */
32906     
32907     root : false,
32908      /**
32909     * @cfg {String} queryParam (optional) 
32910     * Name of the query as it will be passed on the querystring (defaults to 'node')
32911     * eg. the request will be ?node=[id]
32912     */
32913     
32914     
32915     queryParam: false,
32916     
32917     /**
32918      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32919      * This is called automatically when a node is expanded, but may be used to reload
32920      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32921      * @param {Roo.tree.TreeNode} node
32922      * @param {Function} callback
32923      */
32924     load : function(node, callback){
32925         if(this.clearOnLoad){
32926             while(node.firstChild){
32927                 node.removeChild(node.firstChild);
32928             }
32929         }
32930         if(node.attributes.children){ // preloaded json children
32931             var cs = node.attributes.children;
32932             for(var i = 0, len = cs.length; i < len; i++){
32933                 node.appendChild(this.createNode(cs[i]));
32934             }
32935             if(typeof callback == "function"){
32936                 callback();
32937             }
32938         }else if(this.dataUrl){
32939             this.requestData(node, callback);
32940         }
32941     },
32942
32943     getParams: function(node){
32944         var buf = [], bp = this.baseParams;
32945         for(var key in bp){
32946             if(typeof bp[key] != "function"){
32947                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32948             }
32949         }
32950         var n = this.queryParam === false ? 'node' : this.queryParam;
32951         buf.push(n + "=", encodeURIComponent(node.id));
32952         return buf.join("");
32953     },
32954
32955     requestData : function(node, callback){
32956         if(this.fireEvent("beforeload", this, node, callback) !== false){
32957             this.transId = Roo.Ajax.request({
32958                 method:this.requestMethod,
32959                 url: this.dataUrl||this.url,
32960                 success: this.handleResponse,
32961                 failure: this.handleFailure,
32962                 scope: this,
32963                 argument: {callback: callback, node: node},
32964                 params: this.getParams(node)
32965             });
32966         }else{
32967             // if the load is cancelled, make sure we notify
32968             // the node that we are done
32969             if(typeof callback == "function"){
32970                 callback();
32971             }
32972         }
32973     },
32974
32975     isLoading : function(){
32976         return this.transId ? true : false;
32977     },
32978
32979     abort : function(){
32980         if(this.isLoading()){
32981             Roo.Ajax.abort(this.transId);
32982         }
32983     },
32984
32985     // private
32986     createNode : function(attr)
32987     {
32988         // apply baseAttrs, nice idea Corey!
32989         if(this.baseAttrs){
32990             Roo.applyIf(attr, this.baseAttrs);
32991         }
32992         if(this.applyLoader !== false){
32993             attr.loader = this;
32994         }
32995         // uiProvider = depreciated..
32996         
32997         if(typeof(attr.uiProvider) == 'string'){
32998            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32999                 /**  eval:var:attr */ eval(attr.uiProvider);
33000         }
33001         if(typeof(this.uiProviders['default']) != 'undefined') {
33002             attr.uiProvider = this.uiProviders['default'];
33003         }
33004         
33005         this.fireEvent('create', this, attr);
33006         
33007         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33008         return(attr.leaf ?
33009                         new Roo.tree.TreeNode(attr) :
33010                         new Roo.tree.AsyncTreeNode(attr));
33011     },
33012
33013     processResponse : function(response, node, callback)
33014     {
33015         var json = response.responseText;
33016         try {
33017             
33018             var o = Roo.decode(json);
33019             
33020             if (!o.success) {
33021                 // it's a failure condition.
33022                 var a = response.argument;
33023                 this.fireEvent("loadexception", this, a.node, response);
33024                 Roo.log("Load failed - should have a handler really");
33025                 return;
33026             }
33027             
33028             if (this.root !== false) {
33029                 o = o[this.root];
33030             }
33031             
33032             for(var i = 0, len = o.length; i < len; i++){
33033                 var n = this.createNode(o[i]);
33034                 if(n){
33035                     node.appendChild(n);
33036                 }
33037             }
33038             if(typeof callback == "function"){
33039                 callback(this, node);
33040             }
33041         }catch(e){
33042             this.handleFailure(response);
33043         }
33044     },
33045
33046     handleResponse : function(response){
33047         this.transId = false;
33048         var a = response.argument;
33049         this.processResponse(response, a.node, a.callback);
33050         this.fireEvent("load", this, a.node, response);
33051     },
33052
33053     handleFailure : function(response)
33054     {
33055         // should handle failure better..
33056         this.transId = false;
33057         var a = response.argument;
33058         this.fireEvent("loadexception", this, a.node, response);
33059         if(typeof a.callback == "function"){
33060             a.callback(this, a.node);
33061         }
33062     }
33063 });/*
33064  * Based on:
33065  * Ext JS Library 1.1.1
33066  * Copyright(c) 2006-2007, Ext JS, LLC.
33067  *
33068  * Originally Released Under LGPL - original licence link has changed is not relivant.
33069  *
33070  * Fork - LGPL
33071  * <script type="text/javascript">
33072  */
33073
33074 /**
33075 * @class Roo.tree.TreeFilter
33076 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33077 * @param {TreePanel} tree
33078 * @param {Object} config (optional)
33079  */
33080 Roo.tree.TreeFilter = function(tree, config){
33081     this.tree = tree;
33082     this.filtered = {};
33083     Roo.apply(this, config);
33084 };
33085
33086 Roo.tree.TreeFilter.prototype = {
33087     clearBlank:false,
33088     reverse:false,
33089     autoClear:false,
33090     remove:false,
33091
33092      /**
33093      * Filter the data by a specific attribute.
33094      * @param {String/RegExp} value Either string that the attribute value
33095      * should start with or a RegExp to test against the attribute
33096      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33097      * @param {TreeNode} startNode (optional) The node to start the filter at.
33098      */
33099     filter : function(value, attr, startNode){
33100         attr = attr || "text";
33101         var f;
33102         if(typeof value == "string"){
33103             var vlen = value.length;
33104             // auto clear empty filter
33105             if(vlen == 0 && this.clearBlank){
33106                 this.clear();
33107                 return;
33108             }
33109             value = value.toLowerCase();
33110             f = function(n){
33111                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33112             };
33113         }else if(value.exec){ // regex?
33114             f = function(n){
33115                 return value.test(n.attributes[attr]);
33116             };
33117         }else{
33118             throw 'Illegal filter type, must be string or regex';
33119         }
33120         this.filterBy(f, null, startNode);
33121         },
33122
33123     /**
33124      * Filter by a function. The passed function will be called with each
33125      * node in the tree (or from the startNode). If the function returns true, the node is kept
33126      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33127      * @param {Function} fn The filter function
33128      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33129      */
33130     filterBy : function(fn, scope, startNode){
33131         startNode = startNode || this.tree.root;
33132         if(this.autoClear){
33133             this.clear();
33134         }
33135         var af = this.filtered, rv = this.reverse;
33136         var f = function(n){
33137             if(n == startNode){
33138                 return true;
33139             }
33140             if(af[n.id]){
33141                 return false;
33142             }
33143             var m = fn.call(scope || n, n);
33144             if(!m || rv){
33145                 af[n.id] = n;
33146                 n.ui.hide();
33147                 return false;
33148             }
33149             return true;
33150         };
33151         startNode.cascade(f);
33152         if(this.remove){
33153            for(var id in af){
33154                if(typeof id != "function"){
33155                    var n = af[id];
33156                    if(n && n.parentNode){
33157                        n.parentNode.removeChild(n);
33158                    }
33159                }
33160            }
33161         }
33162     },
33163
33164     /**
33165      * Clears the current filter. Note: with the "remove" option
33166      * set a filter cannot be cleared.
33167      */
33168     clear : function(){
33169         var t = this.tree;
33170         var af = this.filtered;
33171         for(var id in af){
33172             if(typeof id != "function"){
33173                 var n = af[id];
33174                 if(n){
33175                     n.ui.show();
33176                 }
33177             }
33178         }
33179         this.filtered = {};
33180     }
33181 };
33182 /*
33183  * Based on:
33184  * Ext JS Library 1.1.1
33185  * Copyright(c) 2006-2007, Ext JS, LLC.
33186  *
33187  * Originally Released Under LGPL - original licence link has changed is not relivant.
33188  *
33189  * Fork - LGPL
33190  * <script type="text/javascript">
33191  */
33192  
33193
33194 /**
33195  * @class Roo.tree.TreeSorter
33196  * Provides sorting of nodes in a TreePanel
33197  * 
33198  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33199  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33200  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33201  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33202  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33203  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33204  * @constructor
33205  * @param {TreePanel} tree
33206  * @param {Object} config
33207  */
33208 Roo.tree.TreeSorter = function(tree, config){
33209     Roo.apply(this, config);
33210     tree.on("beforechildrenrendered", this.doSort, this);
33211     tree.on("append", this.updateSort, this);
33212     tree.on("insert", this.updateSort, this);
33213     
33214     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33215     var p = this.property || "text";
33216     var sortType = this.sortType;
33217     var fs = this.folderSort;
33218     var cs = this.caseSensitive === true;
33219     var leafAttr = this.leafAttr || 'leaf';
33220
33221     this.sortFn = function(n1, n2){
33222         if(fs){
33223             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33224                 return 1;
33225             }
33226             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33227                 return -1;
33228             }
33229         }
33230         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33231         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33232         if(v1 < v2){
33233                         return dsc ? +1 : -1;
33234                 }else if(v1 > v2){
33235                         return dsc ? -1 : +1;
33236         }else{
33237                 return 0;
33238         }
33239     };
33240 };
33241
33242 Roo.tree.TreeSorter.prototype = {
33243     doSort : function(node){
33244         node.sort(this.sortFn);
33245     },
33246     
33247     compareNodes : function(n1, n2){
33248         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33249     },
33250     
33251     updateSort : function(tree, node){
33252         if(node.childrenRendered){
33253             this.doSort.defer(1, this, [node]);
33254         }
33255     }
33256 };/*
33257  * Based on:
33258  * Ext JS Library 1.1.1
33259  * Copyright(c) 2006-2007, Ext JS, LLC.
33260  *
33261  * Originally Released Under LGPL - original licence link has changed is not relivant.
33262  *
33263  * Fork - LGPL
33264  * <script type="text/javascript">
33265  */
33266
33267 if(Roo.dd.DropZone){
33268     
33269 Roo.tree.TreeDropZone = function(tree, config){
33270     this.allowParentInsert = false;
33271     this.allowContainerDrop = false;
33272     this.appendOnly = false;
33273     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33274     this.tree = tree;
33275     this.lastInsertClass = "x-tree-no-status";
33276     this.dragOverData = {};
33277 };
33278
33279 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33280     ddGroup : "TreeDD",
33281     
33282     expandDelay : 1000,
33283     
33284     expandNode : function(node){
33285         if(node.hasChildNodes() && !node.isExpanded()){
33286             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33287         }
33288     },
33289     
33290     queueExpand : function(node){
33291         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33292     },
33293     
33294     cancelExpand : function(){
33295         if(this.expandProcId){
33296             clearTimeout(this.expandProcId);
33297             this.expandProcId = false;
33298         }
33299     },
33300     
33301     isValidDropPoint : function(n, pt, dd, e, data){
33302         if(!n || !data){ return false; }
33303         var targetNode = n.node;
33304         var dropNode = data.node;
33305         // default drop rules
33306         if(!(targetNode && targetNode.isTarget && pt)){
33307             return false;
33308         }
33309         if(pt == "append" && targetNode.allowChildren === false){
33310             return false;
33311         }
33312         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33313             return false;
33314         }
33315         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33316             return false;
33317         }
33318         // reuse the object
33319         var overEvent = this.dragOverData;
33320         overEvent.tree = this.tree;
33321         overEvent.target = targetNode;
33322         overEvent.data = data;
33323         overEvent.point = pt;
33324         overEvent.source = dd;
33325         overEvent.rawEvent = e;
33326         overEvent.dropNode = dropNode;
33327         overEvent.cancel = false;  
33328         var result = this.tree.fireEvent("nodedragover", overEvent);
33329         return overEvent.cancel === false && result !== false;
33330     },
33331     
33332     getDropPoint : function(e, n, dd){
33333         var tn = n.node;
33334         if(tn.isRoot){
33335             return tn.allowChildren !== false ? "append" : false; // always append for root
33336         }
33337         var dragEl = n.ddel;
33338         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33339         var y = Roo.lib.Event.getPageY(e);
33340         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33341         
33342         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33343         var noAppend = tn.allowChildren === false;
33344         if(this.appendOnly || tn.parentNode.allowChildren === false){
33345             return noAppend ? false : "append";
33346         }
33347         var noBelow = false;
33348         if(!this.allowParentInsert){
33349             noBelow = tn.hasChildNodes() && tn.isExpanded();
33350         }
33351         var q = (b - t) / (noAppend ? 2 : 3);
33352         if(y >= t && y < (t + q)){
33353             return "above";
33354         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33355             return "below";
33356         }else{
33357             return "append";
33358         }
33359     },
33360     
33361     onNodeEnter : function(n, dd, e, data){
33362         this.cancelExpand();
33363     },
33364     
33365     onNodeOver : function(n, dd, e, data){
33366         var pt = this.getDropPoint(e, n, dd);
33367         var node = n.node;
33368         
33369         // auto node expand check
33370         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33371             this.queueExpand(node);
33372         }else if(pt != "append"){
33373             this.cancelExpand();
33374         }
33375         
33376         // set the insert point style on the target node
33377         var returnCls = this.dropNotAllowed;
33378         if(this.isValidDropPoint(n, pt, dd, e, data)){
33379            if(pt){
33380                var el = n.ddel;
33381                var cls;
33382                if(pt == "above"){
33383                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33384                    cls = "x-tree-drag-insert-above";
33385                }else if(pt == "below"){
33386                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33387                    cls = "x-tree-drag-insert-below";
33388                }else{
33389                    returnCls = "x-tree-drop-ok-append";
33390                    cls = "x-tree-drag-append";
33391                }
33392                if(this.lastInsertClass != cls){
33393                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33394                    this.lastInsertClass = cls;
33395                }
33396            }
33397        }
33398        return returnCls;
33399     },
33400     
33401     onNodeOut : function(n, dd, e, data){
33402         this.cancelExpand();
33403         this.removeDropIndicators(n);
33404     },
33405     
33406     onNodeDrop : function(n, dd, e, data){
33407         var point = this.getDropPoint(e, n, dd);
33408         var targetNode = n.node;
33409         targetNode.ui.startDrop();
33410         if(!this.isValidDropPoint(n, point, dd, e, data)){
33411             targetNode.ui.endDrop();
33412             return false;
33413         }
33414         // first try to find the drop node
33415         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33416         var dropEvent = {
33417             tree : this.tree,
33418             target: targetNode,
33419             data: data,
33420             point: point,
33421             source: dd,
33422             rawEvent: e,
33423             dropNode: dropNode,
33424             cancel: !dropNode   
33425         };
33426         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33427         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33428             targetNode.ui.endDrop();
33429             return false;
33430         }
33431         // allow target changing
33432         targetNode = dropEvent.target;
33433         if(point == "append" && !targetNode.isExpanded()){
33434             targetNode.expand(false, null, function(){
33435                 this.completeDrop(dropEvent);
33436             }.createDelegate(this));
33437         }else{
33438             this.completeDrop(dropEvent);
33439         }
33440         return true;
33441     },
33442     
33443     completeDrop : function(de){
33444         var ns = de.dropNode, p = de.point, t = de.target;
33445         if(!(ns instanceof Array)){
33446             ns = [ns];
33447         }
33448         var n;
33449         for(var i = 0, len = ns.length; i < len; i++){
33450             n = ns[i];
33451             if(p == "above"){
33452                 t.parentNode.insertBefore(n, t);
33453             }else if(p == "below"){
33454                 t.parentNode.insertBefore(n, t.nextSibling);
33455             }else{
33456                 t.appendChild(n);
33457             }
33458         }
33459         n.ui.focus();
33460         if(this.tree.hlDrop){
33461             n.ui.highlight();
33462         }
33463         t.ui.endDrop();
33464         this.tree.fireEvent("nodedrop", de);
33465     },
33466     
33467     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33468         if(this.tree.hlDrop){
33469             dropNode.ui.focus();
33470             dropNode.ui.highlight();
33471         }
33472         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33473     },
33474     
33475     getTree : function(){
33476         return this.tree;
33477     },
33478     
33479     removeDropIndicators : function(n){
33480         if(n && n.ddel){
33481             var el = n.ddel;
33482             Roo.fly(el).removeClass([
33483                     "x-tree-drag-insert-above",
33484                     "x-tree-drag-insert-below",
33485                     "x-tree-drag-append"]);
33486             this.lastInsertClass = "_noclass";
33487         }
33488     },
33489     
33490     beforeDragDrop : function(target, e, id){
33491         this.cancelExpand();
33492         return true;
33493     },
33494     
33495     afterRepair : function(data){
33496         if(data && Roo.enableFx){
33497             data.node.ui.highlight();
33498         }
33499         this.hideProxy();
33500     }    
33501 });
33502
33503 }
33504 /*
33505  * Based on:
33506  * Ext JS Library 1.1.1
33507  * Copyright(c) 2006-2007, Ext JS, LLC.
33508  *
33509  * Originally Released Under LGPL - original licence link has changed is not relivant.
33510  *
33511  * Fork - LGPL
33512  * <script type="text/javascript">
33513  */
33514  
33515
33516 if(Roo.dd.DragZone){
33517 Roo.tree.TreeDragZone = function(tree, config){
33518     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33519     this.tree = tree;
33520 };
33521
33522 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33523     ddGroup : "TreeDD",
33524     
33525     onBeforeDrag : function(data, e){
33526         var n = data.node;
33527         return n && n.draggable && !n.disabled;
33528     },
33529     
33530     onInitDrag : function(e){
33531         var data = this.dragData;
33532         this.tree.getSelectionModel().select(data.node);
33533         this.proxy.update("");
33534         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33535         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33536     },
33537     
33538     getRepairXY : function(e, data){
33539         return data.node.ui.getDDRepairXY();
33540     },
33541     
33542     onEndDrag : function(data, e){
33543         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33544     },
33545     
33546     onValidDrop : function(dd, e, id){
33547         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33548         this.hideProxy();
33549     },
33550     
33551     beforeInvalidDrop : function(e, id){
33552         // this scrolls the original position back into view
33553         var sm = this.tree.getSelectionModel();
33554         sm.clearSelections();
33555         sm.select(this.dragData.node);
33556     }
33557 });
33558 }/*
33559  * Based on:
33560  * Ext JS Library 1.1.1
33561  * Copyright(c) 2006-2007, Ext JS, LLC.
33562  *
33563  * Originally Released Under LGPL - original licence link has changed is not relivant.
33564  *
33565  * Fork - LGPL
33566  * <script type="text/javascript">
33567  */
33568 /**
33569  * @class Roo.tree.TreeEditor
33570  * @extends Roo.Editor
33571  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33572  * as the editor field.
33573  * @constructor
33574  * @param {Object} config (used to be the tree panel.)
33575  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33576  * 
33577  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33578  * @cfg {Roo.form.TextField|Object} field The field configuration
33579  *
33580  * 
33581  */
33582 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33583     var tree = config;
33584     var field;
33585     if (oldconfig) { // old style..
33586         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33587     } else {
33588         // new style..
33589         tree = config.tree;
33590         config.field = config.field  || {};
33591         config.field.xtype = 'TextField';
33592         field = Roo.factory(config.field, Roo.form);
33593     }
33594     config = config || {};
33595     
33596     
33597     this.addEvents({
33598         /**
33599          * @event beforenodeedit
33600          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33601          * false from the handler of this event.
33602          * @param {Editor} this
33603          * @param {Roo.tree.Node} node 
33604          */
33605         "beforenodeedit" : true
33606     });
33607     
33608     //Roo.log(config);
33609     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33610
33611     this.tree = tree;
33612
33613     tree.on('beforeclick', this.beforeNodeClick, this);
33614     tree.getTreeEl().on('mousedown', this.hide, this);
33615     this.on('complete', this.updateNode, this);
33616     this.on('beforestartedit', this.fitToTree, this);
33617     this.on('startedit', this.bindScroll, this, {delay:10});
33618     this.on('specialkey', this.onSpecialKey, this);
33619 };
33620
33621 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33622     /**
33623      * @cfg {String} alignment
33624      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33625      */
33626     alignment: "l-l",
33627     // inherit
33628     autoSize: false,
33629     /**
33630      * @cfg {Boolean} hideEl
33631      * True to hide the bound element while the editor is displayed (defaults to false)
33632      */
33633     hideEl : false,
33634     /**
33635      * @cfg {String} cls
33636      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33637      */
33638     cls: "x-small-editor x-tree-editor",
33639     /**
33640      * @cfg {Boolean} shim
33641      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33642      */
33643     shim:false,
33644     // inherit
33645     shadow:"frame",
33646     /**
33647      * @cfg {Number} maxWidth
33648      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33649      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33650      * scroll and client offsets into account prior to each edit.
33651      */
33652     maxWidth: 250,
33653
33654     editDelay : 350,
33655
33656     // private
33657     fitToTree : function(ed, el){
33658         var td = this.tree.getTreeEl().dom, nd = el.dom;
33659         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33660             td.scrollLeft = nd.offsetLeft;
33661         }
33662         var w = Math.min(
33663                 this.maxWidth,
33664                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33665         this.setSize(w, '');
33666         
33667         return this.fireEvent('beforenodeedit', this, this.editNode);
33668         
33669     },
33670
33671     // private
33672     triggerEdit : function(node){
33673         this.completeEdit();
33674         this.editNode = node;
33675         this.startEdit(node.ui.textNode, node.text);
33676     },
33677
33678     // private
33679     bindScroll : function(){
33680         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33681     },
33682
33683     // private
33684     beforeNodeClick : function(node, e){
33685         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33686         this.lastClick = new Date();
33687         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33688             e.stopEvent();
33689             this.triggerEdit(node);
33690             return false;
33691         }
33692         return true;
33693     },
33694
33695     // private
33696     updateNode : function(ed, value){
33697         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33698         this.editNode.setText(value);
33699     },
33700
33701     // private
33702     onHide : function(){
33703         Roo.tree.TreeEditor.superclass.onHide.call(this);
33704         if(this.editNode){
33705             this.editNode.ui.focus();
33706         }
33707     },
33708
33709     // private
33710     onSpecialKey : function(field, e){
33711         var k = e.getKey();
33712         if(k == e.ESC){
33713             e.stopEvent();
33714             this.cancelEdit();
33715         }else if(k == e.ENTER && !e.hasModifier()){
33716             e.stopEvent();
33717             this.completeEdit();
33718         }
33719     }
33720 });//<Script type="text/javascript">
33721 /*
33722  * Based on:
33723  * Ext JS Library 1.1.1
33724  * Copyright(c) 2006-2007, Ext JS, LLC.
33725  *
33726  * Originally Released Under LGPL - original licence link has changed is not relivant.
33727  *
33728  * Fork - LGPL
33729  * <script type="text/javascript">
33730  */
33731  
33732 /**
33733  * Not documented??? - probably should be...
33734  */
33735
33736 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33737     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33738     
33739     renderElements : function(n, a, targetNode, bulkRender){
33740         //consel.log("renderElements?");
33741         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33742
33743         var t = n.getOwnerTree();
33744         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33745         
33746         var cols = t.columns;
33747         var bw = t.borderWidth;
33748         var c = cols[0];
33749         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33750          var cb = typeof a.checked == "boolean";
33751         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33752         var colcls = 'x-t-' + tid + '-c0';
33753         var buf = [
33754             '<li class="x-tree-node">',
33755             
33756                 
33757                 '<div class="x-tree-node-el ', a.cls,'">',
33758                     // extran...
33759                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33760                 
33761                 
33762                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33763                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33764                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33765                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33766                            (a.iconCls ? ' '+a.iconCls : ''),
33767                            '" unselectable="on" />',
33768                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33769                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33770                              
33771                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33772                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33773                             '<span unselectable="on" qtip="' + tx + '">',
33774                              tx,
33775                              '</span></a>' ,
33776                     '</div>',
33777                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33778                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33779                  ];
33780         for(var i = 1, len = cols.length; i < len; i++){
33781             c = cols[i];
33782             colcls = 'x-t-' + tid + '-c' +i;
33783             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33784             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33785                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33786                       "</div>");
33787          }
33788          
33789          buf.push(
33790             '</a>',
33791             '<div class="x-clear"></div></div>',
33792             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33793             "</li>");
33794         
33795         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33796             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33797                                 n.nextSibling.ui.getEl(), buf.join(""));
33798         }else{
33799             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33800         }
33801         var el = this.wrap.firstChild;
33802         this.elRow = el;
33803         this.elNode = el.firstChild;
33804         this.ranchor = el.childNodes[1];
33805         this.ctNode = this.wrap.childNodes[1];
33806         var cs = el.firstChild.childNodes;
33807         this.indentNode = cs[0];
33808         this.ecNode = cs[1];
33809         this.iconNode = cs[2];
33810         var index = 3;
33811         if(cb){
33812             this.checkbox = cs[3];
33813             index++;
33814         }
33815         this.anchor = cs[index];
33816         
33817         this.textNode = cs[index].firstChild;
33818         
33819         //el.on("click", this.onClick, this);
33820         //el.on("dblclick", this.onDblClick, this);
33821         
33822         
33823        // console.log(this);
33824     },
33825     initEvents : function(){
33826         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33827         
33828             
33829         var a = this.ranchor;
33830
33831         var el = Roo.get(a);
33832
33833         if(Roo.isOpera){ // opera render bug ignores the CSS
33834             el.setStyle("text-decoration", "none");
33835         }
33836
33837         el.on("click", this.onClick, this);
33838         el.on("dblclick", this.onDblClick, this);
33839         el.on("contextmenu", this.onContextMenu, this);
33840         
33841     },
33842     
33843     /*onSelectedChange : function(state){
33844         if(state){
33845             this.focus();
33846             this.addClass("x-tree-selected");
33847         }else{
33848             //this.blur();
33849             this.removeClass("x-tree-selected");
33850         }
33851     },*/
33852     addClass : function(cls){
33853         if(this.elRow){
33854             Roo.fly(this.elRow).addClass(cls);
33855         }
33856         
33857     },
33858     
33859     
33860     removeClass : function(cls){
33861         if(this.elRow){
33862             Roo.fly(this.elRow).removeClass(cls);
33863         }
33864     }
33865
33866     
33867     
33868 });//<Script type="text/javascript">
33869
33870 /*
33871  * Based on:
33872  * Ext JS Library 1.1.1
33873  * Copyright(c) 2006-2007, Ext JS, LLC.
33874  *
33875  * Originally Released Under LGPL - original licence link has changed is not relivant.
33876  *
33877  * Fork - LGPL
33878  * <script type="text/javascript">
33879  */
33880  
33881
33882 /**
33883  * @class Roo.tree.ColumnTree
33884  * @extends Roo.data.TreePanel
33885  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33886  * @cfg {int} borderWidth  compined right/left border allowance
33887  * @constructor
33888  * @param {String/HTMLElement/Element} el The container element
33889  * @param {Object} config
33890  */
33891 Roo.tree.ColumnTree =  function(el, config)
33892 {
33893    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33894    this.addEvents({
33895         /**
33896         * @event resize
33897         * Fire this event on a container when it resizes
33898         * @param {int} w Width
33899         * @param {int} h Height
33900         */
33901        "resize" : true
33902     });
33903     this.on('resize', this.onResize, this);
33904 };
33905
33906 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33907     //lines:false,
33908     
33909     
33910     borderWidth: Roo.isBorderBox ? 0 : 2, 
33911     headEls : false,
33912     
33913     render : function(){
33914         // add the header.....
33915        
33916         Roo.tree.ColumnTree.superclass.render.apply(this);
33917         
33918         this.el.addClass('x-column-tree');
33919         
33920         this.headers = this.el.createChild(
33921             {cls:'x-tree-headers'},this.innerCt.dom);
33922    
33923         var cols = this.columns, c;
33924         var totalWidth = 0;
33925         this.headEls = [];
33926         var  len = cols.length;
33927         for(var i = 0; i < len; i++){
33928              c = cols[i];
33929              totalWidth += c.width;
33930             this.headEls.push(this.headers.createChild({
33931                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33932                  cn: {
33933                      cls:'x-tree-hd-text',
33934                      html: c.header
33935                  },
33936                  style:'width:'+(c.width-this.borderWidth)+'px;'
33937              }));
33938         }
33939         this.headers.createChild({cls:'x-clear'});
33940         // prevent floats from wrapping when clipped
33941         this.headers.setWidth(totalWidth);
33942         //this.innerCt.setWidth(totalWidth);
33943         this.innerCt.setStyle({ overflow: 'auto' });
33944         this.onResize(this.width, this.height);
33945              
33946         
33947     },
33948     onResize : function(w,h)
33949     {
33950         this.height = h;
33951         this.width = w;
33952         // resize cols..
33953         this.innerCt.setWidth(this.width);
33954         this.innerCt.setHeight(this.height-20);
33955         
33956         // headers...
33957         var cols = this.columns, c;
33958         var totalWidth = 0;
33959         var expEl = false;
33960         var len = cols.length;
33961         for(var i = 0; i < len; i++){
33962             c = cols[i];
33963             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33964                 // it's the expander..
33965                 expEl  = this.headEls[i];
33966                 continue;
33967             }
33968             totalWidth += c.width;
33969             
33970         }
33971         if (expEl) {
33972             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33973         }
33974         this.headers.setWidth(w-20);
33975
33976         
33977         
33978         
33979     }
33980 });
33981 /*
33982  * Based on:
33983  * Ext JS Library 1.1.1
33984  * Copyright(c) 2006-2007, Ext JS, LLC.
33985  *
33986  * Originally Released Under LGPL - original licence link has changed is not relivant.
33987  *
33988  * Fork - LGPL
33989  * <script type="text/javascript">
33990  */
33991  
33992 /**
33993  * @class Roo.menu.Menu
33994  * @extends Roo.util.Observable
33995  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33996  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33997  * @constructor
33998  * Creates a new Menu
33999  * @param {Object} config Configuration options
34000  */
34001 Roo.menu.Menu = function(config){
34002     Roo.apply(this, config);
34003     this.id = this.id || Roo.id();
34004     this.addEvents({
34005         /**
34006          * @event beforeshow
34007          * Fires before this menu is displayed
34008          * @param {Roo.menu.Menu} this
34009          */
34010         beforeshow : true,
34011         /**
34012          * @event beforehide
34013          * Fires before this menu is hidden
34014          * @param {Roo.menu.Menu} this
34015          */
34016         beforehide : true,
34017         /**
34018          * @event show
34019          * Fires after this menu is displayed
34020          * @param {Roo.menu.Menu} this
34021          */
34022         show : true,
34023         /**
34024          * @event hide
34025          * Fires after this menu is hidden
34026          * @param {Roo.menu.Menu} this
34027          */
34028         hide : true,
34029         /**
34030          * @event click
34031          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34032          * @param {Roo.menu.Menu} this
34033          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34034          * @param {Roo.EventObject} e
34035          */
34036         click : true,
34037         /**
34038          * @event mouseover
34039          * Fires when the mouse is hovering over this menu
34040          * @param {Roo.menu.Menu} this
34041          * @param {Roo.EventObject} e
34042          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34043          */
34044         mouseover : true,
34045         /**
34046          * @event mouseout
34047          * Fires when the mouse exits this menu
34048          * @param {Roo.menu.Menu} this
34049          * @param {Roo.EventObject} e
34050          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34051          */
34052         mouseout : true,
34053         /**
34054          * @event itemclick
34055          * Fires when a menu item contained in this menu is clicked
34056          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34057          * @param {Roo.EventObject} e
34058          */
34059         itemclick: true
34060     });
34061     if (this.registerMenu) {
34062         Roo.menu.MenuMgr.register(this);
34063     }
34064     
34065     var mis = this.items;
34066     this.items = new Roo.util.MixedCollection();
34067     if(mis){
34068         this.add.apply(this, mis);
34069     }
34070 };
34071
34072 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34073     /**
34074      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34075      */
34076     minWidth : 120,
34077     /**
34078      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34079      * for bottom-right shadow (defaults to "sides")
34080      */
34081     shadow : "sides",
34082     /**
34083      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34084      * this menu (defaults to "tl-tr?")
34085      */
34086     subMenuAlign : "tl-tr?",
34087     /**
34088      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34089      * relative to its element of origin (defaults to "tl-bl?")
34090      */
34091     defaultAlign : "tl-bl?",
34092     /**
34093      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34094      */
34095     allowOtherMenus : false,
34096     /**
34097      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34098      */
34099     registerMenu : true,
34100
34101     hidden:true,
34102
34103     // private
34104     render : function(){
34105         if(this.el){
34106             return;
34107         }
34108         var el = this.el = new Roo.Layer({
34109             cls: "x-menu",
34110             shadow:this.shadow,
34111             constrain: false,
34112             parentEl: this.parentEl || document.body,
34113             zindex:15000
34114         });
34115
34116         this.keyNav = new Roo.menu.MenuNav(this);
34117
34118         if(this.plain){
34119             el.addClass("x-menu-plain");
34120         }
34121         if(this.cls){
34122             el.addClass(this.cls);
34123         }
34124         // generic focus element
34125         this.focusEl = el.createChild({
34126             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34127         });
34128         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34129         ul.on("click", this.onClick, this);
34130         ul.on("mouseover", this.onMouseOver, this);
34131         ul.on("mouseout", this.onMouseOut, this);
34132         this.items.each(function(item){
34133             var li = document.createElement("li");
34134             li.className = "x-menu-list-item";
34135             ul.dom.appendChild(li);
34136             item.render(li, this);
34137         }, this);
34138         this.ul = ul;
34139         this.autoWidth();
34140     },
34141
34142     // private
34143     autoWidth : function(){
34144         var el = this.el, ul = this.ul;
34145         if(!el){
34146             return;
34147         }
34148         var w = this.width;
34149         if(w){
34150             el.setWidth(w);
34151         }else if(Roo.isIE){
34152             el.setWidth(this.minWidth);
34153             var t = el.dom.offsetWidth; // force recalc
34154             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34155         }
34156     },
34157
34158     // private
34159     delayAutoWidth : function(){
34160         if(this.rendered){
34161             if(!this.awTask){
34162                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34163             }
34164             this.awTask.delay(20);
34165         }
34166     },
34167
34168     // private
34169     findTargetItem : function(e){
34170         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34171         if(t && t.menuItemId){
34172             return this.items.get(t.menuItemId);
34173         }
34174     },
34175
34176     // private
34177     onClick : function(e){
34178         var t;
34179         if(t = this.findTargetItem(e)){
34180             t.onClick(e);
34181             this.fireEvent("click", this, t, e);
34182         }
34183     },
34184
34185     // private
34186     setActiveItem : function(item, autoExpand){
34187         if(item != this.activeItem){
34188             if(this.activeItem){
34189                 this.activeItem.deactivate();
34190             }
34191             this.activeItem = item;
34192             item.activate(autoExpand);
34193         }else if(autoExpand){
34194             item.expandMenu();
34195         }
34196     },
34197
34198     // private
34199     tryActivate : function(start, step){
34200         var items = this.items;
34201         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34202             var item = items.get(i);
34203             if(!item.disabled && item.canActivate){
34204                 this.setActiveItem(item, false);
34205                 return item;
34206             }
34207         }
34208         return false;
34209     },
34210
34211     // private
34212     onMouseOver : function(e){
34213         var t;
34214         if(t = this.findTargetItem(e)){
34215             if(t.canActivate && !t.disabled){
34216                 this.setActiveItem(t, true);
34217             }
34218         }
34219         this.fireEvent("mouseover", this, e, t);
34220     },
34221
34222     // private
34223     onMouseOut : function(e){
34224         var t;
34225         if(t = this.findTargetItem(e)){
34226             if(t == this.activeItem && t.shouldDeactivate(e)){
34227                 this.activeItem.deactivate();
34228                 delete this.activeItem;
34229             }
34230         }
34231         this.fireEvent("mouseout", this, e, t);
34232     },
34233
34234     /**
34235      * Read-only.  Returns true if the menu is currently displayed, else false.
34236      * @type Boolean
34237      */
34238     isVisible : function(){
34239         return this.el && !this.hidden;
34240     },
34241
34242     /**
34243      * Displays this menu relative to another element
34244      * @param {String/HTMLElement/Roo.Element} element The element to align to
34245      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34246      * the element (defaults to this.defaultAlign)
34247      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34248      */
34249     show : function(el, pos, parentMenu){
34250         this.parentMenu = parentMenu;
34251         if(!this.el){
34252             this.render();
34253         }
34254         this.fireEvent("beforeshow", this);
34255         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34256     },
34257
34258     /**
34259      * Displays this menu at a specific xy position
34260      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34261      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34262      */
34263     showAt : function(xy, parentMenu, /* private: */_e){
34264         this.parentMenu = parentMenu;
34265         if(!this.el){
34266             this.render();
34267         }
34268         if(_e !== false){
34269             this.fireEvent("beforeshow", this);
34270             xy = this.el.adjustForConstraints(xy);
34271         }
34272         this.el.setXY(xy);
34273         this.el.show();
34274         this.hidden = false;
34275         this.focus();
34276         this.fireEvent("show", this);
34277     },
34278
34279     focus : function(){
34280         if(!this.hidden){
34281             this.doFocus.defer(50, this);
34282         }
34283     },
34284
34285     doFocus : function(){
34286         if(!this.hidden){
34287             this.focusEl.focus();
34288         }
34289     },
34290
34291     /**
34292      * Hides this menu and optionally all parent menus
34293      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34294      */
34295     hide : function(deep){
34296         if(this.el && this.isVisible()){
34297             this.fireEvent("beforehide", this);
34298             if(this.activeItem){
34299                 this.activeItem.deactivate();
34300                 this.activeItem = null;
34301             }
34302             this.el.hide();
34303             this.hidden = true;
34304             this.fireEvent("hide", this);
34305         }
34306         if(deep === true && this.parentMenu){
34307             this.parentMenu.hide(true);
34308         }
34309     },
34310
34311     /**
34312      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34313      * Any of the following are valid:
34314      * <ul>
34315      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34316      * <li>An HTMLElement object which will be converted to a menu item</li>
34317      * <li>A menu item config object that will be created as a new menu item</li>
34318      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34319      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34320      * </ul>
34321      * Usage:
34322      * <pre><code>
34323 // Create the menu
34324 var menu = new Roo.menu.Menu();
34325
34326 // Create a menu item to add by reference
34327 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34328
34329 // Add a bunch of items at once using different methods.
34330 // Only the last item added will be returned.
34331 var item = menu.add(
34332     menuItem,                // add existing item by ref
34333     'Dynamic Item',          // new TextItem
34334     '-',                     // new separator
34335     { text: 'Config Item' }  // new item by config
34336 );
34337 </code></pre>
34338      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34339      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34340      */
34341     add : function(){
34342         var a = arguments, l = a.length, item;
34343         for(var i = 0; i < l; i++){
34344             var el = a[i];
34345             if ((typeof(el) == "object") && el.xtype && el.xns) {
34346                 el = Roo.factory(el, Roo.menu);
34347             }
34348             
34349             if(el.render){ // some kind of Item
34350                 item = this.addItem(el);
34351             }else if(typeof el == "string"){ // string
34352                 if(el == "separator" || el == "-"){
34353                     item = this.addSeparator();
34354                 }else{
34355                     item = this.addText(el);
34356                 }
34357             }else if(el.tagName || el.el){ // element
34358                 item = this.addElement(el);
34359             }else if(typeof el == "object"){ // must be menu item config?
34360                 item = this.addMenuItem(el);
34361             }
34362         }
34363         return item;
34364     },
34365
34366     /**
34367      * Returns this menu's underlying {@link Roo.Element} object
34368      * @return {Roo.Element} The element
34369      */
34370     getEl : function(){
34371         if(!this.el){
34372             this.render();
34373         }
34374         return this.el;
34375     },
34376
34377     /**
34378      * Adds a separator bar to the menu
34379      * @return {Roo.menu.Item} The menu item that was added
34380      */
34381     addSeparator : function(){
34382         return this.addItem(new Roo.menu.Separator());
34383     },
34384
34385     /**
34386      * Adds an {@link Roo.Element} object to the menu
34387      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34388      * @return {Roo.menu.Item} The menu item that was added
34389      */
34390     addElement : function(el){
34391         return this.addItem(new Roo.menu.BaseItem(el));
34392     },
34393
34394     /**
34395      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34396      * @param {Roo.menu.Item} item The menu item to add
34397      * @return {Roo.menu.Item} The menu item that was added
34398      */
34399     addItem : function(item){
34400         this.items.add(item);
34401         if(this.ul){
34402             var li = document.createElement("li");
34403             li.className = "x-menu-list-item";
34404             this.ul.dom.appendChild(li);
34405             item.render(li, this);
34406             this.delayAutoWidth();
34407         }
34408         return item;
34409     },
34410
34411     /**
34412      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34413      * @param {Object} config A MenuItem config object
34414      * @return {Roo.menu.Item} The menu item that was added
34415      */
34416     addMenuItem : function(config){
34417         if(!(config instanceof Roo.menu.Item)){
34418             if(typeof config.checked == "boolean"){ // must be check menu item config?
34419                 config = new Roo.menu.CheckItem(config);
34420             }else{
34421                 config = new Roo.menu.Item(config);
34422             }
34423         }
34424         return this.addItem(config);
34425     },
34426
34427     /**
34428      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34429      * @param {String} text The text to display in the menu item
34430      * @return {Roo.menu.Item} The menu item that was added
34431      */
34432     addText : function(text){
34433         return this.addItem(new Roo.menu.TextItem({ text : text }));
34434     },
34435
34436     /**
34437      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34438      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34439      * @param {Roo.menu.Item} item The menu item to add
34440      * @return {Roo.menu.Item} The menu item that was added
34441      */
34442     insert : function(index, item){
34443         this.items.insert(index, item);
34444         if(this.ul){
34445             var li = document.createElement("li");
34446             li.className = "x-menu-list-item";
34447             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34448             item.render(li, this);
34449             this.delayAutoWidth();
34450         }
34451         return item;
34452     },
34453
34454     /**
34455      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34456      * @param {Roo.menu.Item} item The menu item to remove
34457      */
34458     remove : function(item){
34459         this.items.removeKey(item.id);
34460         item.destroy();
34461     },
34462
34463     /**
34464      * Removes and destroys all items in the menu
34465      */
34466     removeAll : function(){
34467         var f;
34468         while(f = this.items.first()){
34469             this.remove(f);
34470         }
34471     }
34472 });
34473
34474 // MenuNav is a private utility class used internally by the Menu
34475 Roo.menu.MenuNav = function(menu){
34476     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34477     this.scope = this.menu = menu;
34478 };
34479
34480 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34481     doRelay : function(e, h){
34482         var k = e.getKey();
34483         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34484             this.menu.tryActivate(0, 1);
34485             return false;
34486         }
34487         return h.call(this.scope || this, e, this.menu);
34488     },
34489
34490     up : function(e, m){
34491         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34492             m.tryActivate(m.items.length-1, -1);
34493         }
34494     },
34495
34496     down : function(e, m){
34497         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34498             m.tryActivate(0, 1);
34499         }
34500     },
34501
34502     right : function(e, m){
34503         if(m.activeItem){
34504             m.activeItem.expandMenu(true);
34505         }
34506     },
34507
34508     left : function(e, m){
34509         m.hide();
34510         if(m.parentMenu && m.parentMenu.activeItem){
34511             m.parentMenu.activeItem.activate();
34512         }
34513     },
34514
34515     enter : function(e, m){
34516         if(m.activeItem){
34517             e.stopPropagation();
34518             m.activeItem.onClick(e);
34519             m.fireEvent("click", this, m.activeItem);
34520             return true;
34521         }
34522     }
34523 });/*
34524  * Based on:
34525  * Ext JS Library 1.1.1
34526  * Copyright(c) 2006-2007, Ext JS, LLC.
34527  *
34528  * Originally Released Under LGPL - original licence link has changed is not relivant.
34529  *
34530  * Fork - LGPL
34531  * <script type="text/javascript">
34532  */
34533  
34534 /**
34535  * @class Roo.menu.MenuMgr
34536  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34537  * @singleton
34538  */
34539 Roo.menu.MenuMgr = function(){
34540    var menus, active, groups = {}, attached = false, lastShow = new Date();
34541
34542    // private - called when first menu is created
34543    function init(){
34544        menus = {};
34545        active = new Roo.util.MixedCollection();
34546        Roo.get(document).addKeyListener(27, function(){
34547            if(active.length > 0){
34548                hideAll();
34549            }
34550        });
34551    }
34552
34553    // private
34554    function hideAll(){
34555        if(active && active.length > 0){
34556            var c = active.clone();
34557            c.each(function(m){
34558                m.hide();
34559            });
34560        }
34561    }
34562
34563    // private
34564    function onHide(m){
34565        active.remove(m);
34566        if(active.length < 1){
34567            Roo.get(document).un("mousedown", onMouseDown);
34568            attached = false;
34569        }
34570    }
34571
34572    // private
34573    function onShow(m){
34574        var last = active.last();
34575        lastShow = new Date();
34576        active.add(m);
34577        if(!attached){
34578            Roo.get(document).on("mousedown", onMouseDown);
34579            attached = true;
34580        }
34581        if(m.parentMenu){
34582           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34583           m.parentMenu.activeChild = m;
34584        }else if(last && last.isVisible()){
34585           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34586        }
34587    }
34588
34589    // private
34590    function onBeforeHide(m){
34591        if(m.activeChild){
34592            m.activeChild.hide();
34593        }
34594        if(m.autoHideTimer){
34595            clearTimeout(m.autoHideTimer);
34596            delete m.autoHideTimer;
34597        }
34598    }
34599
34600    // private
34601    function onBeforeShow(m){
34602        var pm = m.parentMenu;
34603        if(!pm && !m.allowOtherMenus){
34604            hideAll();
34605        }else if(pm && pm.activeChild && active != m){
34606            pm.activeChild.hide();
34607        }
34608    }
34609
34610    // private
34611    function onMouseDown(e){
34612        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34613            hideAll();
34614        }
34615    }
34616
34617    // private
34618    function onBeforeCheck(mi, state){
34619        if(state){
34620            var g = groups[mi.group];
34621            for(var i = 0, l = g.length; i < l; i++){
34622                if(g[i] != mi){
34623                    g[i].setChecked(false);
34624                }
34625            }
34626        }
34627    }
34628
34629    return {
34630
34631        /**
34632         * Hides all menus that are currently visible
34633         */
34634        hideAll : function(){
34635             hideAll();  
34636        },
34637
34638        // private
34639        register : function(menu){
34640            if(!menus){
34641                init();
34642            }
34643            menus[menu.id] = menu;
34644            menu.on("beforehide", onBeforeHide);
34645            menu.on("hide", onHide);
34646            menu.on("beforeshow", onBeforeShow);
34647            menu.on("show", onShow);
34648            var g = menu.group;
34649            if(g && menu.events["checkchange"]){
34650                if(!groups[g]){
34651                    groups[g] = [];
34652                }
34653                groups[g].push(menu);
34654                menu.on("checkchange", onCheck);
34655            }
34656        },
34657
34658         /**
34659          * Returns a {@link Roo.menu.Menu} object
34660          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34661          * be used to generate and return a new Menu instance.
34662          */
34663        get : function(menu){
34664            if(typeof menu == "string"){ // menu id
34665                return menus[menu];
34666            }else if(menu.events){  // menu instance
34667                return menu;
34668            }else if(typeof menu.length == 'number'){ // array of menu items?
34669                return new Roo.menu.Menu({items:menu});
34670            }else{ // otherwise, must be a config
34671                return new Roo.menu.Menu(menu);
34672            }
34673        },
34674
34675        // private
34676        unregister : function(menu){
34677            delete menus[menu.id];
34678            menu.un("beforehide", onBeforeHide);
34679            menu.un("hide", onHide);
34680            menu.un("beforeshow", onBeforeShow);
34681            menu.un("show", onShow);
34682            var g = menu.group;
34683            if(g && menu.events["checkchange"]){
34684                groups[g].remove(menu);
34685                menu.un("checkchange", onCheck);
34686            }
34687        },
34688
34689        // private
34690        registerCheckable : function(menuItem){
34691            var g = menuItem.group;
34692            if(g){
34693                if(!groups[g]){
34694                    groups[g] = [];
34695                }
34696                groups[g].push(menuItem);
34697                menuItem.on("beforecheckchange", onBeforeCheck);
34698            }
34699        },
34700
34701        // private
34702        unregisterCheckable : function(menuItem){
34703            var g = menuItem.group;
34704            if(g){
34705                groups[g].remove(menuItem);
34706                menuItem.un("beforecheckchange", onBeforeCheck);
34707            }
34708        }
34709    };
34710 }();/*
34711  * Based on:
34712  * Ext JS Library 1.1.1
34713  * Copyright(c) 2006-2007, Ext JS, LLC.
34714  *
34715  * Originally Released Under LGPL - original licence link has changed is not relivant.
34716  *
34717  * Fork - LGPL
34718  * <script type="text/javascript">
34719  */
34720  
34721
34722 /**
34723  * @class Roo.menu.BaseItem
34724  * @extends Roo.Component
34725  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34726  * management and base configuration options shared by all menu components.
34727  * @constructor
34728  * Creates a new BaseItem
34729  * @param {Object} config Configuration options
34730  */
34731 Roo.menu.BaseItem = function(config){
34732     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34733
34734     this.addEvents({
34735         /**
34736          * @event click
34737          * Fires when this item is clicked
34738          * @param {Roo.menu.BaseItem} this
34739          * @param {Roo.EventObject} e
34740          */
34741         click: true,
34742         /**
34743          * @event activate
34744          * Fires when this item is activated
34745          * @param {Roo.menu.BaseItem} this
34746          */
34747         activate : true,
34748         /**
34749          * @event deactivate
34750          * Fires when this item is deactivated
34751          * @param {Roo.menu.BaseItem} this
34752          */
34753         deactivate : true
34754     });
34755
34756     if(this.handler){
34757         this.on("click", this.handler, this.scope, true);
34758     }
34759 };
34760
34761 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34762     /**
34763      * @cfg {Function} handler
34764      * A function that will handle the click event of this menu item (defaults to undefined)
34765      */
34766     /**
34767      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34768      */
34769     canActivate : false,
34770     /**
34771      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34772      */
34773     activeClass : "x-menu-item-active",
34774     /**
34775      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34776      */
34777     hideOnClick : true,
34778     /**
34779      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34780      */
34781     hideDelay : 100,
34782
34783     // private
34784     ctype: "Roo.menu.BaseItem",
34785
34786     // private
34787     actionMode : "container",
34788
34789     // private
34790     render : function(container, parentMenu){
34791         this.parentMenu = parentMenu;
34792         Roo.menu.BaseItem.superclass.render.call(this, container);
34793         this.container.menuItemId = this.id;
34794     },
34795
34796     // private
34797     onRender : function(container, position){
34798         this.el = Roo.get(this.el);
34799         container.dom.appendChild(this.el.dom);
34800     },
34801
34802     // private
34803     onClick : function(e){
34804         if(!this.disabled && this.fireEvent("click", this, e) !== false
34805                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34806             this.handleClick(e);
34807         }else{
34808             e.stopEvent();
34809         }
34810     },
34811
34812     // private
34813     activate : function(){
34814         if(this.disabled){
34815             return false;
34816         }
34817         var li = this.container;
34818         li.addClass(this.activeClass);
34819         this.region = li.getRegion().adjust(2, 2, -2, -2);
34820         this.fireEvent("activate", this);
34821         return true;
34822     },
34823
34824     // private
34825     deactivate : function(){
34826         this.container.removeClass(this.activeClass);
34827         this.fireEvent("deactivate", this);
34828     },
34829
34830     // private
34831     shouldDeactivate : function(e){
34832         return !this.region || !this.region.contains(e.getPoint());
34833     },
34834
34835     // private
34836     handleClick : function(e){
34837         if(this.hideOnClick){
34838             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34839         }
34840     },
34841
34842     // private
34843     expandMenu : function(autoActivate){
34844         // do nothing
34845     },
34846
34847     // private
34848     hideMenu : function(){
34849         // do nothing
34850     }
34851 });/*
34852  * Based on:
34853  * Ext JS Library 1.1.1
34854  * Copyright(c) 2006-2007, Ext JS, LLC.
34855  *
34856  * Originally Released Under LGPL - original licence link has changed is not relivant.
34857  *
34858  * Fork - LGPL
34859  * <script type="text/javascript">
34860  */
34861  
34862 /**
34863  * @class Roo.menu.Adapter
34864  * @extends Roo.menu.BaseItem
34865  * 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.
34866  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34867  * @constructor
34868  * Creates a new Adapter
34869  * @param {Object} config Configuration options
34870  */
34871 Roo.menu.Adapter = function(component, config){
34872     Roo.menu.Adapter.superclass.constructor.call(this, config);
34873     this.component = component;
34874 };
34875 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34876     // private
34877     canActivate : true,
34878
34879     // private
34880     onRender : function(container, position){
34881         this.component.render(container);
34882         this.el = this.component.getEl();
34883     },
34884
34885     // private
34886     activate : function(){
34887         if(this.disabled){
34888             return false;
34889         }
34890         this.component.focus();
34891         this.fireEvent("activate", this);
34892         return true;
34893     },
34894
34895     // private
34896     deactivate : function(){
34897         this.fireEvent("deactivate", this);
34898     },
34899
34900     // private
34901     disable : function(){
34902         this.component.disable();
34903         Roo.menu.Adapter.superclass.disable.call(this);
34904     },
34905
34906     // private
34907     enable : function(){
34908         this.component.enable();
34909         Roo.menu.Adapter.superclass.enable.call(this);
34910     }
34911 });/*
34912  * Based on:
34913  * Ext JS Library 1.1.1
34914  * Copyright(c) 2006-2007, Ext JS, LLC.
34915  *
34916  * Originally Released Under LGPL - original licence link has changed is not relivant.
34917  *
34918  * Fork - LGPL
34919  * <script type="text/javascript">
34920  */
34921
34922 /**
34923  * @class Roo.menu.TextItem
34924  * @extends Roo.menu.BaseItem
34925  * Adds a static text string to a menu, usually used as either a heading or group separator.
34926  * Note: old style constructor with text is still supported.
34927  * 
34928  * @constructor
34929  * Creates a new TextItem
34930  * @param {Object} cfg Configuration
34931  */
34932 Roo.menu.TextItem = function(cfg){
34933     if (typeof(cfg) == 'string') {
34934         this.text = cfg;
34935     } else {
34936         Roo.apply(this,cfg);
34937     }
34938     
34939     Roo.menu.TextItem.superclass.constructor.call(this);
34940 };
34941
34942 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34943     /**
34944      * @cfg {Boolean} text Text to show on item.
34945      */
34946     text : '',
34947     
34948     /**
34949      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34950      */
34951     hideOnClick : false,
34952     /**
34953      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34954      */
34955     itemCls : "x-menu-text",
34956
34957     // private
34958     onRender : function(){
34959         var s = document.createElement("span");
34960         s.className = this.itemCls;
34961         s.innerHTML = this.text;
34962         this.el = s;
34963         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34964     }
34965 });/*
34966  * Based on:
34967  * Ext JS Library 1.1.1
34968  * Copyright(c) 2006-2007, Ext JS, LLC.
34969  *
34970  * Originally Released Under LGPL - original licence link has changed is not relivant.
34971  *
34972  * Fork - LGPL
34973  * <script type="text/javascript">
34974  */
34975
34976 /**
34977  * @class Roo.menu.Separator
34978  * @extends Roo.menu.BaseItem
34979  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34980  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34981  * @constructor
34982  * @param {Object} config Configuration options
34983  */
34984 Roo.menu.Separator = function(config){
34985     Roo.menu.Separator.superclass.constructor.call(this, config);
34986 };
34987
34988 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34989     /**
34990      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34991      */
34992     itemCls : "x-menu-sep",
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     // private
34999     onRender : function(li){
35000         var s = document.createElement("span");
35001         s.className = this.itemCls;
35002         s.innerHTML = "&#160;";
35003         this.el = s;
35004         li.addClass("x-menu-sep-li");
35005         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35006     }
35007 });/*
35008  * Based on:
35009  * Ext JS Library 1.1.1
35010  * Copyright(c) 2006-2007, Ext JS, LLC.
35011  *
35012  * Originally Released Under LGPL - original licence link has changed is not relivant.
35013  *
35014  * Fork - LGPL
35015  * <script type="text/javascript">
35016  */
35017 /**
35018  * @class Roo.menu.Item
35019  * @extends Roo.menu.BaseItem
35020  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35021  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35022  * activation and click handling.
35023  * @constructor
35024  * Creates a new Item
35025  * @param {Object} config Configuration options
35026  */
35027 Roo.menu.Item = function(config){
35028     Roo.menu.Item.superclass.constructor.call(this, config);
35029     if(this.menu){
35030         this.menu = Roo.menu.MenuMgr.get(this.menu);
35031     }
35032 };
35033 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35034     
35035     /**
35036      * @cfg {String} text
35037      * The text to show on the menu item.
35038      */
35039     text: '',
35040      /**
35041      * @cfg {String} HTML to render in menu
35042      * The text to show on the menu item (HTML version).
35043      */
35044     html: '',
35045     /**
35046      * @cfg {String} icon
35047      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35048      */
35049     icon: undefined,
35050     /**
35051      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35052      */
35053     itemCls : "x-menu-item",
35054     /**
35055      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35056      */
35057     canActivate : true,
35058     /**
35059      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35060      */
35061     showDelay: 200,
35062     // doc'd in BaseItem
35063     hideDelay: 200,
35064
35065     // private
35066     ctype: "Roo.menu.Item",
35067     
35068     // private
35069     onRender : function(container, position){
35070         var el = document.createElement("a");
35071         el.hideFocus = true;
35072         el.unselectable = "on";
35073         el.href = this.href || "#";
35074         if(this.hrefTarget){
35075             el.target = this.hrefTarget;
35076         }
35077         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35078         
35079         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35080         
35081         el.innerHTML = String.format(
35082                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35083                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35084         this.el = el;
35085         Roo.menu.Item.superclass.onRender.call(this, container, position);
35086     },
35087
35088     /**
35089      * Sets the text to display in this menu item
35090      * @param {String} text The text to display
35091      * @param {Boolean} isHTML true to indicate text is pure html.
35092      */
35093     setText : function(text, isHTML){
35094         if (isHTML) {
35095             this.html = text;
35096         } else {
35097             this.text = text;
35098             this.html = '';
35099         }
35100         if(this.rendered){
35101             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35102      
35103             this.el.update(String.format(
35104                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35105                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35106             this.parentMenu.autoWidth();
35107         }
35108     },
35109
35110     // private
35111     handleClick : function(e){
35112         if(!this.href){ // if no link defined, stop the event automatically
35113             e.stopEvent();
35114         }
35115         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35116     },
35117
35118     // private
35119     activate : function(autoExpand){
35120         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35121             this.focus();
35122             if(autoExpand){
35123                 this.expandMenu();
35124             }
35125         }
35126         return true;
35127     },
35128
35129     // private
35130     shouldDeactivate : function(e){
35131         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35132             if(this.menu && this.menu.isVisible()){
35133                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35134             }
35135             return true;
35136         }
35137         return false;
35138     },
35139
35140     // private
35141     deactivate : function(){
35142         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35143         this.hideMenu();
35144     },
35145
35146     // private
35147     expandMenu : function(autoActivate){
35148         if(!this.disabled && this.menu){
35149             clearTimeout(this.hideTimer);
35150             delete this.hideTimer;
35151             if(!this.menu.isVisible() && !this.showTimer){
35152                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35153             }else if (this.menu.isVisible() && autoActivate){
35154                 this.menu.tryActivate(0, 1);
35155             }
35156         }
35157     },
35158
35159     // private
35160     deferExpand : function(autoActivate){
35161         delete this.showTimer;
35162         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35163         if(autoActivate){
35164             this.menu.tryActivate(0, 1);
35165         }
35166     },
35167
35168     // private
35169     hideMenu : function(){
35170         clearTimeout(this.showTimer);
35171         delete this.showTimer;
35172         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35173             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35174         }
35175     },
35176
35177     // private
35178     deferHide : function(){
35179         delete this.hideTimer;
35180         this.menu.hide();
35181     }
35182 });/*
35183  * Based on:
35184  * Ext JS Library 1.1.1
35185  * Copyright(c) 2006-2007, Ext JS, LLC.
35186  *
35187  * Originally Released Under LGPL - original licence link has changed is not relivant.
35188  *
35189  * Fork - LGPL
35190  * <script type="text/javascript">
35191  */
35192  
35193 /**
35194  * @class Roo.menu.CheckItem
35195  * @extends Roo.menu.Item
35196  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35197  * @constructor
35198  * Creates a new CheckItem
35199  * @param {Object} config Configuration options
35200  */
35201 Roo.menu.CheckItem = function(config){
35202     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35203     this.addEvents({
35204         /**
35205          * @event beforecheckchange
35206          * Fires before the checked value is set, providing an opportunity to cancel if needed
35207          * @param {Roo.menu.CheckItem} this
35208          * @param {Boolean} checked The new checked value that will be set
35209          */
35210         "beforecheckchange" : true,
35211         /**
35212          * @event checkchange
35213          * Fires after the checked value has been set
35214          * @param {Roo.menu.CheckItem} this
35215          * @param {Boolean} checked The checked value that was set
35216          */
35217         "checkchange" : true
35218     });
35219     if(this.checkHandler){
35220         this.on('checkchange', this.checkHandler, this.scope);
35221     }
35222 };
35223 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35224     /**
35225      * @cfg {String} group
35226      * All check items with the same group name will automatically be grouped into a single-select
35227      * radio button group (defaults to '')
35228      */
35229     /**
35230      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35231      */
35232     itemCls : "x-menu-item x-menu-check-item",
35233     /**
35234      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35235      */
35236     groupClass : "x-menu-group-item",
35237
35238     /**
35239      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35240      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35241      * initialized with checked = true will be rendered as checked.
35242      */
35243     checked: false,
35244
35245     // private
35246     ctype: "Roo.menu.CheckItem",
35247
35248     // private
35249     onRender : function(c){
35250         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35251         if(this.group){
35252             this.el.addClass(this.groupClass);
35253         }
35254         Roo.menu.MenuMgr.registerCheckable(this);
35255         if(this.checked){
35256             this.checked = false;
35257             this.setChecked(true, true);
35258         }
35259     },
35260
35261     // private
35262     destroy : function(){
35263         if(this.rendered){
35264             Roo.menu.MenuMgr.unregisterCheckable(this);
35265         }
35266         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35267     },
35268
35269     /**
35270      * Set the checked state of this item
35271      * @param {Boolean} checked The new checked value
35272      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35273      */
35274     setChecked : function(state, suppressEvent){
35275         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35276             if(this.container){
35277                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35278             }
35279             this.checked = state;
35280             if(suppressEvent !== true){
35281                 this.fireEvent("checkchange", this, state);
35282             }
35283         }
35284     },
35285
35286     // private
35287     handleClick : function(e){
35288        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35289            this.setChecked(!this.checked);
35290        }
35291        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35292     }
35293 });/*
35294  * Based on:
35295  * Ext JS Library 1.1.1
35296  * Copyright(c) 2006-2007, Ext JS, LLC.
35297  *
35298  * Originally Released Under LGPL - original licence link has changed is not relivant.
35299  *
35300  * Fork - LGPL
35301  * <script type="text/javascript">
35302  */
35303  
35304 /**
35305  * @class Roo.menu.DateItem
35306  * @extends Roo.menu.Adapter
35307  * A menu item that wraps the {@link Roo.DatPicker} component.
35308  * @constructor
35309  * Creates a new DateItem
35310  * @param {Object} config Configuration options
35311  */
35312 Roo.menu.DateItem = function(config){
35313     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35314     /** The Roo.DatePicker object @type Roo.DatePicker */
35315     this.picker = this.component;
35316     this.addEvents({select: true});
35317     
35318     this.picker.on("render", function(picker){
35319         picker.getEl().swallowEvent("click");
35320         picker.container.addClass("x-menu-date-item");
35321     });
35322
35323     this.picker.on("select", this.onSelect, this);
35324 };
35325
35326 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35327     // private
35328     onSelect : function(picker, date){
35329         this.fireEvent("select", this, date, picker);
35330         Roo.menu.DateItem.superclass.handleClick.call(this);
35331     }
35332 });/*
35333  * Based on:
35334  * Ext JS Library 1.1.1
35335  * Copyright(c) 2006-2007, Ext JS, LLC.
35336  *
35337  * Originally Released Under LGPL - original licence link has changed is not relivant.
35338  *
35339  * Fork - LGPL
35340  * <script type="text/javascript">
35341  */
35342  
35343 /**
35344  * @class Roo.menu.ColorItem
35345  * @extends Roo.menu.Adapter
35346  * A menu item that wraps the {@link Roo.ColorPalette} component.
35347  * @constructor
35348  * Creates a new ColorItem
35349  * @param {Object} config Configuration options
35350  */
35351 Roo.menu.ColorItem = function(config){
35352     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35353     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35354     this.palette = this.component;
35355     this.relayEvents(this.palette, ["select"]);
35356     if(this.selectHandler){
35357         this.on('select', this.selectHandler, this.scope);
35358     }
35359 };
35360 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35361  * Based on:
35362  * Ext JS Library 1.1.1
35363  * Copyright(c) 2006-2007, Ext JS, LLC.
35364  *
35365  * Originally Released Under LGPL - original licence link has changed is not relivant.
35366  *
35367  * Fork - LGPL
35368  * <script type="text/javascript">
35369  */
35370  
35371
35372 /**
35373  * @class Roo.menu.DateMenu
35374  * @extends Roo.menu.Menu
35375  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35376  * @constructor
35377  * Creates a new DateMenu
35378  * @param {Object} config Configuration options
35379  */
35380 Roo.menu.DateMenu = function(config){
35381     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35382     this.plain = true;
35383     var di = new Roo.menu.DateItem(config);
35384     this.add(di);
35385     /**
35386      * The {@link Roo.DatePicker} instance for this DateMenu
35387      * @type DatePicker
35388      */
35389     this.picker = di.picker;
35390     /**
35391      * @event select
35392      * @param {DatePicker} picker
35393      * @param {Date} date
35394      */
35395     this.relayEvents(di, ["select"]);
35396
35397     this.on('beforeshow', function(){
35398         if(this.picker){
35399             this.picker.hideMonthPicker(true);
35400         }
35401     }, this);
35402 };
35403 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35404     cls:'x-date-menu'
35405 });/*
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.ColorMenu
35419  * @extends Roo.menu.Menu
35420  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35421  * @constructor
35422  * Creates a new ColorMenu
35423  * @param {Object} config Configuration options
35424  */
35425 Roo.menu.ColorMenu = function(config){
35426     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35427     this.plain = true;
35428     var ci = new Roo.menu.ColorItem(config);
35429     this.add(ci);
35430     /**
35431      * The {@link Roo.ColorPalette} instance for this ColorMenu
35432      * @type ColorPalette
35433      */
35434     this.palette = ci.palette;
35435     /**
35436      * @event select
35437      * @param {ColorPalette} palette
35438      * @param {String} color
35439      */
35440     this.relayEvents(ci, ["select"]);
35441 };
35442 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35443  * Based on:
35444  * Ext JS Library 1.1.1
35445  * Copyright(c) 2006-2007, Ext JS, LLC.
35446  *
35447  * Originally Released Under LGPL - original licence link has changed is not relivant.
35448  *
35449  * Fork - LGPL
35450  * <script type="text/javascript">
35451  */
35452  
35453 /**
35454  * @class Roo.form.Field
35455  * @extends Roo.BoxComponent
35456  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35457  * @constructor
35458  * Creates a new Field
35459  * @param {Object} config Configuration options
35460  */
35461 Roo.form.Field = function(config){
35462     Roo.form.Field.superclass.constructor.call(this, config);
35463 };
35464
35465 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35466     /**
35467      * @cfg {String} fieldLabel Label to use when rendering a form.
35468      */
35469        /**
35470      * @cfg {String} qtip Mouse over tip
35471      */
35472      
35473     /**
35474      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35475      */
35476     invalidClass : "x-form-invalid",
35477     /**
35478      * @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")
35479      */
35480     invalidText : "The value in this field is invalid",
35481     /**
35482      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35483      */
35484     focusClass : "x-form-focus",
35485     /**
35486      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35487       automatic validation (defaults to "keyup").
35488      */
35489     validationEvent : "keyup",
35490     /**
35491      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35492      */
35493     validateOnBlur : true,
35494     /**
35495      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35496      */
35497     validationDelay : 250,
35498     /**
35499      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35500      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35501      */
35502     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35503     /**
35504      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35505      */
35506     fieldClass : "x-form-field",
35507     /**
35508      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35509      *<pre>
35510 Value         Description
35511 -----------   ----------------------------------------------------------------------
35512 qtip          Display a quick tip when the user hovers over the field
35513 title         Display a default browser title attribute popup
35514 under         Add a block div beneath the field containing the error text
35515 side          Add an error icon to the right of the field with a popup on hover
35516 [element id]  Add the error text directly to the innerHTML of the specified element
35517 </pre>
35518      */
35519     msgTarget : 'qtip',
35520     /**
35521      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35522      */
35523     msgFx : 'normal',
35524
35525     /**
35526      * @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.
35527      */
35528     readOnly : false,
35529
35530     /**
35531      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35532      */
35533     disabled : false,
35534
35535     /**
35536      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35537      */
35538     inputType : undefined,
35539     
35540     /**
35541      * @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).
35542          */
35543         tabIndex : undefined,
35544         
35545     // private
35546     isFormField : true,
35547
35548     // private
35549     hasFocus : false,
35550     /**
35551      * @property {Roo.Element} fieldEl
35552      * Element Containing the rendered Field (with label etc.)
35553      */
35554     /**
35555      * @cfg {Mixed} value A value to initialize this field with.
35556      */
35557     value : undefined,
35558
35559     /**
35560      * @cfg {String} name The field's HTML name attribute.
35561      */
35562     /**
35563      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35564      */
35565
35566         // private ??
35567         initComponent : function(){
35568         Roo.form.Field.superclass.initComponent.call(this);
35569         this.addEvents({
35570             /**
35571              * @event focus
35572              * Fires when this field receives input focus.
35573              * @param {Roo.form.Field} this
35574              */
35575             focus : true,
35576             /**
35577              * @event blur
35578              * Fires when this field loses input focus.
35579              * @param {Roo.form.Field} this
35580              */
35581             blur : true,
35582             /**
35583              * @event specialkey
35584              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35585              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35586              * @param {Roo.form.Field} this
35587              * @param {Roo.EventObject} e The event object
35588              */
35589             specialkey : true,
35590             /**
35591              * @event change
35592              * Fires just before the field blurs if the field value has changed.
35593              * @param {Roo.form.Field} this
35594              * @param {Mixed} newValue The new value
35595              * @param {Mixed} oldValue The original value
35596              */
35597             change : true,
35598             /**
35599              * @event invalid
35600              * Fires after the field has been marked as invalid.
35601              * @param {Roo.form.Field} this
35602              * @param {String} msg The validation message
35603              */
35604             invalid : true,
35605             /**
35606              * @event valid
35607              * Fires after the field has been validated with no errors.
35608              * @param {Roo.form.Field} this
35609              */
35610             valid : true,
35611              /**
35612              * @event keyup
35613              * Fires after the key up
35614              * @param {Roo.form.Field} this
35615              * @param {Roo.EventObject}  e The event Object
35616              */
35617             keyup : true
35618         });
35619     },
35620
35621     /**
35622      * Returns the name attribute of the field if available
35623      * @return {String} name The field name
35624      */
35625     getName: function(){
35626          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35627     },
35628
35629     // private
35630     onRender : function(ct, position){
35631         Roo.form.Field.superclass.onRender.call(this, ct, position);
35632         if(!this.el){
35633             var cfg = this.getAutoCreate();
35634             if(!cfg.name){
35635                 cfg.name = this.name || this.id;
35636             }
35637             if(this.inputType){
35638                 cfg.type = this.inputType;
35639             }
35640             this.el = ct.createChild(cfg, position);
35641         }
35642         var type = this.el.dom.type;
35643         if(type){
35644             if(type == 'password'){
35645                 type = 'text';
35646             }
35647             this.el.addClass('x-form-'+type);
35648         }
35649         if(this.readOnly){
35650             this.el.dom.readOnly = true;
35651         }
35652         if(this.tabIndex !== undefined){
35653             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35654         }
35655
35656         this.el.addClass([this.fieldClass, this.cls]);
35657         this.initValue();
35658     },
35659
35660     /**
35661      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35662      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35663      * @return {Roo.form.Field} this
35664      */
35665     applyTo : function(target){
35666         this.allowDomMove = false;
35667         this.el = Roo.get(target);
35668         this.render(this.el.dom.parentNode);
35669         return this;
35670     },
35671
35672     // private
35673     initValue : function(){
35674         if(this.value !== undefined){
35675             this.setValue(this.value);
35676         }else if(this.el.dom.value.length > 0){
35677             this.setValue(this.el.dom.value);
35678         }
35679     },
35680
35681     /**
35682      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35683      */
35684     isDirty : function() {
35685         if(this.disabled) {
35686             return false;
35687         }
35688         return String(this.getValue()) !== String(this.originalValue);
35689     },
35690
35691     // private
35692     afterRender : function(){
35693         Roo.form.Field.superclass.afterRender.call(this);
35694         this.initEvents();
35695     },
35696
35697     // private
35698     fireKey : function(e){
35699         //Roo.log('field ' + e.getKey());
35700         if(e.isNavKeyPress()){
35701             this.fireEvent("specialkey", this, e);
35702         }
35703     },
35704
35705     /**
35706      * Resets the current field value to the originally loaded value and clears any validation messages
35707      */
35708     reset : function(){
35709         this.setValue(this.originalValue);
35710         this.clearInvalid();
35711     },
35712
35713     // private
35714     initEvents : function(){
35715         // safari killled keypress - so keydown is now used..
35716         this.el.on("keydown" , this.fireKey,  this);
35717         this.el.on("focus", this.onFocus,  this);
35718         this.el.on("blur", this.onBlur,  this);
35719         this.el.relayEvent('keyup', this);
35720
35721         // reference to original value for reset
35722         this.originalValue = this.getValue();
35723     },
35724
35725     // private
35726     onFocus : function(){
35727         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35728             this.el.addClass(this.focusClass);
35729         }
35730         if(!this.hasFocus){
35731             this.hasFocus = true;
35732             this.startValue = this.getValue();
35733             this.fireEvent("focus", this);
35734         }
35735     },
35736
35737     beforeBlur : Roo.emptyFn,
35738
35739     // private
35740     onBlur : function(){
35741         this.beforeBlur();
35742         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35743             this.el.removeClass(this.focusClass);
35744         }
35745         this.hasFocus = false;
35746         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35747             this.validate();
35748         }
35749         var v = this.getValue();
35750         if(String(v) !== String(this.startValue)){
35751             this.fireEvent('change', this, v, this.startValue);
35752         }
35753         this.fireEvent("blur", this);
35754     },
35755
35756     /**
35757      * Returns whether or not the field value is currently valid
35758      * @param {Boolean} preventMark True to disable marking the field invalid
35759      * @return {Boolean} True if the value is valid, else false
35760      */
35761     isValid : function(preventMark){
35762         if(this.disabled){
35763             return true;
35764         }
35765         var restore = this.preventMark;
35766         this.preventMark = preventMark === true;
35767         var v = this.validateValue(this.processValue(this.getRawValue()));
35768         this.preventMark = restore;
35769         return v;
35770     },
35771
35772     /**
35773      * Validates the field value
35774      * @return {Boolean} True if the value is valid, else false
35775      */
35776     validate : function(){
35777         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35778             this.clearInvalid();
35779             return true;
35780         }
35781         return false;
35782     },
35783
35784     processValue : function(value){
35785         return value;
35786     },
35787
35788     // private
35789     // Subclasses should provide the validation implementation by overriding this
35790     validateValue : function(value){
35791         return true;
35792     },
35793
35794     /**
35795      * Mark this field as invalid
35796      * @param {String} msg The validation message
35797      */
35798     markInvalid : function(msg){
35799         if(!this.rendered || this.preventMark){ // not rendered
35800             return;
35801         }
35802         this.el.addClass(this.invalidClass);
35803         msg = msg || this.invalidText;
35804         switch(this.msgTarget){
35805             case 'qtip':
35806                 this.el.dom.qtip = msg;
35807                 this.el.dom.qclass = 'x-form-invalid-tip';
35808                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35809                     Roo.QuickTips.enable();
35810                 }
35811                 break;
35812             case 'title':
35813                 this.el.dom.title = msg;
35814                 break;
35815             case 'under':
35816                 if(!this.errorEl){
35817                     var elp = this.el.findParent('.x-form-element', 5, true);
35818                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35819                     this.errorEl.setWidth(elp.getWidth(true)-20);
35820                 }
35821                 this.errorEl.update(msg);
35822                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35823                 break;
35824             case 'side':
35825                 if(!this.errorIcon){
35826                     var elp = this.el.findParent('.x-form-element', 5, true);
35827                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35828                 }
35829                 this.alignErrorIcon();
35830                 this.errorIcon.dom.qtip = msg;
35831                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35832                 this.errorIcon.show();
35833                 this.on('resize', this.alignErrorIcon, this);
35834                 break;
35835             default:
35836                 var t = Roo.getDom(this.msgTarget);
35837                 t.innerHTML = msg;
35838                 t.style.display = this.msgDisplay;
35839                 break;
35840         }
35841         this.fireEvent('invalid', this, msg);
35842     },
35843
35844     // private
35845     alignErrorIcon : function(){
35846         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35847     },
35848
35849     /**
35850      * Clear any invalid styles/messages for this field
35851      */
35852     clearInvalid : function(){
35853         if(!this.rendered || this.preventMark){ // not rendered
35854             return;
35855         }
35856         this.el.removeClass(this.invalidClass);
35857         switch(this.msgTarget){
35858             case 'qtip':
35859                 this.el.dom.qtip = '';
35860                 break;
35861             case 'title':
35862                 this.el.dom.title = '';
35863                 break;
35864             case 'under':
35865                 if(this.errorEl){
35866                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35867                 }
35868                 break;
35869             case 'side':
35870                 if(this.errorIcon){
35871                     this.errorIcon.dom.qtip = '';
35872                     this.errorIcon.hide();
35873                     this.un('resize', this.alignErrorIcon, this);
35874                 }
35875                 break;
35876             default:
35877                 var t = Roo.getDom(this.msgTarget);
35878                 t.innerHTML = '';
35879                 t.style.display = 'none';
35880                 break;
35881         }
35882         this.fireEvent('valid', this);
35883     },
35884
35885     /**
35886      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35887      * @return {Mixed} value The field value
35888      */
35889     getRawValue : function(){
35890         var v = this.el.getValue();
35891         if(v === this.emptyText){
35892             v = '';
35893         }
35894         return v;
35895     },
35896
35897     /**
35898      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35899      * @return {Mixed} value The field value
35900      */
35901     getValue : function(){
35902         var v = this.el.getValue();
35903         if(v === this.emptyText || v === undefined){
35904             v = '';
35905         }
35906         return v;
35907     },
35908
35909     /**
35910      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35911      * @param {Mixed} value The value to set
35912      */
35913     setRawValue : function(v){
35914         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35915     },
35916
35917     /**
35918      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35919      * @param {Mixed} value The value to set
35920      */
35921     setValue : function(v){
35922         this.value = v;
35923         if(this.rendered){
35924             this.el.dom.value = (v === null || v === undefined ? '' : v);
35925              this.validate();
35926         }
35927     },
35928
35929     adjustSize : function(w, h){
35930         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35931         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35932         return s;
35933     },
35934
35935     adjustWidth : function(tag, w){
35936         tag = tag.toLowerCase();
35937         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35938             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35939                 if(tag == 'input'){
35940                     return w + 2;
35941                 }
35942                 if(tag = 'textarea'){
35943                     return w-2;
35944                 }
35945             }else if(Roo.isOpera){
35946                 if(tag == 'input'){
35947                     return w + 2;
35948                 }
35949                 if(tag = 'textarea'){
35950                     return w-2;
35951                 }
35952             }
35953         }
35954         return w;
35955     }
35956 });
35957
35958
35959 // anything other than normal should be considered experimental
35960 Roo.form.Field.msgFx = {
35961     normal : {
35962         show: function(msgEl, f){
35963             msgEl.setDisplayed('block');
35964         },
35965
35966         hide : function(msgEl, f){
35967             msgEl.setDisplayed(false).update('');
35968         }
35969     },
35970
35971     slide : {
35972         show: function(msgEl, f){
35973             msgEl.slideIn('t', {stopFx:true});
35974         },
35975
35976         hide : function(msgEl, f){
35977             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35978         }
35979     },
35980
35981     slideRight : {
35982         show: function(msgEl, f){
35983             msgEl.fixDisplay();
35984             msgEl.alignTo(f.el, 'tl-tr');
35985             msgEl.slideIn('l', {stopFx:true});
35986         },
35987
35988         hide : function(msgEl, f){
35989             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35990         }
35991     }
35992 };/*
35993  * Based on:
35994  * Ext JS Library 1.1.1
35995  * Copyright(c) 2006-2007, Ext JS, LLC.
35996  *
35997  * Originally Released Under LGPL - original licence link has changed is not relivant.
35998  *
35999  * Fork - LGPL
36000  * <script type="text/javascript">
36001  */
36002  
36003
36004 /**
36005  * @class Roo.form.TextField
36006  * @extends Roo.form.Field
36007  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36008  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36009  * @constructor
36010  * Creates a new TextField
36011  * @param {Object} config Configuration options
36012  */
36013 Roo.form.TextField = function(config){
36014     Roo.form.TextField.superclass.constructor.call(this, config);
36015     this.addEvents({
36016         /**
36017          * @event autosize
36018          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36019          * according to the default logic, but this event provides a hook for the developer to apply additional
36020          * logic at runtime to resize the field if needed.
36021              * @param {Roo.form.Field} this This text field
36022              * @param {Number} width The new field width
36023              */
36024         autosize : true
36025     });
36026 };
36027
36028 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36029     /**
36030      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36031      */
36032     grow : false,
36033     /**
36034      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36035      */
36036     growMin : 30,
36037     /**
36038      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36039      */
36040     growMax : 800,
36041     /**
36042      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36043      */
36044     vtype : null,
36045     /**
36046      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36047      */
36048     maskRe : null,
36049     /**
36050      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36051      */
36052     disableKeyFilter : false,
36053     /**
36054      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36055      */
36056     allowBlank : true,
36057     /**
36058      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36059      */
36060     minLength : 0,
36061     /**
36062      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36063      */
36064     maxLength : Number.MAX_VALUE,
36065     /**
36066      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36067      */
36068     minLengthText : "The minimum length for this field is {0}",
36069     /**
36070      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36071      */
36072     maxLengthText : "The maximum length for this field is {0}",
36073     /**
36074      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36075      */
36076     selectOnFocus : false,
36077     /**
36078      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36079      */
36080     blankText : "This field is required",
36081     /**
36082      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36083      * If available, this function will be called only after the basic validators all return true, and will be passed the
36084      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36085      */
36086     validator : null,
36087     /**
36088      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36089      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36090      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36091      */
36092     regex : null,
36093     /**
36094      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36095      */
36096     regexText : "",
36097     /**
36098      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36099      */
36100     emptyText : null,
36101     /**
36102      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36103      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36104      */
36105     emptyClass : 'x-form-empty-field',
36106
36107     // private
36108     initEvents : function(){
36109         Roo.form.TextField.superclass.initEvents.call(this);
36110         if(this.validationEvent == 'keyup'){
36111             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36112             this.el.on('keyup', this.filterValidation, this);
36113         }
36114         else if(this.validationEvent !== false){
36115             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36116         }
36117         if(this.selectOnFocus || this.emptyText){
36118             this.on("focus", this.preFocus, this);
36119             if(this.emptyText){
36120                 this.on('blur', this.postBlur, this);
36121                 this.applyEmptyText();
36122             }
36123         }
36124         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36125             this.el.on("keypress", this.filterKeys, this);
36126         }
36127         if(this.grow){
36128             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36129             this.el.on("click", this.autoSize,  this);
36130         }
36131     },
36132
36133     processValue : function(value){
36134         if(this.stripCharsRe){
36135             var newValue = value.replace(this.stripCharsRe, '');
36136             if(newValue !== value){
36137                 this.setRawValue(newValue);
36138                 return newValue;
36139             }
36140         }
36141         return value;
36142     },
36143
36144     filterValidation : function(e){
36145         if(!e.isNavKeyPress()){
36146             this.validationTask.delay(this.validationDelay);
36147         }
36148     },
36149
36150     // private
36151     onKeyUp : function(e){
36152         if(!e.isNavKeyPress()){
36153             this.autoSize();
36154         }
36155     },
36156
36157     /**
36158      * Resets the current field value to the originally-loaded value and clears any validation messages.
36159      * Also adds emptyText and emptyClass if the original value was blank.
36160      */
36161     reset : function(){
36162         Roo.form.TextField.superclass.reset.call(this);
36163         this.applyEmptyText();
36164     },
36165
36166     applyEmptyText : function(){
36167         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36168             this.setRawValue(this.emptyText);
36169             this.el.addClass(this.emptyClass);
36170         }
36171     },
36172
36173     // private
36174     preFocus : function(){
36175         if(this.emptyText){
36176             if(this.el.dom.value == this.emptyText){
36177                 this.setRawValue('');
36178             }
36179             this.el.removeClass(this.emptyClass);
36180         }
36181         if(this.selectOnFocus){
36182             this.el.dom.select();
36183         }
36184     },
36185
36186     // private
36187     postBlur : function(){
36188         this.applyEmptyText();
36189     },
36190
36191     // private
36192     filterKeys : function(e){
36193         var k = e.getKey();
36194         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36195             return;
36196         }
36197         var c = e.getCharCode(), cc = String.fromCharCode(c);
36198         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36199             return;
36200         }
36201         if(!this.maskRe.test(cc)){
36202             e.stopEvent();
36203         }
36204     },
36205
36206     setValue : function(v){
36207         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36208             this.el.removeClass(this.emptyClass);
36209         }
36210         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36211         this.applyEmptyText();
36212         this.autoSize();
36213     },
36214
36215     /**
36216      * Validates a value according to the field's validation rules and marks the field as invalid
36217      * if the validation fails
36218      * @param {Mixed} value The value to validate
36219      * @return {Boolean} True if the value is valid, else false
36220      */
36221     validateValue : function(value){
36222         if(value.length < 1 || value === this.emptyText){ // if it's blank
36223              if(this.allowBlank){
36224                 this.clearInvalid();
36225                 return true;
36226              }else{
36227                 this.markInvalid(this.blankText);
36228                 return false;
36229              }
36230         }
36231         if(value.length < this.minLength){
36232             this.markInvalid(String.format(this.minLengthText, this.minLength));
36233             return false;
36234         }
36235         if(value.length > this.maxLength){
36236             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36237             return false;
36238         }
36239         if(this.vtype){
36240             var vt = Roo.form.VTypes;
36241             if(!vt[this.vtype](value, this)){
36242                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36243                 return false;
36244             }
36245         }
36246         if(typeof this.validator == "function"){
36247             var msg = this.validator(value);
36248             if(msg !== true){
36249                 this.markInvalid(msg);
36250                 return false;
36251             }
36252         }
36253         if(this.regex && !this.regex.test(value)){
36254             this.markInvalid(this.regexText);
36255             return false;
36256         }
36257         return true;
36258     },
36259
36260     /**
36261      * Selects text in this field
36262      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36263      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36264      */
36265     selectText : function(start, end){
36266         var v = this.getRawValue();
36267         if(v.length > 0){
36268             start = start === undefined ? 0 : start;
36269             end = end === undefined ? v.length : end;
36270             var d = this.el.dom;
36271             if(d.setSelectionRange){
36272                 d.setSelectionRange(start, end);
36273             }else if(d.createTextRange){
36274                 var range = d.createTextRange();
36275                 range.moveStart("character", start);
36276                 range.moveEnd("character", v.length-end);
36277                 range.select();
36278             }
36279         }
36280     },
36281
36282     /**
36283      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36284      * This only takes effect if grow = true, and fires the autosize event.
36285      */
36286     autoSize : function(){
36287         if(!this.grow || !this.rendered){
36288             return;
36289         }
36290         if(!this.metrics){
36291             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36292         }
36293         var el = this.el;
36294         var v = el.dom.value;
36295         var d = document.createElement('div');
36296         d.appendChild(document.createTextNode(v));
36297         v = d.innerHTML;
36298         d = null;
36299         v += "&#160;";
36300         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36301         this.el.setWidth(w);
36302         this.fireEvent("autosize", this, w);
36303     }
36304 });/*
36305  * Based on:
36306  * Ext JS Library 1.1.1
36307  * Copyright(c) 2006-2007, Ext JS, LLC.
36308  *
36309  * Originally Released Under LGPL - original licence link has changed is not relivant.
36310  *
36311  * Fork - LGPL
36312  * <script type="text/javascript">
36313  */
36314  
36315 /**
36316  * @class Roo.form.Hidden
36317  * @extends Roo.form.TextField
36318  * Simple Hidden element used on forms 
36319  * 
36320  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36321  * 
36322  * @constructor
36323  * Creates a new Hidden form element.
36324  * @param {Object} config Configuration options
36325  */
36326
36327
36328
36329 // easy hidden field...
36330 Roo.form.Hidden = function(config){
36331     Roo.form.Hidden.superclass.constructor.call(this, config);
36332 };
36333   
36334 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36335     fieldLabel:      '',
36336     inputType:      'hidden',
36337     width:          50,
36338     allowBlank:     true,
36339     labelSeparator: '',
36340     hidden:         true,
36341     itemCls :       'x-form-item-display-none'
36342
36343
36344 });
36345
36346
36347 /*
36348  * Based on:
36349  * Ext JS Library 1.1.1
36350  * Copyright(c) 2006-2007, Ext JS, LLC.
36351  *
36352  * Originally Released Under LGPL - original licence link has changed is not relivant.
36353  *
36354  * Fork - LGPL
36355  * <script type="text/javascript">
36356  */
36357  
36358 /**
36359  * @class Roo.form.TriggerField
36360  * @extends Roo.form.TextField
36361  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36362  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36363  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36364  * for which you can provide a custom implementation.  For example:
36365  * <pre><code>
36366 var trigger = new Roo.form.TriggerField();
36367 trigger.onTriggerClick = myTriggerFn;
36368 trigger.applyTo('my-field');
36369 </code></pre>
36370  *
36371  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36372  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36373  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36374  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36375  * @constructor
36376  * Create a new TriggerField.
36377  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36378  * to the base TextField)
36379  */
36380 Roo.form.TriggerField = function(config){
36381     this.mimicing = false;
36382     Roo.form.TriggerField.superclass.constructor.call(this, config);
36383 };
36384
36385 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36386     /**
36387      * @cfg {String} triggerClass A CSS class to apply to the trigger
36388      */
36389     /**
36390      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36391      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36392      */
36393     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36394     /**
36395      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36396      */
36397     hideTrigger:false,
36398
36399     /** @cfg {Boolean} grow @hide */
36400     /** @cfg {Number} growMin @hide */
36401     /** @cfg {Number} growMax @hide */
36402
36403     /**
36404      * @hide 
36405      * @method
36406      */
36407     autoSize: Roo.emptyFn,
36408     // private
36409     monitorTab : true,
36410     // private
36411     deferHeight : true,
36412
36413     
36414     actionMode : 'wrap',
36415     // private
36416     onResize : function(w, h){
36417         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36418         if(typeof w == 'number'){
36419             var x = w - this.trigger.getWidth();
36420             this.el.setWidth(this.adjustWidth('input', x));
36421             this.trigger.setStyle('left', x+'px');
36422         }
36423     },
36424
36425     // private
36426     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36427
36428     // private
36429     getResizeEl : function(){
36430         return this.wrap;
36431     },
36432
36433     // private
36434     getPositionEl : function(){
36435         return this.wrap;
36436     },
36437
36438     // private
36439     alignErrorIcon : function(){
36440         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36441     },
36442
36443     // private
36444     onRender : function(ct, position){
36445         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36446         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36447         this.trigger = this.wrap.createChild(this.triggerConfig ||
36448                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36449         if(this.hideTrigger){
36450             this.trigger.setDisplayed(false);
36451         }
36452         this.initTrigger();
36453         if(!this.width){
36454             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36455         }
36456     },
36457
36458     // private
36459     initTrigger : function(){
36460         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36461         this.trigger.addClassOnOver('x-form-trigger-over');
36462         this.trigger.addClassOnClick('x-form-trigger-click');
36463     },
36464
36465     // private
36466     onDestroy : function(){
36467         if(this.trigger){
36468             this.trigger.removeAllListeners();
36469             this.trigger.remove();
36470         }
36471         if(this.wrap){
36472             this.wrap.remove();
36473         }
36474         Roo.form.TriggerField.superclass.onDestroy.call(this);
36475     },
36476
36477     // private
36478     onFocus : function(){
36479         Roo.form.TriggerField.superclass.onFocus.call(this);
36480         if(!this.mimicing){
36481             this.wrap.addClass('x-trigger-wrap-focus');
36482             this.mimicing = true;
36483             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36484             if(this.monitorTab){
36485                 this.el.on("keydown", this.checkTab, this);
36486             }
36487         }
36488     },
36489
36490     // private
36491     checkTab : function(e){
36492         if(e.getKey() == e.TAB){
36493             this.triggerBlur();
36494         }
36495     },
36496
36497     // private
36498     onBlur : function(){
36499         // do nothing
36500     },
36501
36502     // private
36503     mimicBlur : function(e, t){
36504         if(!this.wrap.contains(t) && this.validateBlur()){
36505             this.triggerBlur();
36506         }
36507     },
36508
36509     // private
36510     triggerBlur : function(){
36511         this.mimicing = false;
36512         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36513         if(this.monitorTab){
36514             this.el.un("keydown", this.checkTab, this);
36515         }
36516         this.wrap.removeClass('x-trigger-wrap-focus');
36517         Roo.form.TriggerField.superclass.onBlur.call(this);
36518     },
36519
36520     // private
36521     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36522     validateBlur : function(e, t){
36523         return true;
36524     },
36525
36526     // private
36527     onDisable : function(){
36528         Roo.form.TriggerField.superclass.onDisable.call(this);
36529         if(this.wrap){
36530             this.wrap.addClass('x-item-disabled');
36531         }
36532     },
36533
36534     // private
36535     onEnable : function(){
36536         Roo.form.TriggerField.superclass.onEnable.call(this);
36537         if(this.wrap){
36538             this.wrap.removeClass('x-item-disabled');
36539         }
36540     },
36541
36542     // private
36543     onShow : function(){
36544         var ae = this.getActionEl();
36545         
36546         if(ae){
36547             ae.dom.style.display = '';
36548             ae.dom.style.visibility = 'visible';
36549         }
36550     },
36551
36552     // private
36553     
36554     onHide : function(){
36555         var ae = this.getActionEl();
36556         ae.dom.style.display = 'none';
36557     },
36558
36559     /**
36560      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36561      * by an implementing function.
36562      * @method
36563      * @param {EventObject} e
36564      */
36565     onTriggerClick : Roo.emptyFn
36566 });
36567
36568 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36569 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36570 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36571 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36572     initComponent : function(){
36573         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36574
36575         this.triggerConfig = {
36576             tag:'span', cls:'x-form-twin-triggers', cn:[
36577             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36578             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36579         ]};
36580     },
36581
36582     getTrigger : function(index){
36583         return this.triggers[index];
36584     },
36585
36586     initTrigger : function(){
36587         var ts = this.trigger.select('.x-form-trigger', true);
36588         this.wrap.setStyle('overflow', 'hidden');
36589         var triggerField = this;
36590         ts.each(function(t, all, index){
36591             t.hide = function(){
36592                 var w = triggerField.wrap.getWidth();
36593                 this.dom.style.display = 'none';
36594                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36595             };
36596             t.show = function(){
36597                 var w = triggerField.wrap.getWidth();
36598                 this.dom.style.display = '';
36599                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36600             };
36601             var triggerIndex = 'Trigger'+(index+1);
36602
36603             if(this['hide'+triggerIndex]){
36604                 t.dom.style.display = 'none';
36605             }
36606             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36607             t.addClassOnOver('x-form-trigger-over');
36608             t.addClassOnClick('x-form-trigger-click');
36609         }, this);
36610         this.triggers = ts.elements;
36611     },
36612
36613     onTrigger1Click : Roo.emptyFn,
36614     onTrigger2Click : Roo.emptyFn
36615 });/*
36616  * Based on:
36617  * Ext JS Library 1.1.1
36618  * Copyright(c) 2006-2007, Ext JS, LLC.
36619  *
36620  * Originally Released Under LGPL - original licence link has changed is not relivant.
36621  *
36622  * Fork - LGPL
36623  * <script type="text/javascript">
36624  */
36625  
36626 /**
36627  * @class Roo.form.TextArea
36628  * @extends Roo.form.TextField
36629  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36630  * support for auto-sizing.
36631  * @constructor
36632  * Creates a new TextArea
36633  * @param {Object} config Configuration options
36634  */
36635 Roo.form.TextArea = function(config){
36636     Roo.form.TextArea.superclass.constructor.call(this, config);
36637     // these are provided exchanges for backwards compat
36638     // minHeight/maxHeight were replaced by growMin/growMax to be
36639     // compatible with TextField growing config values
36640     if(this.minHeight !== undefined){
36641         this.growMin = this.minHeight;
36642     }
36643     if(this.maxHeight !== undefined){
36644         this.growMax = this.maxHeight;
36645     }
36646 };
36647
36648 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36649     /**
36650      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36651      */
36652     growMin : 60,
36653     /**
36654      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36655      */
36656     growMax: 1000,
36657     /**
36658      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36659      * in the field (equivalent to setting overflow: hidden, defaults to false)
36660      */
36661     preventScrollbars: false,
36662     /**
36663      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36664      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36665      */
36666
36667     // private
36668     onRender : function(ct, position){
36669         if(!this.el){
36670             this.defaultAutoCreate = {
36671                 tag: "textarea",
36672                 style:"width:300px;height:60px;",
36673                 autocomplete: "off"
36674             };
36675         }
36676         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36677         if(this.grow){
36678             this.textSizeEl = Roo.DomHelper.append(document.body, {
36679                 tag: "pre", cls: "x-form-grow-sizer"
36680             });
36681             if(this.preventScrollbars){
36682                 this.el.setStyle("overflow", "hidden");
36683             }
36684             this.el.setHeight(this.growMin);
36685         }
36686     },
36687
36688     onDestroy : function(){
36689         if(this.textSizeEl){
36690             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36691         }
36692         Roo.form.TextArea.superclass.onDestroy.call(this);
36693     },
36694
36695     // private
36696     onKeyUp : function(e){
36697         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36698             this.autoSize();
36699         }
36700     },
36701
36702     /**
36703      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36704      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36705      */
36706     autoSize : function(){
36707         if(!this.grow || !this.textSizeEl){
36708             return;
36709         }
36710         var el = this.el;
36711         var v = el.dom.value;
36712         var ts = this.textSizeEl;
36713
36714         ts.innerHTML = '';
36715         ts.appendChild(document.createTextNode(v));
36716         v = ts.innerHTML;
36717
36718         Roo.fly(ts).setWidth(this.el.getWidth());
36719         if(v.length < 1){
36720             v = "&#160;&#160;";
36721         }else{
36722             if(Roo.isIE){
36723                 v = v.replace(/\n/g, '<p>&#160;</p>');
36724             }
36725             v += "&#160;\n&#160;";
36726         }
36727         ts.innerHTML = v;
36728         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36729         if(h != this.lastHeight){
36730             this.lastHeight = h;
36731             this.el.setHeight(h);
36732             this.fireEvent("autosize", this, h);
36733         }
36734     }
36735 });/*
36736  * Based on:
36737  * Ext JS Library 1.1.1
36738  * Copyright(c) 2006-2007, Ext JS, LLC.
36739  *
36740  * Originally Released Under LGPL - original licence link has changed is not relivant.
36741  *
36742  * Fork - LGPL
36743  * <script type="text/javascript">
36744  */
36745  
36746
36747 /**
36748  * @class Roo.form.NumberField
36749  * @extends Roo.form.TextField
36750  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36751  * @constructor
36752  * Creates a new NumberField
36753  * @param {Object} config Configuration options
36754  */
36755 Roo.form.NumberField = function(config){
36756     Roo.form.NumberField.superclass.constructor.call(this, config);
36757 };
36758
36759 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36760     /**
36761      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36762      */
36763     fieldClass: "x-form-field x-form-num-field",
36764     /**
36765      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36766      */
36767     allowDecimals : true,
36768     /**
36769      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36770      */
36771     decimalSeparator : ".",
36772     /**
36773      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36774      */
36775     decimalPrecision : 2,
36776     /**
36777      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36778      */
36779     allowNegative : true,
36780     /**
36781      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36782      */
36783     minValue : Number.NEGATIVE_INFINITY,
36784     /**
36785      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36786      */
36787     maxValue : Number.MAX_VALUE,
36788     /**
36789      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36790      */
36791     minText : "The minimum value for this field is {0}",
36792     /**
36793      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36794      */
36795     maxText : "The maximum value for this field is {0}",
36796     /**
36797      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36798      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36799      */
36800     nanText : "{0} is not a valid number",
36801
36802     // private
36803     initEvents : function(){
36804         Roo.form.NumberField.superclass.initEvents.call(this);
36805         var allowed = "0123456789";
36806         if(this.allowDecimals){
36807             allowed += this.decimalSeparator;
36808         }
36809         if(this.allowNegative){
36810             allowed += "-";
36811         }
36812         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36813         var keyPress = function(e){
36814             var k = e.getKey();
36815             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36816                 return;
36817             }
36818             var c = e.getCharCode();
36819             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36820                 e.stopEvent();
36821             }
36822         };
36823         this.el.on("keypress", keyPress, this);
36824     },
36825
36826     // private
36827     validateValue : function(value){
36828         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36829             return false;
36830         }
36831         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36832              return true;
36833         }
36834         var num = this.parseValue(value);
36835         if(isNaN(num)){
36836             this.markInvalid(String.format(this.nanText, value));
36837             return false;
36838         }
36839         if(num < this.minValue){
36840             this.markInvalid(String.format(this.minText, this.minValue));
36841             return false;
36842         }
36843         if(num > this.maxValue){
36844             this.markInvalid(String.format(this.maxText, this.maxValue));
36845             return false;
36846         }
36847         return true;
36848     },
36849
36850     getValue : function(){
36851         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36852     },
36853
36854     // private
36855     parseValue : function(value){
36856         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36857         return isNaN(value) ? '' : value;
36858     },
36859
36860     // private
36861     fixPrecision : function(value){
36862         var nan = isNaN(value);
36863         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36864             return nan ? '' : value;
36865         }
36866         return parseFloat(value).toFixed(this.decimalPrecision);
36867     },
36868
36869     setValue : function(v){
36870         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36871     },
36872
36873     // private
36874     decimalPrecisionFcn : function(v){
36875         return Math.floor(v);
36876     },
36877
36878     beforeBlur : function(){
36879         var v = this.parseValue(this.getRawValue());
36880         if(v){
36881             this.setValue(this.fixPrecision(v));
36882         }
36883     }
36884 });/*
36885  * Based on:
36886  * Ext JS Library 1.1.1
36887  * Copyright(c) 2006-2007, Ext JS, LLC.
36888  *
36889  * Originally Released Under LGPL - original licence link has changed is not relivant.
36890  *
36891  * Fork - LGPL
36892  * <script type="text/javascript">
36893  */
36894  
36895 /**
36896  * @class Roo.form.DateField
36897  * @extends Roo.form.TriggerField
36898  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36899 * @constructor
36900 * Create a new DateField
36901 * @param {Object} config
36902  */
36903 Roo.form.DateField = function(config){
36904     Roo.form.DateField.superclass.constructor.call(this, config);
36905     
36906       this.addEvents({
36907          
36908         /**
36909          * @event select
36910          * Fires when a date is selected
36911              * @param {Roo.form.DateField} combo This combo box
36912              * @param {Date} date The date selected
36913              */
36914         'select' : true
36915          
36916     });
36917     
36918     
36919     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36920     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36921     this.ddMatch = null;
36922     if(this.disabledDates){
36923         var dd = this.disabledDates;
36924         var re = "(?:";
36925         for(var i = 0; i < dd.length; i++){
36926             re += dd[i];
36927             if(i != dd.length-1) re += "|";
36928         }
36929         this.ddMatch = new RegExp(re + ")");
36930     }
36931 };
36932
36933 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36934     /**
36935      * @cfg {String} format
36936      * The default date format string which can be overriden for localization support.  The format must be
36937      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36938      */
36939     format : "m/d/y",
36940     /**
36941      * @cfg {String} altFormats
36942      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36943      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36944      */
36945     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36946     /**
36947      * @cfg {Array} disabledDays
36948      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36949      */
36950     disabledDays : null,
36951     /**
36952      * @cfg {String} disabledDaysText
36953      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36954      */
36955     disabledDaysText : "Disabled",
36956     /**
36957      * @cfg {Array} disabledDates
36958      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36959      * expression so they are very powerful. Some examples:
36960      * <ul>
36961      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36962      * <li>["03/08", "09/16"] would disable those days for every year</li>
36963      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36964      * <li>["03/../2006"] would disable every day in March 2006</li>
36965      * <li>["^03"] would disable every day in every March</li>
36966      * </ul>
36967      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36968      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36969      */
36970     disabledDates : null,
36971     /**
36972      * @cfg {String} disabledDatesText
36973      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36974      */
36975     disabledDatesText : "Disabled",
36976     /**
36977      * @cfg {Date/String} minValue
36978      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36979      * valid format (defaults to null).
36980      */
36981     minValue : null,
36982     /**
36983      * @cfg {Date/String} maxValue
36984      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36985      * valid format (defaults to null).
36986      */
36987     maxValue : null,
36988     /**
36989      * @cfg {String} minText
36990      * The error text to display when the date in the cell is before minValue (defaults to
36991      * 'The date in this field must be after {minValue}').
36992      */
36993     minText : "The date in this field must be equal to or after {0}",
36994     /**
36995      * @cfg {String} maxText
36996      * The error text to display when the date in the cell is after maxValue (defaults to
36997      * 'The date in this field must be before {maxValue}').
36998      */
36999     maxText : "The date in this field must be equal to or before {0}",
37000     /**
37001      * @cfg {String} invalidText
37002      * The error text to display when the date in the field is invalid (defaults to
37003      * '{value} is not a valid date - it must be in the format {format}').
37004      */
37005     invalidText : "{0} is not a valid date - it must be in the format {1}",
37006     /**
37007      * @cfg {String} triggerClass
37008      * An additional CSS class used to style the trigger button.  The trigger will always get the
37009      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37010      * which displays a calendar icon).
37011      */
37012     triggerClass : 'x-form-date-trigger',
37013     
37014
37015     /**
37016      * @cfg {bool} useIso
37017      * if enabled, then the date field will use a hidden field to store the 
37018      * real value as iso formated date. default (false)
37019      */ 
37020     useIso : false,
37021     /**
37022      * @cfg {String/Object} autoCreate
37023      * A DomHelper element spec, or true for a default element spec (defaults to
37024      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37025      */ 
37026     // private
37027     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37028     
37029     // private
37030     hiddenField: false,
37031     
37032     onRender : function(ct, position)
37033     {
37034         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37035         if (this.useIso) {
37036             this.el.dom.removeAttribute('name'); 
37037             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37038                     'before', true);
37039             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37040             // prevent input submission
37041             this.hiddenName = this.name;
37042         }
37043             
37044             
37045     },
37046     
37047     // private
37048     validateValue : function(value)
37049     {
37050         value = this.formatDate(value);
37051         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37052             return false;
37053         }
37054         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37055              return true;
37056         }
37057         var svalue = value;
37058         value = this.parseDate(value);
37059         if(!value){
37060             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37061             return false;
37062         }
37063         var time = value.getTime();
37064         if(this.minValue && time < this.minValue.getTime()){
37065             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37066             return false;
37067         }
37068         if(this.maxValue && time > this.maxValue.getTime()){
37069             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37070             return false;
37071         }
37072         if(this.disabledDays){
37073             var day = value.getDay();
37074             for(var i = 0; i < this.disabledDays.length; i++) {
37075                 if(day === this.disabledDays[i]){
37076                     this.markInvalid(this.disabledDaysText);
37077                     return false;
37078                 }
37079             }
37080         }
37081         var fvalue = this.formatDate(value);
37082         if(this.ddMatch && this.ddMatch.test(fvalue)){
37083             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37084             return false;
37085         }
37086         return true;
37087     },
37088
37089     // private
37090     // Provides logic to override the default TriggerField.validateBlur which just returns true
37091     validateBlur : function(){
37092         return !this.menu || !this.menu.isVisible();
37093     },
37094
37095     /**
37096      * Returns the current date value of the date field.
37097      * @return {Date} The date value
37098      */
37099     getValue : function(){
37100         
37101         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37102     },
37103
37104     /**
37105      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37106      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37107      * (the default format used is "m/d/y").
37108      * <br />Usage:
37109      * <pre><code>
37110 //All of these calls set the same date value (May 4, 2006)
37111
37112 //Pass a date object:
37113 var dt = new Date('5/4/06');
37114 dateField.setValue(dt);
37115
37116 //Pass a date string (default format):
37117 dateField.setValue('5/4/06');
37118
37119 //Pass a date string (custom format):
37120 dateField.format = 'Y-m-d';
37121 dateField.setValue('2006-5-4');
37122 </code></pre>
37123      * @param {String/Date} date The date or valid date string
37124      */
37125     setValue : function(date){
37126         if (this.hiddenField) {
37127             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37128         }
37129         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37130     },
37131
37132     // private
37133     parseDate : function(value){
37134         if(!value || value instanceof Date){
37135             return value;
37136         }
37137         var v = Date.parseDate(value, this.format);
37138         if(!v && this.altFormats){
37139             if(!this.altFormatsArray){
37140                 this.altFormatsArray = this.altFormats.split("|");
37141             }
37142             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37143                 v = Date.parseDate(value, this.altFormatsArray[i]);
37144             }
37145         }
37146         return v;
37147     },
37148
37149     // private
37150     formatDate : function(date, fmt){
37151         return (!date || !(date instanceof Date)) ?
37152                date : date.dateFormat(fmt || this.format);
37153     },
37154
37155     // private
37156     menuListeners : {
37157         select: function(m, d){
37158             this.setValue(d);
37159             this.fireEvent('select', this, d);
37160         },
37161         show : function(){ // retain focus styling
37162             this.onFocus();
37163         },
37164         hide : function(){
37165             this.focus.defer(10, this);
37166             var ml = this.menuListeners;
37167             this.menu.un("select", ml.select,  this);
37168             this.menu.un("show", ml.show,  this);
37169             this.menu.un("hide", ml.hide,  this);
37170         }
37171     },
37172
37173     // private
37174     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37175     onTriggerClick : function(){
37176         if(this.disabled){
37177             return;
37178         }
37179         if(this.menu == null){
37180             this.menu = new Roo.menu.DateMenu();
37181         }
37182         Roo.apply(this.menu.picker,  {
37183             showClear: this.allowBlank,
37184             minDate : this.minValue,
37185             maxDate : this.maxValue,
37186             disabledDatesRE : this.ddMatch,
37187             disabledDatesText : this.disabledDatesText,
37188             disabledDays : this.disabledDays,
37189             disabledDaysText : this.disabledDaysText,
37190             format : this.format,
37191             minText : String.format(this.minText, this.formatDate(this.minValue)),
37192             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37193         });
37194         this.menu.on(Roo.apply({}, this.menuListeners, {
37195             scope:this
37196         }));
37197         this.menu.picker.setValue(this.getValue() || new Date());
37198         this.menu.show(this.el, "tl-bl?");
37199     },
37200
37201     beforeBlur : function(){
37202         var v = this.parseDate(this.getRawValue());
37203         if(v){
37204             this.setValue(v);
37205         }
37206     }
37207
37208     /** @cfg {Boolean} grow @hide */
37209     /** @cfg {Number} growMin @hide */
37210     /** @cfg {Number} growMax @hide */
37211     /**
37212      * @hide
37213      * @method autoSize
37214      */
37215 });/*
37216  * Based on:
37217  * Ext JS Library 1.1.1
37218  * Copyright(c) 2006-2007, Ext JS, LLC.
37219  *
37220  * Originally Released Under LGPL - original licence link has changed is not relivant.
37221  *
37222  * Fork - LGPL
37223  * <script type="text/javascript">
37224  */
37225  
37226
37227 /**
37228  * @class Roo.form.ComboBox
37229  * @extends Roo.form.TriggerField
37230  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37231  * @constructor
37232  * Create a new ComboBox.
37233  * @param {Object} config Configuration options
37234  */
37235 Roo.form.ComboBox = function(config){
37236     Roo.form.ComboBox.superclass.constructor.call(this, config);
37237     this.addEvents({
37238         /**
37239          * @event expand
37240          * Fires when the dropdown list is expanded
37241              * @param {Roo.form.ComboBox} combo This combo box
37242              */
37243         'expand' : true,
37244         /**
37245          * @event collapse
37246          * Fires when the dropdown list is collapsed
37247              * @param {Roo.form.ComboBox} combo This combo box
37248              */
37249         'collapse' : true,
37250         /**
37251          * @event beforeselect
37252          * Fires before a list item is selected. Return false to cancel the selection.
37253              * @param {Roo.form.ComboBox} combo This combo box
37254              * @param {Roo.data.Record} record The data record returned from the underlying store
37255              * @param {Number} index The index of the selected item in the dropdown list
37256              */
37257         'beforeselect' : true,
37258         /**
37259          * @event select
37260          * Fires when a list item is selected
37261              * @param {Roo.form.ComboBox} combo This combo box
37262              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37263              * @param {Number} index The index of the selected item in the dropdown list
37264              */
37265         'select' : true,
37266         /**
37267          * @event beforequery
37268          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37269          * The event object passed has these properties:
37270              * @param {Roo.form.ComboBox} combo This combo box
37271              * @param {String} query The query
37272              * @param {Boolean} forceAll true to force "all" query
37273              * @param {Boolean} cancel true to cancel the query
37274              * @param {Object} e The query event object
37275              */
37276         'beforequery': true,
37277          /**
37278          * @event add
37279          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37280              * @param {Roo.form.ComboBox} combo This combo box
37281              */
37282         'add' : true,
37283         /**
37284          * @event edit
37285          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37286              * @param {Roo.form.ComboBox} combo This combo box
37287              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37288              */
37289         'edit' : true
37290         
37291         
37292     });
37293     if(this.transform){
37294         this.allowDomMove = false;
37295         var s = Roo.getDom(this.transform);
37296         if(!this.hiddenName){
37297             this.hiddenName = s.name;
37298         }
37299         if(!this.store){
37300             this.mode = 'local';
37301             var d = [], opts = s.options;
37302             for(var i = 0, len = opts.length;i < len; i++){
37303                 var o = opts[i];
37304                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37305                 if(o.selected) {
37306                     this.value = value;
37307                 }
37308                 d.push([value, o.text]);
37309             }
37310             this.store = new Roo.data.SimpleStore({
37311                 'id': 0,
37312                 fields: ['value', 'text'],
37313                 data : d
37314             });
37315             this.valueField = 'value';
37316             this.displayField = 'text';
37317         }
37318         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37319         if(!this.lazyRender){
37320             this.target = true;
37321             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37322             s.parentNode.removeChild(s); // remove it
37323             this.render(this.el.parentNode);
37324         }else{
37325             s.parentNode.removeChild(s); // remove it
37326         }
37327
37328     }
37329     if (this.store) {
37330         this.store = Roo.factory(this.store, Roo.data);
37331     }
37332     
37333     this.selectedIndex = -1;
37334     if(this.mode == 'local'){
37335         if(config.queryDelay === undefined){
37336             this.queryDelay = 10;
37337         }
37338         if(config.minChars === undefined){
37339             this.minChars = 0;
37340         }
37341     }
37342 };
37343
37344 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37345     /**
37346      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37347      */
37348     /**
37349      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37350      * rendering into an Roo.Editor, defaults to false)
37351      */
37352     /**
37353      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37354      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37355      */
37356     /**
37357      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37358      */
37359     /**
37360      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37361      * the dropdown list (defaults to undefined, with no header element)
37362      */
37363
37364      /**
37365      * @cfg {String/Roo.Template} tpl The template to use to render the output
37366      */
37367      
37368     // private
37369     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37370     /**
37371      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37372      */
37373     listWidth: undefined,
37374     /**
37375      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37376      * mode = 'remote' or 'text' if mode = 'local')
37377      */
37378     displayField: undefined,
37379     /**
37380      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37381      * mode = 'remote' or 'value' if mode = 'local'). 
37382      * Note: use of a valueField requires the user make a selection
37383      * in order for a value to be mapped.
37384      */
37385     valueField: undefined,
37386     
37387     
37388     /**
37389      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37390      * field's data value (defaults to the underlying DOM element's name)
37391      */
37392     hiddenName: undefined,
37393     /**
37394      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37395      */
37396     listClass: '',
37397     /**
37398      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37399      */
37400     selectedClass: 'x-combo-selected',
37401     /**
37402      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37403      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37404      * which displays a downward arrow icon).
37405      */
37406     triggerClass : 'x-form-arrow-trigger',
37407     /**
37408      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37409      */
37410     shadow:'sides',
37411     /**
37412      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37413      * anchor positions (defaults to 'tl-bl')
37414      */
37415     listAlign: 'tl-bl?',
37416     /**
37417      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37418      */
37419     maxHeight: 300,
37420     /**
37421      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37422      * query specified by the allQuery config option (defaults to 'query')
37423      */
37424     triggerAction: 'query',
37425     /**
37426      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37427      * (defaults to 4, does not apply if editable = false)
37428      */
37429     minChars : 4,
37430     /**
37431      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37432      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37433      */
37434     typeAhead: false,
37435     /**
37436      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37437      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37438      */
37439     queryDelay: 500,
37440     /**
37441      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37442      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37443      */
37444     pageSize: 0,
37445     /**
37446      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37447      * when editable = true (defaults to false)
37448      */
37449     selectOnFocus:false,
37450     /**
37451      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37452      */
37453     queryParam: 'query',
37454     /**
37455      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37456      * when mode = 'remote' (defaults to 'Loading...')
37457      */
37458     loadingText: 'Loading...',
37459     /**
37460      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37461      */
37462     resizable: false,
37463     /**
37464      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37465      */
37466     handleHeight : 8,
37467     /**
37468      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37469      * traditional select (defaults to true)
37470      */
37471     editable: true,
37472     /**
37473      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37474      */
37475     allQuery: '',
37476     /**
37477      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37478      */
37479     mode: 'remote',
37480     /**
37481      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37482      * listWidth has a higher value)
37483      */
37484     minListWidth : 70,
37485     /**
37486      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37487      * allow the user to set arbitrary text into the field (defaults to false)
37488      */
37489     forceSelection:false,
37490     /**
37491      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37492      * if typeAhead = true (defaults to 250)
37493      */
37494     typeAheadDelay : 250,
37495     /**
37496      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37497      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37498      */
37499     valueNotFoundText : undefined,
37500     /**
37501      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37502      */
37503     blockFocus : false,
37504     
37505     /**
37506      * @cfg {Boolean} disableClear Disable showing of clear button.
37507      */
37508     disableClear : false,
37509     /**
37510      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37511      */
37512     alwaysQuery : false,
37513     
37514     //private
37515     addicon : false,
37516     editicon: false,
37517     
37518     // element that contains real text value.. (when hidden is used..)
37519      
37520     // private
37521     onRender : function(ct, position){
37522         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37523         if(this.hiddenName){
37524             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37525                     'before', true);
37526             this.hiddenField.value =
37527                 this.hiddenValue !== undefined ? this.hiddenValue :
37528                 this.value !== undefined ? this.value : '';
37529
37530             // prevent input submission
37531             this.el.dom.removeAttribute('name');
37532              
37533              
37534         }
37535         if(Roo.isGecko){
37536             this.el.dom.setAttribute('autocomplete', 'off');
37537         }
37538
37539         var cls = 'x-combo-list';
37540
37541         this.list = new Roo.Layer({
37542             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37543         });
37544
37545         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37546         this.list.setWidth(lw);
37547         this.list.swallowEvent('mousewheel');
37548         this.assetHeight = 0;
37549
37550         if(this.title){
37551             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37552             this.assetHeight += this.header.getHeight();
37553         }
37554
37555         this.innerList = this.list.createChild({cls:cls+'-inner'});
37556         this.innerList.on('mouseover', this.onViewOver, this);
37557         this.innerList.on('mousemove', this.onViewMove, this);
37558         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37559         
37560         if(this.allowBlank && !this.pageSize && !this.disableClear){
37561             this.footer = this.list.createChild({cls:cls+'-ft'});
37562             this.pageTb = new Roo.Toolbar(this.footer);
37563            
37564         }
37565         if(this.pageSize){
37566             this.footer = this.list.createChild({cls:cls+'-ft'});
37567             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37568                     {pageSize: this.pageSize});
37569             
37570         }
37571         
37572         if (this.pageTb && this.allowBlank && !this.disableClear) {
37573             var _this = this;
37574             this.pageTb.add(new Roo.Toolbar.Fill(), {
37575                 cls: 'x-btn-icon x-btn-clear',
37576                 text: '&#160;',
37577                 handler: function()
37578                 {
37579                     _this.collapse();
37580                     _this.clearValue();
37581                     _this.onSelect(false, -1);
37582                 }
37583             });
37584         }
37585         if (this.footer) {
37586             this.assetHeight += this.footer.getHeight();
37587         }
37588         
37589
37590         if(!this.tpl){
37591             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37592         }
37593
37594         this.view = new Roo.View(this.innerList, this.tpl, {
37595             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37596         });
37597
37598         this.view.on('click', this.onViewClick, this);
37599
37600         this.store.on('beforeload', this.onBeforeLoad, this);
37601         this.store.on('load', this.onLoad, this);
37602         this.store.on('loadexception', this.onLoadException, this);
37603
37604         if(this.resizable){
37605             this.resizer = new Roo.Resizable(this.list,  {
37606                pinned:true, handles:'se'
37607             });
37608             this.resizer.on('resize', function(r, w, h){
37609                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37610                 this.listWidth = w;
37611                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37612                 this.restrictHeight();
37613             }, this);
37614             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37615         }
37616         if(!this.editable){
37617             this.editable = true;
37618             this.setEditable(false);
37619         }  
37620         
37621         
37622         if (typeof(this.events.add.listeners) != 'undefined') {
37623             
37624             this.addicon = this.wrap.createChild(
37625                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37626        
37627             this.addicon.on('click', function(e) {
37628                 this.fireEvent('add', this);
37629             }, this);
37630         }
37631         if (typeof(this.events.edit.listeners) != 'undefined') {
37632             
37633             this.editicon = this.wrap.createChild(
37634                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37635             if (this.addicon) {
37636                 this.editicon.setStyle('margin-left', '40px');
37637             }
37638             this.editicon.on('click', function(e) {
37639                 
37640                 // we fire even  if inothing is selected..
37641                 this.fireEvent('edit', this, this.lastData );
37642                 
37643             }, this);
37644         }
37645         
37646         
37647         
37648     },
37649
37650     // private
37651     initEvents : function(){
37652         Roo.form.ComboBox.superclass.initEvents.call(this);
37653
37654         this.keyNav = new Roo.KeyNav(this.el, {
37655             "up" : function(e){
37656                 this.inKeyMode = true;
37657                 this.selectPrev();
37658             },
37659
37660             "down" : function(e){
37661                 if(!this.isExpanded()){
37662                     this.onTriggerClick();
37663                 }else{
37664                     this.inKeyMode = true;
37665                     this.selectNext();
37666                 }
37667             },
37668
37669             "enter" : function(e){
37670                 this.onViewClick();
37671                 //return true;
37672             },
37673
37674             "esc" : function(e){
37675                 this.collapse();
37676             },
37677
37678             "tab" : function(e){
37679                 this.onViewClick(false);
37680                 this.fireEvent("specialkey", this, e);
37681                 return true;
37682             },
37683
37684             scope : this,
37685
37686             doRelay : function(foo, bar, hname){
37687                 if(hname == 'down' || this.scope.isExpanded()){
37688                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37689                 }
37690                 return true;
37691             },
37692
37693             forceKeyDown: true
37694         });
37695         this.queryDelay = Math.max(this.queryDelay || 10,
37696                 this.mode == 'local' ? 10 : 250);
37697         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37698         if(this.typeAhead){
37699             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37700         }
37701         if(this.editable !== false){
37702             this.el.on("keyup", this.onKeyUp, this);
37703         }
37704         if(this.forceSelection){
37705             this.on('blur', this.doForce, this);
37706         }
37707     },
37708
37709     onDestroy : function(){
37710         if(this.view){
37711             this.view.setStore(null);
37712             this.view.el.removeAllListeners();
37713             this.view.el.remove();
37714             this.view.purgeListeners();
37715         }
37716         if(this.list){
37717             this.list.destroy();
37718         }
37719         if(this.store){
37720             this.store.un('beforeload', this.onBeforeLoad, this);
37721             this.store.un('load', this.onLoad, this);
37722             this.store.un('loadexception', this.onLoadException, this);
37723         }
37724         Roo.form.ComboBox.superclass.onDestroy.call(this);
37725     },
37726
37727     // private
37728     fireKey : function(e){
37729         if(e.isNavKeyPress() && !this.list.isVisible()){
37730             this.fireEvent("specialkey", this, e);
37731         }
37732     },
37733
37734     // private
37735     onResize: function(w, h){
37736         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37737         
37738         if(typeof w != 'number'){
37739             // we do not handle it!?!?
37740             return;
37741         }
37742         var tw = this.trigger.getWidth();
37743         tw += this.addicon ? this.addicon.getWidth() : 0;
37744         tw += this.editicon ? this.editicon.getWidth() : 0;
37745         var x = w - tw;
37746         this.el.setWidth( this.adjustWidth('input', x));
37747             
37748         this.trigger.setStyle('left', x+'px');
37749         
37750         if(this.list && this.listWidth === undefined){
37751             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37752             this.list.setWidth(lw);
37753             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37754         }
37755         
37756     
37757         
37758     },
37759
37760     /**
37761      * Allow or prevent the user from directly editing the field text.  If false is passed,
37762      * the user will only be able to select from the items defined in the dropdown list.  This method
37763      * is the runtime equivalent of setting the 'editable' config option at config time.
37764      * @param {Boolean} value True to allow the user to directly edit the field text
37765      */
37766     setEditable : function(value){
37767         if(value == this.editable){
37768             return;
37769         }
37770         this.editable = value;
37771         if(!value){
37772             this.el.dom.setAttribute('readOnly', true);
37773             this.el.on('mousedown', this.onTriggerClick,  this);
37774             this.el.addClass('x-combo-noedit');
37775         }else{
37776             this.el.dom.setAttribute('readOnly', false);
37777             this.el.un('mousedown', this.onTriggerClick,  this);
37778             this.el.removeClass('x-combo-noedit');
37779         }
37780     },
37781
37782     // private
37783     onBeforeLoad : function(){
37784         if(!this.hasFocus){
37785             return;
37786         }
37787         this.innerList.update(this.loadingText ?
37788                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37789         this.restrictHeight();
37790         this.selectedIndex = -1;
37791     },
37792
37793     // private
37794     onLoad : function(){
37795         if(!this.hasFocus){
37796             return;
37797         }
37798         if(this.store.getCount() > 0){
37799             this.expand();
37800             this.restrictHeight();
37801             if(this.lastQuery == this.allQuery){
37802                 if(this.editable){
37803                     this.el.dom.select();
37804                 }
37805                 if(!this.selectByValue(this.value, true)){
37806                     this.select(0, true);
37807                 }
37808             }else{
37809                 this.selectNext();
37810                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37811                     this.taTask.delay(this.typeAheadDelay);
37812                 }
37813             }
37814         }else{
37815             this.onEmptyResults();
37816         }
37817         //this.el.focus();
37818     },
37819     // private
37820     onLoadException : function()
37821     {
37822         this.collapse();
37823         Roo.log(this.store.reader.jsonData);
37824         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37825             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37826         }
37827         
37828         
37829     },
37830     // private
37831     onTypeAhead : function(){
37832         if(this.store.getCount() > 0){
37833             var r = this.store.getAt(0);
37834             var newValue = r.data[this.displayField];
37835             var len = newValue.length;
37836             var selStart = this.getRawValue().length;
37837             if(selStart != len){
37838                 this.setRawValue(newValue);
37839                 this.selectText(selStart, newValue.length);
37840             }
37841         }
37842     },
37843
37844     // private
37845     onSelect : function(record, index){
37846         if(this.fireEvent('beforeselect', this, record, index) !== false){
37847             this.setFromData(index > -1 ? record.data : false);
37848             this.collapse();
37849             this.fireEvent('select', this, record, index);
37850         }
37851     },
37852
37853     /**
37854      * Returns the currently selected field value or empty string if no value is set.
37855      * @return {String} value The selected value
37856      */
37857     getValue : function(){
37858         if(this.valueField){
37859             return typeof this.value != 'undefined' ? this.value : '';
37860         }else{
37861             return Roo.form.ComboBox.superclass.getValue.call(this);
37862         }
37863     },
37864
37865     /**
37866      * Clears any text/value currently set in the field
37867      */
37868     clearValue : function(){
37869         if(this.hiddenField){
37870             this.hiddenField.value = '';
37871         }
37872         this.value = '';
37873         this.setRawValue('');
37874         this.lastSelectionText = '';
37875         this.applyEmptyText();
37876     },
37877
37878     /**
37879      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37880      * will be displayed in the field.  If the value does not match the data value of an existing item,
37881      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37882      * Otherwise the field will be blank (although the value will still be set).
37883      * @param {String} value The value to match
37884      */
37885     setValue : function(v){
37886         var text = v;
37887         if(this.valueField){
37888             var r = this.findRecord(this.valueField, v);
37889             if(r){
37890                 text = r.data[this.displayField];
37891             }else if(this.valueNotFoundText !== undefined){
37892                 text = this.valueNotFoundText;
37893             }
37894         }
37895         this.lastSelectionText = text;
37896         if(this.hiddenField){
37897             this.hiddenField.value = v;
37898         }
37899         Roo.form.ComboBox.superclass.setValue.call(this, text);
37900         this.value = v;
37901     },
37902     /**
37903      * @property {Object} the last set data for the element
37904      */
37905     
37906     lastData : false,
37907     /**
37908      * Sets the value of the field based on a object which is related to the record format for the store.
37909      * @param {Object} value the value to set as. or false on reset?
37910      */
37911     setFromData : function(o){
37912         var dv = ''; // display value
37913         var vv = ''; // value value..
37914         this.lastData = o;
37915         if (this.displayField) {
37916             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37917         } else {
37918             // this is an error condition!!!
37919             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37920         }
37921         
37922         if(this.valueField){
37923             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37924         }
37925         if(this.hiddenField){
37926             this.hiddenField.value = vv;
37927             
37928             this.lastSelectionText = dv;
37929             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37930             this.value = vv;
37931             return;
37932         }
37933         // no hidden field.. - we store the value in 'value', but still display
37934         // display field!!!!
37935         this.lastSelectionText = dv;
37936         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37937         this.value = vv;
37938         
37939         
37940     },
37941     // private
37942     reset : function(){
37943         // overridden so that last data is reset..
37944         this.setValue(this.originalValue);
37945         this.clearInvalid();
37946         this.lastData = false;
37947     },
37948     // private
37949     findRecord : function(prop, value){
37950         var record;
37951         if(this.store.getCount() > 0){
37952             this.store.each(function(r){
37953                 if(r.data[prop] == value){
37954                     record = r;
37955                     return false;
37956                 }
37957                 return true;
37958             });
37959         }
37960         return record;
37961     },
37962     
37963     getName: function()
37964     {
37965         // returns hidden if it's set..
37966         if (!this.rendered) {return ''};
37967         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37968         
37969     },
37970     // private
37971     onViewMove : function(e, t){
37972         this.inKeyMode = false;
37973     },
37974
37975     // private
37976     onViewOver : function(e, t){
37977         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37978             return;
37979         }
37980         var item = this.view.findItemFromChild(t);
37981         if(item){
37982             var index = this.view.indexOf(item);
37983             this.select(index, false);
37984         }
37985     },
37986
37987     // private
37988     onViewClick : function(doFocus)
37989     {
37990         var index = this.view.getSelectedIndexes()[0];
37991         var r = this.store.getAt(index);
37992         if(r){
37993             this.onSelect(r, index);
37994         }
37995         if(doFocus !== false && !this.blockFocus){
37996             this.el.focus();
37997         }
37998     },
37999
38000     // private
38001     restrictHeight : function(){
38002         this.innerList.dom.style.height = '';
38003         var inner = this.innerList.dom;
38004         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38005         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38006         this.list.beginUpdate();
38007         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38008         this.list.alignTo(this.el, this.listAlign);
38009         this.list.endUpdate();
38010     },
38011
38012     // private
38013     onEmptyResults : function(){
38014         this.collapse();
38015     },
38016
38017     /**
38018      * Returns true if the dropdown list is expanded, else false.
38019      */
38020     isExpanded : function(){
38021         return this.list.isVisible();
38022     },
38023
38024     /**
38025      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38026      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38027      * @param {String} value The data value of the item to select
38028      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38029      * selected item if it is not currently in view (defaults to true)
38030      * @return {Boolean} True if the value matched an item in the list, else false
38031      */
38032     selectByValue : function(v, scrollIntoView){
38033         if(v !== undefined && v !== null){
38034             var r = this.findRecord(this.valueField || this.displayField, v);
38035             if(r){
38036                 this.select(this.store.indexOf(r), scrollIntoView);
38037                 return true;
38038             }
38039         }
38040         return false;
38041     },
38042
38043     /**
38044      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38045      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38046      * @param {Number} index The zero-based index of the list item to select
38047      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38048      * selected item if it is not currently in view (defaults to true)
38049      */
38050     select : function(index, scrollIntoView){
38051         this.selectedIndex = index;
38052         this.view.select(index);
38053         if(scrollIntoView !== false){
38054             var el = this.view.getNode(index);
38055             if(el){
38056                 this.innerList.scrollChildIntoView(el, false);
38057             }
38058         }
38059     },
38060
38061     // private
38062     selectNext : function(){
38063         var ct = this.store.getCount();
38064         if(ct > 0){
38065             if(this.selectedIndex == -1){
38066                 this.select(0);
38067             }else if(this.selectedIndex < ct-1){
38068                 this.select(this.selectedIndex+1);
38069             }
38070         }
38071     },
38072
38073     // private
38074     selectPrev : function(){
38075         var ct = this.store.getCount();
38076         if(ct > 0){
38077             if(this.selectedIndex == -1){
38078                 this.select(0);
38079             }else if(this.selectedIndex != 0){
38080                 this.select(this.selectedIndex-1);
38081             }
38082         }
38083     },
38084
38085     // private
38086     onKeyUp : function(e){
38087         if(this.editable !== false && !e.isSpecialKey()){
38088             this.lastKey = e.getKey();
38089             this.dqTask.delay(this.queryDelay);
38090         }
38091     },
38092
38093     // private
38094     validateBlur : function(){
38095         return !this.list || !this.list.isVisible();   
38096     },
38097
38098     // private
38099     initQuery : function(){
38100         this.doQuery(this.getRawValue());
38101     },
38102
38103     // private
38104     doForce : function(){
38105         if(this.el.dom.value.length > 0){
38106             this.el.dom.value =
38107                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38108             this.applyEmptyText();
38109         }
38110     },
38111
38112     /**
38113      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38114      * query allowing the query action to be canceled if needed.
38115      * @param {String} query The SQL query to execute
38116      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38117      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38118      * saved in the current store (defaults to false)
38119      */
38120     doQuery : function(q, forceAll){
38121         if(q === undefined || q === null){
38122             q = '';
38123         }
38124         var qe = {
38125             query: q,
38126             forceAll: forceAll,
38127             combo: this,
38128             cancel:false
38129         };
38130         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38131             return false;
38132         }
38133         q = qe.query;
38134         forceAll = qe.forceAll;
38135         if(forceAll === true || (q.length >= this.minChars)){
38136             if(this.lastQuery != q || this.alwaysQuery){
38137                 this.lastQuery = q;
38138                 if(this.mode == 'local'){
38139                     this.selectedIndex = -1;
38140                     if(forceAll){
38141                         this.store.clearFilter();
38142                     }else{
38143                         this.store.filter(this.displayField, q);
38144                     }
38145                     this.onLoad();
38146                 }else{
38147                     this.store.baseParams[this.queryParam] = q;
38148                     this.store.load({
38149                         params: this.getParams(q)
38150                     });
38151                     this.expand();
38152                 }
38153             }else{
38154                 this.selectedIndex = -1;
38155                 this.onLoad();   
38156             }
38157         }
38158     },
38159
38160     // private
38161     getParams : function(q){
38162         var p = {};
38163         //p[this.queryParam] = q;
38164         if(this.pageSize){
38165             p.start = 0;
38166             p.limit = this.pageSize;
38167         }
38168         return p;
38169     },
38170
38171     /**
38172      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38173      */
38174     collapse : function(){
38175         if(!this.isExpanded()){
38176             return;
38177         }
38178         this.list.hide();
38179         Roo.get(document).un('mousedown', this.collapseIf, this);
38180         Roo.get(document).un('mousewheel', this.collapseIf, this);
38181         if (!this.editable) {
38182             Roo.get(document).un('keydown', this.listKeyPress, this);
38183         }
38184         this.fireEvent('collapse', this);
38185     },
38186
38187     // private
38188     collapseIf : function(e){
38189         if(!e.within(this.wrap) && !e.within(this.list)){
38190             this.collapse();
38191         }
38192     },
38193
38194     /**
38195      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38196      */
38197     expand : function(){
38198         if(this.isExpanded() || !this.hasFocus){
38199             return;
38200         }
38201         this.list.alignTo(this.el, this.listAlign);
38202         this.list.show();
38203         Roo.get(document).on('mousedown', this.collapseIf, this);
38204         Roo.get(document).on('mousewheel', this.collapseIf, this);
38205         if (!this.editable) {
38206             Roo.get(document).on('keydown', this.listKeyPress, this);
38207         }
38208         
38209         this.fireEvent('expand', this);
38210     },
38211
38212     // private
38213     // Implements the default empty TriggerField.onTriggerClick function
38214     onTriggerClick : function(){
38215         if(this.disabled){
38216             return;
38217         }
38218         if(this.isExpanded()){
38219             this.collapse();
38220             if (!this.blockFocus) {
38221                 this.el.focus();
38222             }
38223             
38224         }else {
38225             this.hasFocus = true;
38226             if(this.triggerAction == 'all') {
38227                 this.doQuery(this.allQuery, true);
38228             } else {
38229                 this.doQuery(this.getRawValue());
38230             }
38231             if (!this.blockFocus) {
38232                 this.el.focus();
38233             }
38234         }
38235     },
38236     listKeyPress : function(e)
38237     {
38238         //Roo.log('listkeypress');
38239         // scroll to first matching element based on key pres..
38240         if (e.isSpecialKey()) {
38241             return false;
38242         }
38243         var k = String.fromCharCode(e.getKey()).toUpperCase();
38244         //Roo.log(k);
38245         var match  = false;
38246         var csel = this.view.getSelectedNodes();
38247         var cselitem = false;
38248         if (csel.length) {
38249             var ix = this.view.indexOf(csel[0]);
38250             cselitem  = this.store.getAt(ix);
38251             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38252                 cselitem = false;
38253             }
38254             
38255         }
38256         
38257         this.store.each(function(v) { 
38258             if (cselitem) {
38259                 // start at existing selection.
38260                 if (cselitem.id == v.id) {
38261                     cselitem = false;
38262                 }
38263                 return;
38264             }
38265                 
38266             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38267                 match = this.store.indexOf(v);
38268                 return false;
38269             }
38270         }, this);
38271         
38272         if (match === false) {
38273             return true; // no more action?
38274         }
38275         // scroll to?
38276         this.view.select(match);
38277         var sn = Roo.get(this.view.getSelectedNodes()[0])
38278         sn.scrollIntoView(sn.dom.parentNode, false);
38279     }
38280
38281     /** 
38282     * @cfg {Boolean} grow 
38283     * @hide 
38284     */
38285     /** 
38286     * @cfg {Number} growMin 
38287     * @hide 
38288     */
38289     /** 
38290     * @cfg {Number} growMax 
38291     * @hide 
38292     */
38293     /**
38294      * @hide
38295      * @method autoSize
38296      */
38297 });/*
38298  * Based on:
38299  * Ext JS Library 1.1.1
38300  * Copyright(c) 2006-2007, Ext JS, LLC.
38301  *
38302  * Originally Released Under LGPL - original licence link has changed is not relivant.
38303  *
38304  * Fork - LGPL
38305  * <script type="text/javascript">
38306  */
38307 /**
38308  * @class Roo.form.Checkbox
38309  * @extends Roo.form.Field
38310  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38311  * @constructor
38312  * Creates a new Checkbox
38313  * @param {Object} config Configuration options
38314  */
38315 Roo.form.Checkbox = function(config){
38316     Roo.form.Checkbox.superclass.constructor.call(this, config);
38317     this.addEvents({
38318         /**
38319          * @event check
38320          * Fires when the checkbox is checked or unchecked.
38321              * @param {Roo.form.Checkbox} this This checkbox
38322              * @param {Boolean} checked The new checked value
38323              */
38324         check : true
38325     });
38326 };
38327
38328 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38329     /**
38330      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38331      */
38332     focusClass : undefined,
38333     /**
38334      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38335      */
38336     fieldClass: "x-form-field",
38337     /**
38338      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38339      */
38340     checked: false,
38341     /**
38342      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38343      * {tag: "input", type: "checkbox", autocomplete: "off"})
38344      */
38345     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38346     /**
38347      * @cfg {String} boxLabel The text that appears beside the checkbox
38348      */
38349     boxLabel : "",
38350     /**
38351      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38352      */  
38353     inputValue : '1',
38354     /**
38355      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38356      */
38357      valueOff: '0', // value when not checked..
38358
38359     actionMode : 'viewEl', 
38360     //
38361     // private
38362     itemCls : 'x-menu-check-item x-form-item',
38363     groupClass : 'x-menu-group-item',
38364     inputType : 'hidden',
38365     
38366     
38367     inSetChecked: false, // check that we are not calling self...
38368     
38369     inputElement: false, // real input element?
38370     basedOn: false, // ????
38371     
38372     isFormField: true, // not sure where this is needed!!!!
38373
38374     onResize : function(){
38375         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38376         if(!this.boxLabel){
38377             this.el.alignTo(this.wrap, 'c-c');
38378         }
38379     },
38380
38381     initEvents : function(){
38382         Roo.form.Checkbox.superclass.initEvents.call(this);
38383         this.el.on("click", this.onClick,  this);
38384         this.el.on("change", this.onClick,  this);
38385     },
38386
38387
38388     getResizeEl : function(){
38389         return this.wrap;
38390     },
38391
38392     getPositionEl : function(){
38393         return this.wrap;
38394     },
38395
38396     // private
38397     onRender : function(ct, position){
38398         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38399         /*
38400         if(this.inputValue !== undefined){
38401             this.el.dom.value = this.inputValue;
38402         }
38403         */
38404         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38405         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38406         var viewEl = this.wrap.createChild({ 
38407             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38408         this.viewEl = viewEl;   
38409         this.wrap.on('click', this.onClick,  this); 
38410         
38411         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38412         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38413         
38414         
38415         
38416         if(this.boxLabel){
38417             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38418         //    viewEl.on('click', this.onClick,  this); 
38419         }
38420         //if(this.checked){
38421             this.setChecked(this.checked);
38422         //}else{
38423             //this.checked = this.el.dom;
38424         //}
38425
38426     },
38427
38428     // private
38429     initValue : Roo.emptyFn,
38430
38431     /**
38432      * Returns the checked state of the checkbox.
38433      * @return {Boolean} True if checked, else false
38434      */
38435     getValue : function(){
38436         if(this.el){
38437             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38438         }
38439         return this.valueOff;
38440         
38441     },
38442
38443         // private
38444     onClick : function(){ 
38445         this.setChecked(!this.checked);
38446
38447         //if(this.el.dom.checked != this.checked){
38448         //    this.setValue(this.el.dom.checked);
38449        // }
38450     },
38451
38452     /**
38453      * Sets the checked state of the checkbox.
38454      * On is always based on a string comparison between inputValue and the param.
38455      * @param {Boolean/String} value - the value to set 
38456      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38457      */
38458     setValue : function(v,suppressEvent){
38459         
38460         
38461         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38462         //if(this.el && this.el.dom){
38463         //    this.el.dom.checked = this.checked;
38464         //    this.el.dom.defaultChecked = this.checked;
38465         //}
38466         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38467         //this.fireEvent("check", this, this.checked);
38468     },
38469     // private..
38470     setChecked : function(state,suppressEvent)
38471     {
38472         if (this.inSetChecked) {
38473             this.checked = state;
38474             return;
38475         }
38476         
38477     
38478         if(this.wrap){
38479             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38480         }
38481         this.checked = state;
38482         if(suppressEvent !== true){
38483             this.fireEvent('check', this, state);
38484         }
38485         this.inSetChecked = true;
38486         this.el.dom.value = state ? this.inputValue : this.valueOff;
38487         this.inSetChecked = false;
38488         
38489     },
38490     // handle setting of hidden value by some other method!!?!?
38491     setFromHidden: function()
38492     {
38493         if(!this.el){
38494             return;
38495         }
38496         //console.log("SET FROM HIDDEN");
38497         //alert('setFrom hidden');
38498         this.setValue(this.el.dom.value);
38499     },
38500     
38501     onDestroy : function()
38502     {
38503         if(this.viewEl){
38504             Roo.get(this.viewEl).remove();
38505         }
38506          
38507         Roo.form.Checkbox.superclass.onDestroy.call(this);
38508     }
38509
38510 });/*
38511  * Based on:
38512  * Ext JS Library 1.1.1
38513  * Copyright(c) 2006-2007, Ext JS, LLC.
38514  *
38515  * Originally Released Under LGPL - original licence link has changed is not relivant.
38516  *
38517  * Fork - LGPL
38518  * <script type="text/javascript">
38519  */
38520  
38521 /**
38522  * @class Roo.form.Radio
38523  * @extends Roo.form.Checkbox
38524  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38525  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38526  * @constructor
38527  * Creates a new Radio
38528  * @param {Object} config Configuration options
38529  */
38530 Roo.form.Radio = function(){
38531     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38532 };
38533 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38534     inputType: 'radio',
38535
38536     /**
38537      * If this radio is part of a group, it will return the selected value
38538      * @return {String}
38539      */
38540     getGroupValue : function(){
38541         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38542     }
38543 });//<script type="text/javascript">
38544
38545 /*
38546  * Ext JS Library 1.1.1
38547  * Copyright(c) 2006-2007, Ext JS, LLC.
38548  * licensing@extjs.com
38549  * 
38550  * http://www.extjs.com/license
38551  */
38552  
38553  /*
38554   * 
38555   * Known bugs:
38556   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38557   * - IE ? - no idea how much works there.
38558   * 
38559   * 
38560   * 
38561   */
38562  
38563
38564 /**
38565  * @class Ext.form.HtmlEditor
38566  * @extends Ext.form.Field
38567  * Provides a lightweight HTML Editor component.
38568  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38569  * 
38570  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38571  * supported by this editor.</b><br/><br/>
38572  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38573  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38574  */
38575 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38576       /**
38577      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38578      */
38579     toolbars : false,
38580     /**
38581      * @cfg {String} createLinkText The default text for the create link prompt
38582      */
38583     createLinkText : 'Please enter the URL for the link:',
38584     /**
38585      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38586      */
38587     defaultLinkValue : 'http:/'+'/',
38588    
38589      /**
38590      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38591      *                        Roo.resizable.
38592      */
38593     resizable : false,
38594      /**
38595      * @cfg {Number} height (in pixels)
38596      */   
38597     height: 300,
38598    /**
38599      * @cfg {Number} width (in pixels)
38600      */   
38601     width: 500,
38602     
38603     /**
38604      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38605      * 
38606      */
38607     stylesheets: false,
38608     
38609     // id of frame..
38610     frameId: false,
38611     
38612     // private properties
38613     validationEvent : false,
38614     deferHeight: true,
38615     initialized : false,
38616     activated : false,
38617     sourceEditMode : false,
38618     onFocus : Roo.emptyFn,
38619     iframePad:3,
38620     hideMode:'offsets',
38621     
38622     defaultAutoCreate : { // modified by initCompnoent..
38623         tag: "textarea",
38624         style:"width:500px;height:300px;",
38625         autocomplete: "off"
38626     },
38627
38628     // private
38629     initComponent : function(){
38630         this.addEvents({
38631             /**
38632              * @event initialize
38633              * Fires when the editor is fully initialized (including the iframe)
38634              * @param {HtmlEditor} this
38635              */
38636             initialize: true,
38637             /**
38638              * @event activate
38639              * Fires when the editor is first receives the focus. Any insertion must wait
38640              * until after this event.
38641              * @param {HtmlEditor} this
38642              */
38643             activate: true,
38644              /**
38645              * @event beforesync
38646              * Fires before the textarea is updated with content from the editor iframe. Return false
38647              * to cancel the sync.
38648              * @param {HtmlEditor} this
38649              * @param {String} html
38650              */
38651             beforesync: true,
38652              /**
38653              * @event beforepush
38654              * Fires before the iframe editor is updated with content from the textarea. Return false
38655              * to cancel the push.
38656              * @param {HtmlEditor} this
38657              * @param {String} html
38658              */
38659             beforepush: true,
38660              /**
38661              * @event sync
38662              * Fires when the textarea is updated with content from the editor iframe.
38663              * @param {HtmlEditor} this
38664              * @param {String} html
38665              */
38666             sync: true,
38667              /**
38668              * @event push
38669              * Fires when the iframe editor is updated with content from the textarea.
38670              * @param {HtmlEditor} this
38671              * @param {String} html
38672              */
38673             push: true,
38674              /**
38675              * @event editmodechange
38676              * Fires when the editor switches edit modes
38677              * @param {HtmlEditor} this
38678              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38679              */
38680             editmodechange: true,
38681             /**
38682              * @event editorevent
38683              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38684              * @param {HtmlEditor} this
38685              */
38686             editorevent: true
38687         });
38688         this.defaultAutoCreate =  {
38689             tag: "textarea",
38690             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38691             autocomplete: "off"
38692         };
38693     },
38694
38695     /**
38696      * Protected method that will not generally be called directly. It
38697      * is called when the editor creates its toolbar. Override this method if you need to
38698      * add custom toolbar buttons.
38699      * @param {HtmlEditor} editor
38700      */
38701     createToolbar : function(editor){
38702         if (!editor.toolbars || !editor.toolbars.length) {
38703             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38704         }
38705         
38706         for (var i =0 ; i < editor.toolbars.length;i++) {
38707             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38708             editor.toolbars[i].init(editor);
38709         }
38710          
38711         
38712     },
38713
38714     /**
38715      * Protected method that will not generally be called directly. It
38716      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38717      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38718      */
38719     getDocMarkup : function(){
38720         // body styles..
38721         var st = '';
38722         if (this.stylesheets === false) {
38723             
38724             Roo.get(document.head).select('style').each(function(node) {
38725                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38726             });
38727             
38728             Roo.get(document.head).select('link').each(function(node) { 
38729                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38730             });
38731             
38732         } else if (!this.stylesheets.length) {
38733                 // simple..
38734                 st = '<style type="text/css">' +
38735                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38736                    '</style>';
38737         } else {
38738             Roo.each(this.stylesheets, function(s) {
38739                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38740             });
38741             
38742         }
38743         
38744         return '<html><head>' + st  +
38745             //<style type="text/css">' +
38746             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38747             //'</style>' +
38748             ' </head><body></body></html>';
38749     },
38750
38751     // private
38752     onRender : function(ct, position)
38753     {
38754         var _t = this;
38755         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38756         this.el.dom.style.border = '0 none';
38757         this.el.dom.setAttribute('tabIndex', -1);
38758         this.el.addClass('x-hidden');
38759         if(Roo.isIE){ // fix IE 1px bogus margin
38760             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38761         }
38762         this.wrap = this.el.wrap({
38763             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38764         });
38765         
38766         if (this.resizable) {
38767             this.resizeEl = new Roo.Resizable(this.wrap, {
38768                 pinned : true,
38769                 wrap: true,
38770                 dynamic : true,
38771                 minHeight : this.height,
38772                 height: this.height,
38773                 handles : this.resizable,
38774                 width: this.width,
38775                 listeners : {
38776                     resize : function(r, w, h) {
38777                         _t.onResize(w,h); // -something
38778                     }
38779                 }
38780             });
38781             
38782         }
38783
38784         this.frameId = Roo.id();
38785         
38786         this.createToolbar(this);
38787         
38788       
38789         
38790         var iframe = this.wrap.createChild({
38791             tag: 'iframe',
38792             id: this.frameId,
38793             name: this.frameId,
38794             frameBorder : 'no',
38795             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38796         }, this.el
38797         );
38798         
38799        // console.log(iframe);
38800         //this.wrap.dom.appendChild(iframe);
38801
38802         this.iframe = iframe.dom;
38803
38804          this.assignDocWin();
38805         
38806         this.doc.designMode = 'on';
38807        
38808         this.doc.open();
38809         this.doc.write(this.getDocMarkup());
38810         this.doc.close();
38811
38812         
38813         var task = { // must defer to wait for browser to be ready
38814             run : function(){
38815                 //console.log("run task?" + this.doc.readyState);
38816                 this.assignDocWin();
38817                 if(this.doc.body || this.doc.readyState == 'complete'){
38818                     try {
38819                         this.doc.designMode="on";
38820                     } catch (e) {
38821                         return;
38822                     }
38823                     Roo.TaskMgr.stop(task);
38824                     this.initEditor.defer(10, this);
38825                 }
38826             },
38827             interval : 10,
38828             duration:10000,
38829             scope: this
38830         };
38831         Roo.TaskMgr.start(task);
38832
38833         if(!this.width){
38834             this.setSize(this.wrap.getSize());
38835         }
38836         if (this.resizeEl) {
38837             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38838             // should trigger onReize..
38839         }
38840     },
38841
38842     // private
38843     onResize : function(w, h)
38844     {
38845         //Roo.log('resize: ' +w + ',' + h );
38846         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38847         if(this.el && this.iframe){
38848             if(typeof w == 'number'){
38849                 var aw = w - this.wrap.getFrameWidth('lr');
38850                 this.el.setWidth(this.adjustWidth('textarea', aw));
38851                 this.iframe.style.width = aw + 'px';
38852             }
38853             if(typeof h == 'number'){
38854                 var tbh = 0;
38855                 for (var i =0; i < this.toolbars.length;i++) {
38856                     // fixme - ask toolbars for heights?
38857                     tbh += this.toolbars[i].tb.el.getHeight();
38858                     if (this.toolbars[i].footer) {
38859                         tbh += this.toolbars[i].footer.el.getHeight();
38860                     }
38861                 }
38862                 
38863                 
38864                 
38865                 
38866                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38867                 ah -= 5; // knock a few pixes off for look..
38868                 this.el.setHeight(this.adjustWidth('textarea', ah));
38869                 this.iframe.style.height = ah + 'px';
38870                 if(this.doc){
38871                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38872                 }
38873             }
38874         }
38875     },
38876
38877     /**
38878      * Toggles the editor between standard and source edit mode.
38879      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38880      */
38881     toggleSourceEdit : function(sourceEditMode){
38882         
38883         this.sourceEditMode = sourceEditMode === true;
38884         
38885         if(this.sourceEditMode){
38886           
38887             this.syncValue();
38888             this.iframe.className = 'x-hidden';
38889             this.el.removeClass('x-hidden');
38890             this.el.dom.removeAttribute('tabIndex');
38891             this.el.focus();
38892         }else{
38893              
38894             this.pushValue();
38895             this.iframe.className = '';
38896             this.el.addClass('x-hidden');
38897             this.el.dom.setAttribute('tabIndex', -1);
38898             this.deferFocus();
38899         }
38900         this.setSize(this.wrap.getSize());
38901         this.fireEvent('editmodechange', this, this.sourceEditMode);
38902     },
38903
38904     // private used internally
38905     createLink : function(){
38906         var url = prompt(this.createLinkText, this.defaultLinkValue);
38907         if(url && url != 'http:/'+'/'){
38908             this.relayCmd('createlink', url);
38909         }
38910     },
38911
38912     // private (for BoxComponent)
38913     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38914
38915     // private (for BoxComponent)
38916     getResizeEl : function(){
38917         return this.wrap;
38918     },
38919
38920     // private (for BoxComponent)
38921     getPositionEl : function(){
38922         return this.wrap;
38923     },
38924
38925     // private
38926     initEvents : function(){
38927         this.originalValue = this.getValue();
38928     },
38929
38930     /**
38931      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38932      * @method
38933      */
38934     markInvalid : Roo.emptyFn,
38935     /**
38936      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38937      * @method
38938      */
38939     clearInvalid : Roo.emptyFn,
38940
38941     setValue : function(v){
38942         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38943         this.pushValue();
38944     },
38945
38946     /**
38947      * Protected method that will not generally be called directly. If you need/want
38948      * custom HTML cleanup, this is the method you should override.
38949      * @param {String} html The HTML to be cleaned
38950      * return {String} The cleaned HTML
38951      */
38952     cleanHtml : function(html){
38953         html = String(html);
38954         if(html.length > 5){
38955             if(Roo.isSafari){ // strip safari nonsense
38956                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38957             }
38958         }
38959         if(html == '&nbsp;'){
38960             html = '';
38961         }
38962         return html;
38963     },
38964
38965     /**
38966      * Protected method that will not generally be called directly. Syncs the contents
38967      * of the editor iframe with the textarea.
38968      */
38969     syncValue : function(){
38970         if(this.initialized){
38971             var bd = (this.doc.body || this.doc.documentElement);
38972             this.cleanUpPaste();
38973             var html = bd.innerHTML;
38974             if(Roo.isSafari){
38975                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38976                 var m = bs.match(/text-align:(.*?);/i);
38977                 if(m && m[1]){
38978                     html = '<div style="'+m[0]+'">' + html + '</div>';
38979                 }
38980             }
38981             html = this.cleanHtml(html);
38982             if(this.fireEvent('beforesync', this, html) !== false){
38983                 this.el.dom.value = html;
38984                 this.fireEvent('sync', this, html);
38985             }
38986         }
38987     },
38988
38989     /**
38990      * Protected method that will not generally be called directly. Pushes the value of the textarea
38991      * into the iframe editor.
38992      */
38993     pushValue : function(){
38994         if(this.initialized){
38995             var v = this.el.dom.value;
38996             if(v.length < 1){
38997                 v = '&#160;';
38998             }
38999             
39000             if(this.fireEvent('beforepush', this, v) !== false){
39001                 var d = (this.doc.body || this.doc.documentElement);
39002                 d.innerHTML = v;
39003                 this.cleanUpPaste();
39004                 this.el.dom.value = d.innerHTML;
39005                 this.fireEvent('push', this, v);
39006             }
39007         }
39008     },
39009
39010     // private
39011     deferFocus : function(){
39012         this.focus.defer(10, this);
39013     },
39014
39015     // doc'ed in Field
39016     focus : function(){
39017         if(this.win && !this.sourceEditMode){
39018             this.win.focus();
39019         }else{
39020             this.el.focus();
39021         }
39022     },
39023     
39024     assignDocWin: function()
39025     {
39026         var iframe = this.iframe;
39027         
39028          if(Roo.isIE){
39029             this.doc = iframe.contentWindow.document;
39030             this.win = iframe.contentWindow;
39031         } else {
39032             if (!Roo.get(this.frameId)) {
39033                 return;
39034             }
39035             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39036             this.win = Roo.get(this.frameId).dom.contentWindow;
39037         }
39038     },
39039     
39040     // private
39041     initEditor : function(){
39042         //console.log("INIT EDITOR");
39043         this.assignDocWin();
39044         
39045         
39046         
39047         this.doc.designMode="on";
39048         this.doc.open();
39049         this.doc.write(this.getDocMarkup());
39050         this.doc.close();
39051         
39052         var dbody = (this.doc.body || this.doc.documentElement);
39053         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39054         // this copies styles from the containing element into thsi one..
39055         // not sure why we need all of this..
39056         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39057         ss['background-attachment'] = 'fixed'; // w3c
39058         dbody.bgProperties = 'fixed'; // ie
39059         Roo.DomHelper.applyStyles(dbody, ss);
39060         Roo.EventManager.on(this.doc, {
39061             //'mousedown': this.onEditorEvent,
39062             'mouseup': this.onEditorEvent,
39063             'dblclick': this.onEditorEvent,
39064             'click': this.onEditorEvent,
39065             'keyup': this.onEditorEvent,
39066             buffer:100,
39067             scope: this
39068         });
39069         if(Roo.isGecko){
39070             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39071         }
39072         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39073             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39074         }
39075         this.initialized = true;
39076
39077         this.fireEvent('initialize', this);
39078         this.pushValue();
39079     },
39080
39081     // private
39082     onDestroy : function(){
39083         
39084         
39085         
39086         if(this.rendered){
39087             
39088             for (var i =0; i < this.toolbars.length;i++) {
39089                 // fixme - ask toolbars for heights?
39090                 this.toolbars[i].onDestroy();
39091             }
39092             
39093             this.wrap.dom.innerHTML = '';
39094             this.wrap.remove();
39095         }
39096     },
39097
39098     // private
39099     onFirstFocus : function(){
39100         
39101         this.assignDocWin();
39102         
39103         
39104         this.activated = true;
39105         for (var i =0; i < this.toolbars.length;i++) {
39106             this.toolbars[i].onFirstFocus();
39107         }
39108        
39109         if(Roo.isGecko){ // prevent silly gecko errors
39110             this.win.focus();
39111             var s = this.win.getSelection();
39112             if(!s.focusNode || s.focusNode.nodeType != 3){
39113                 var r = s.getRangeAt(0);
39114                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39115                 r.collapse(true);
39116                 this.deferFocus();
39117             }
39118             try{
39119                 this.execCmd('useCSS', true);
39120                 this.execCmd('styleWithCSS', false);
39121             }catch(e){}
39122         }
39123         this.fireEvent('activate', this);
39124     },
39125
39126     // private
39127     adjustFont: function(btn){
39128         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39129         //if(Roo.isSafari){ // safari
39130         //    adjust *= 2;
39131        // }
39132         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39133         if(Roo.isSafari){ // safari
39134             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39135             v =  (v < 10) ? 10 : v;
39136             v =  (v > 48) ? 48 : v;
39137             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39138             
39139         }
39140         
39141         
39142         v = Math.max(1, v+adjust);
39143         
39144         this.execCmd('FontSize', v  );
39145     },
39146
39147     onEditorEvent : function(e){
39148         this.fireEvent('editorevent', this, e);
39149       //  this.updateToolbar();
39150         this.syncValue();
39151     },
39152
39153     insertTag : function(tg)
39154     {
39155         // could be a bit smarter... -> wrap the current selected tRoo..
39156         
39157         this.execCmd("formatblock",   tg);
39158         
39159     },
39160     
39161     insertText : function(txt)
39162     {
39163         
39164         
39165         range = this.createRange();
39166         range.deleteContents();
39167                //alert(Sender.getAttribute('label'));
39168                
39169         range.insertNode(this.doc.createTextNode(txt));
39170     } ,
39171     
39172     // private
39173     relayBtnCmd : function(btn){
39174         this.relayCmd(btn.cmd);
39175     },
39176
39177     /**
39178      * Executes a Midas editor command on the editor document and performs necessary focus and
39179      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39180      * @param {String} cmd The Midas command
39181      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39182      */
39183     relayCmd : function(cmd, value){
39184         this.win.focus();
39185         this.execCmd(cmd, value);
39186         this.fireEvent('editorevent', this);
39187         //this.updateToolbar();
39188         this.deferFocus();
39189     },
39190
39191     /**
39192      * Executes a Midas editor command directly on the editor document.
39193      * For visual commands, you should use {@link #relayCmd} instead.
39194      * <b>This should only be called after the editor is initialized.</b>
39195      * @param {String} cmd The Midas command
39196      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39197      */
39198     execCmd : function(cmd, value){
39199         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39200         this.syncValue();
39201     },
39202
39203    
39204     /**
39205      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39206      * to insert tRoo.
39207      * @param {String} text
39208      */
39209     insertAtCursor : function(text){
39210         if(!this.activated){
39211             return;
39212         }
39213         if(Roo.isIE){
39214             this.win.focus();
39215             var r = this.doc.selection.createRange();
39216             if(r){
39217                 r.collapse(true);
39218                 r.pasteHTML(text);
39219                 this.syncValue();
39220                 this.deferFocus();
39221             }
39222         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39223             this.win.focus();
39224             this.execCmd('InsertHTML', text);
39225             this.deferFocus();
39226         }
39227     },
39228  // private
39229     mozKeyPress : function(e){
39230         if(e.ctrlKey){
39231             var c = e.getCharCode(), cmd;
39232           
39233             if(c > 0){
39234                 c = String.fromCharCode(c).toLowerCase();
39235                 switch(c){
39236                     case 'b':
39237                         cmd = 'bold';
39238                     break;
39239                     case 'i':
39240                         cmd = 'italic';
39241                     break;
39242                     case 'u':
39243                         cmd = 'underline';
39244                     case 'v':
39245                         this.cleanUpPaste.defer(100, this);
39246                         return;
39247                     break;
39248                 }
39249                 if(cmd){
39250                     this.win.focus();
39251                     this.execCmd(cmd);
39252                     this.deferFocus();
39253                     e.preventDefault();
39254                 }
39255                 
39256             }
39257         }
39258     },
39259
39260     // private
39261     fixKeys : function(){ // load time branching for fastest keydown performance
39262         if(Roo.isIE){
39263             return function(e){
39264                 var k = e.getKey(), r;
39265                 if(k == e.TAB){
39266                     e.stopEvent();
39267                     r = this.doc.selection.createRange();
39268                     if(r){
39269                         r.collapse(true);
39270                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39271                         this.deferFocus();
39272                     }
39273                     return;
39274                 }
39275                 
39276                 if(k == e.ENTER){
39277                     r = this.doc.selection.createRange();
39278                     if(r){
39279                         var target = r.parentElement();
39280                         if(!target || target.tagName.toLowerCase() != 'li'){
39281                             e.stopEvent();
39282                             r.pasteHTML('<br />');
39283                             r.collapse(false);
39284                             r.select();
39285                         }
39286                     }
39287                 }
39288                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39289                     this.cleanUpPaste.defer(100, this);
39290                     return;
39291                 }
39292                 
39293                 
39294             };
39295         }else if(Roo.isOpera){
39296             return function(e){
39297                 var k = e.getKey();
39298                 if(k == e.TAB){
39299                     e.stopEvent();
39300                     this.win.focus();
39301                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39302                     this.deferFocus();
39303                 }
39304                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39305                     this.cleanUpPaste.defer(100, this);
39306                     return;
39307                 }
39308                 
39309             };
39310         }else if(Roo.isSafari){
39311             return function(e){
39312                 var k = e.getKey();
39313                 
39314                 if(k == e.TAB){
39315                     e.stopEvent();
39316                     this.execCmd('InsertText','\t');
39317                     this.deferFocus();
39318                     return;
39319                 }
39320                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39321                     this.cleanUpPaste.defer(100, this);
39322                     return;
39323                 }
39324                 
39325              };
39326         }
39327     }(),
39328     
39329     getAllAncestors: function()
39330     {
39331         var p = this.getSelectedNode();
39332         var a = [];
39333         if (!p) {
39334             a.push(p); // push blank onto stack..
39335             p = this.getParentElement();
39336         }
39337         
39338         
39339         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39340             a.push(p);
39341             p = p.parentNode;
39342         }
39343         a.push(this.doc.body);
39344         return a;
39345     },
39346     lastSel : false,
39347     lastSelNode : false,
39348     
39349     
39350     getSelection : function() 
39351     {
39352         this.assignDocWin();
39353         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39354     },
39355     
39356     getSelectedNode: function() 
39357     {
39358         // this may only work on Gecko!!!
39359         
39360         // should we cache this!!!!
39361         
39362         
39363         
39364          
39365         var range = this.createRange(this.getSelection()).cloneRange();
39366         
39367         if (Roo.isIE) {
39368             var parent = range.parentElement();
39369             while (true) {
39370                 var testRange = range.duplicate();
39371                 testRange.moveToElementText(parent);
39372                 if (testRange.inRange(range)) {
39373                     break;
39374                 }
39375                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39376                     break;
39377                 }
39378                 parent = parent.parentElement;
39379             }
39380             return parent;
39381         }
39382         
39383         // is ancestor a text element.
39384         var ac =  range.commonAncestorContainer;
39385         if (ac.nodeType == 3) {
39386             ac = ac.parentNode;
39387         }
39388         
39389         var ar = ac.childNodes;
39390          
39391         var nodes = [];
39392         var other_nodes = [];
39393         var has_other_nodes = false;
39394         for (var i=0;i<ar.length;i++) {
39395             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39396                 continue;
39397             }
39398             // fullly contained node.
39399             
39400             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39401                 nodes.push(ar[i]);
39402                 continue;
39403             }
39404             
39405             // probably selected..
39406             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39407                 other_nodes.push(ar[i]);
39408                 continue;
39409             }
39410             // outer..
39411             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39412                 continue;
39413             }
39414             
39415             
39416             has_other_nodes = true;
39417         }
39418         if (!nodes.length && other_nodes.length) {
39419             nodes= other_nodes;
39420         }
39421         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39422             return false;
39423         }
39424         
39425         return nodes[0];
39426     },
39427     createRange: function(sel)
39428     {
39429         // this has strange effects when using with 
39430         // top toolbar - not sure if it's a great idea.
39431         //this.editor.contentWindow.focus();
39432         if (typeof sel != "undefined") {
39433             try {
39434                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39435             } catch(e) {
39436                 return this.doc.createRange();
39437             }
39438         } else {
39439             return this.doc.createRange();
39440         }
39441     },
39442     getParentElement: function()
39443     {
39444         
39445         this.assignDocWin();
39446         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39447         
39448         var range = this.createRange(sel);
39449          
39450         try {
39451             var p = range.commonAncestorContainer;
39452             while (p.nodeType == 3) { // text node
39453                 p = p.parentNode;
39454             }
39455             return p;
39456         } catch (e) {
39457             return null;
39458         }
39459     
39460     },
39461     /***
39462      *
39463      * Range intersection.. the hard stuff...
39464      *  '-1' = before
39465      *  '0' = hits..
39466      *  '1' = after.
39467      *         [ -- selected range --- ]
39468      *   [fail]                        [fail]
39469      *
39470      *    basically..
39471      *      if end is before start or  hits it. fail.
39472      *      if start is after end or hits it fail.
39473      *
39474      *   if either hits (but other is outside. - then it's not 
39475      *   
39476      *    
39477      **/
39478     
39479     
39480     // @see http://www.thismuchiknow.co.uk/?p=64.
39481     rangeIntersectsNode : function(range, node)
39482     {
39483         var nodeRange = node.ownerDocument.createRange();
39484         try {
39485             nodeRange.selectNode(node);
39486         } catch (e) {
39487             nodeRange.selectNodeContents(node);
39488         }
39489     
39490         var rangeStartRange = range.cloneRange();
39491         rangeStartRange.collapse(true);
39492     
39493         var rangeEndRange = range.cloneRange();
39494         rangeEndRange.collapse(false);
39495     
39496         var nodeStartRange = nodeRange.cloneRange();
39497         nodeStartRange.collapse(true);
39498     
39499         var nodeEndRange = nodeRange.cloneRange();
39500         nodeEndRange.collapse(false);
39501     
39502         return rangeStartRange.compareBoundaryPoints(
39503                  Range.START_TO_START, nodeEndRange) == -1 &&
39504                rangeEndRange.compareBoundaryPoints(
39505                  Range.START_TO_START, nodeStartRange) == 1;
39506         
39507          
39508     },
39509     rangeCompareNode : function(range, node)
39510     {
39511         var nodeRange = node.ownerDocument.createRange();
39512         try {
39513             nodeRange.selectNode(node);
39514         } catch (e) {
39515             nodeRange.selectNodeContents(node);
39516         }
39517         
39518         
39519         range.collapse(true);
39520     
39521         nodeRange.collapse(true);
39522      
39523         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39524         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39525          
39526         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39527         
39528         var nodeIsBefore   =  ss == 1;
39529         var nodeIsAfter    = ee == -1;
39530         
39531         if (nodeIsBefore && nodeIsAfter)
39532             return 0; // outer
39533         if (!nodeIsBefore && nodeIsAfter)
39534             return 1; //right trailed.
39535         
39536         if (nodeIsBefore && !nodeIsAfter)
39537             return 2;  // left trailed.
39538         // fully contined.
39539         return 3;
39540     },
39541
39542     // private? - in a new class?
39543     cleanUpPaste :  function()
39544     {
39545         // cleans up the whole document..
39546       //  console.log('cleanuppaste');
39547         this.cleanUpChildren(this.doc.body);
39548         this.doc.body.innerHTML = this.cleanWordChars(this.doc.body.innerHTML);
39549         
39550     },
39551     
39552     cleanWordChars : function(input) {
39553         var he = Roo.form.HtmlEditor;
39554     
39555         var output = input;
39556         Roo.each(he.swapCodes, function(sw) { 
39557         
39558             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39559             output = output.replace(swapper, sw[1]);
39560         });
39561         return output;
39562     },
39563     
39564     
39565     cleanUpChildren : function (n)
39566     {
39567         if (!n.childNodes.length) {
39568             return;
39569         }
39570         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39571            this.cleanUpChild(n.childNodes[i]);
39572         }
39573     },
39574     
39575     
39576         
39577     
39578     cleanUpChild : function (node)
39579     {
39580         //console.log(node);
39581         if (node.nodeName == "#text") {
39582             // clean up silly Windows -- stuff?
39583             return; 
39584         }
39585         if (node.nodeName == "#comment") {
39586             node.parentNode.removeChild(node);
39587             // clean up silly Windows -- stuff?
39588             return; 
39589         }
39590         
39591         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39592             // remove node.
39593             node.parentNode.removeChild(node);
39594             return;
39595             
39596         }
39597         
39598         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39599         
39600         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39601         
39602         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39603             remove_keep_children = true;
39604         }
39605         
39606         if (remove_keep_children) {
39607             this.cleanUpChildren(node);
39608             // inserts everything just before this node...
39609             while (node.childNodes.length) {
39610                 var cn = node.childNodes[0];
39611                 node.removeChild(cn);
39612                 node.parentNode.insertBefore(cn, node);
39613             }
39614             node.parentNode.removeChild(node);
39615             return;
39616         }
39617         
39618         if (!node.attributes || !node.attributes.length) {
39619             this.cleanUpChildren(node);
39620             return;
39621         }
39622         
39623         function cleanAttr(n,v)
39624         {
39625             
39626             if (v.match(/^\./) || v.match(/^\//)) {
39627                 return;
39628             }
39629             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39630                 return;
39631             }
39632             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39633             node.removeAttribute(n);
39634             
39635         }
39636         
39637         function cleanStyle(n,v)
39638         {
39639             if (v.match(/expression/)) { //XSS?? should we even bother..
39640                 node.removeAttribute(n);
39641                 return;
39642             }
39643             
39644             
39645             var parts = v.split(/;/);
39646             Roo.each(parts, function(p) {
39647                 p = p.replace(/\s+/g,'');
39648                 if (!p.length) {
39649                     return true;
39650                 }
39651                 var l = p.split(':').shift().replace(/\s+/g,'');
39652                 
39653                 // only allow 'c whitelisted system attributes'
39654                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39655                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39656                     node.removeAttribute(n);
39657                     return false;
39658                 }
39659                 return true;
39660             });
39661             
39662             
39663         }
39664         
39665         
39666         for (var i = node.attributes.length-1; i > -1 ; i--) {
39667             var a = node.attributes[i];
39668             //console.log(a);
39669             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39670                 node.removeAttribute(a.name);
39671                 return;
39672             }
39673             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39674                 cleanAttr(a.name,a.value); // fixme..
39675                 return;
39676             }
39677             if (a.name == 'style') {
39678                 cleanStyle(a.name,a.value);
39679             }
39680             /// clean up MS crap..
39681             // tecnically this should be a list of valid class'es..
39682             
39683             
39684             if (a.name == 'class') {
39685                 if (a.value.match(/^Mso/)) {
39686                     node.className = '';
39687                 }
39688                 
39689                 if (a.value.match(/body/)) {
39690                     node.className = '';
39691                 }
39692             }
39693             
39694             // style cleanup!?
39695             // class cleanup?
39696             
39697         }
39698         
39699         
39700         this.cleanUpChildren(node);
39701         
39702         
39703     }
39704     
39705     
39706     // hide stuff that is not compatible
39707     /**
39708      * @event blur
39709      * @hide
39710      */
39711     /**
39712      * @event change
39713      * @hide
39714      */
39715     /**
39716      * @event focus
39717      * @hide
39718      */
39719     /**
39720      * @event specialkey
39721      * @hide
39722      */
39723     /**
39724      * @cfg {String} fieldClass @hide
39725      */
39726     /**
39727      * @cfg {String} focusClass @hide
39728      */
39729     /**
39730      * @cfg {String} autoCreate @hide
39731      */
39732     /**
39733      * @cfg {String} inputType @hide
39734      */
39735     /**
39736      * @cfg {String} invalidClass @hide
39737      */
39738     /**
39739      * @cfg {String} invalidText @hide
39740      */
39741     /**
39742      * @cfg {String} msgFx @hide
39743      */
39744     /**
39745      * @cfg {String} validateOnBlur @hide
39746      */
39747 });
39748
39749 Roo.form.HtmlEditor.white = [
39750         'area', 'br', 'img', 'input', 'hr', 'wbr',
39751         
39752        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39753        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39754        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39755        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39756        'table',   'ul',         'xmp', 
39757        
39758        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39759       'thead',   'tr', 
39760      
39761       'dir', 'menu', 'ol', 'ul', 'dl',
39762        
39763       'embed',  'object'
39764 ];
39765
39766
39767 Roo.form.HtmlEditor.black = [
39768     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39769         'applet', // 
39770         'base',   'basefont', 'bgsound', 'blink',  'body', 
39771         'frame',  'frameset', 'head',    'html',   'ilayer', 
39772         'iframe', 'layer',  'link',     'meta',    'object',   
39773         'script', 'style' ,'title',  'xml' // clean later..
39774 ];
39775 Roo.form.HtmlEditor.clean = [
39776     'script', 'style', 'title', 'xml'
39777 ];
39778 Roo.form.HtmlEditor.remove = [
39779     'font'
39780 ];
39781 // attributes..
39782
39783 Roo.form.HtmlEditor.ablack = [
39784     'on'
39785 ];
39786     
39787 Roo.form.HtmlEditor.aclean = [ 
39788     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39789 ];
39790
39791 // protocols..
39792 Roo.form.HtmlEditor.pwhite= [
39793         'http',  'https',  'mailto'
39794 ];
39795
39796 // white listed style attributes.
39797 Roo.form.HtmlEditor.cwhite= [
39798         'text-align',
39799         'font-size'
39800 ];
39801
39802
39803 Roo.form.HtmlEditor.swapCodes   =[ 
39804     [    8211, "--" ], 
39805     [    8212, "--" ], 
39806     [    8216,  "'" ],  
39807     [    8217, "'" ],  
39808     [    8220, '"' ],  
39809     [    8221, '"' ],  
39810     [    8226, "*" ],  
39811     [    8230, "..." ]
39812 ]; 
39813
39814     // <script type="text/javascript">
39815 /*
39816  * Based on
39817  * Ext JS Library 1.1.1
39818  * Copyright(c) 2006-2007, Ext JS, LLC.
39819  *  
39820  
39821  */
39822
39823 /**
39824  * @class Roo.form.HtmlEditorToolbar1
39825  * Basic Toolbar
39826  * 
39827  * Usage:
39828  *
39829  new Roo.form.HtmlEditor({
39830     ....
39831     toolbars : [
39832         new Roo.form.HtmlEditorToolbar1({
39833             disable : { fonts: 1 , format: 1, ..., ... , ...],
39834             btns : [ .... ]
39835         })
39836     }
39837      
39838  * 
39839  * @cfg {Object} disable List of elements to disable..
39840  * @cfg {Array} btns List of additional buttons.
39841  * 
39842  * 
39843  * NEEDS Extra CSS? 
39844  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39845  */
39846  
39847 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39848 {
39849     
39850     Roo.apply(this, config);
39851     
39852     // default disabled, based on 'good practice'..
39853     this.disable = this.disable || {};
39854     Roo.applyIf(this.disable, {
39855         fontSize : true,
39856         colors : true,
39857         specialElements : true
39858     });
39859     
39860     
39861     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39862     // dont call parent... till later.
39863 }
39864
39865 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39866     
39867     tb: false,
39868     
39869     rendered: false,
39870     
39871     editor : false,
39872     /**
39873      * @cfg {Object} disable  List of toolbar elements to disable
39874          
39875      */
39876     disable : false,
39877       /**
39878      * @cfg {Array} fontFamilies An array of available font families
39879      */
39880     fontFamilies : [
39881         'Arial',
39882         'Courier New',
39883         'Tahoma',
39884         'Times New Roman',
39885         'Verdana'
39886     ],
39887     
39888     specialChars : [
39889            "&#169;",
39890           "&#174;",     
39891           "&#8482;",    
39892           "&#163;" ,    
39893          // "&#8212;",    
39894           "&#8230;",    
39895           "&#247;" ,    
39896         //  "&#225;" ,     ?? a acute?
39897            "&#8364;"    , //Euro
39898        //   "&#8220;"    ,
39899         //  "&#8221;"    ,
39900         //  "&#8226;"    ,
39901           "&#176;"  //   , // degrees
39902
39903          // "&#233;"     , // e ecute
39904          // "&#250;"     , // u ecute?
39905     ],
39906     
39907     specialElements : [
39908         {
39909             text: "Insert Table",
39910             xtype: 'MenuItem',
39911             xns : Roo.Menu,
39912             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39913                 
39914         },
39915         {    
39916             text: "Insert Image",
39917             xtype: 'MenuItem',
39918             xns : Roo.Menu,
39919             ihtml : '<img src="about:blank"/>'
39920             
39921         }
39922         
39923          
39924     ],
39925     
39926     
39927     inputElements : [ 
39928             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39929             "input:submit", "input:button", "select", "textarea", "label" ],
39930     formats : [
39931         ["p"] ,  
39932         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39933         ["pre"],[ "code"], 
39934         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39935     ],
39936      /**
39937      * @cfg {String} defaultFont default font to use.
39938      */
39939     defaultFont: 'tahoma',
39940    
39941     fontSelect : false,
39942     
39943     
39944     formatCombo : false,
39945     
39946     init : function(editor)
39947     {
39948         this.editor = editor;
39949         
39950         
39951         var fid = editor.frameId;
39952         var etb = this;
39953         function btn(id, toggle, handler){
39954             var xid = fid + '-'+ id ;
39955             return {
39956                 id : xid,
39957                 cmd : id,
39958                 cls : 'x-btn-icon x-edit-'+id,
39959                 enableToggle:toggle !== false,
39960                 scope: editor, // was editor...
39961                 handler:handler||editor.relayBtnCmd,
39962                 clickEvent:'mousedown',
39963                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39964                 tabIndex:-1
39965             };
39966         }
39967         
39968         
39969         
39970         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39971         this.tb = tb;
39972          // stop form submits
39973         tb.el.on('click', function(e){
39974             e.preventDefault(); // what does this do?
39975         });
39976
39977         if(!this.disable.font && !Roo.isSafari){
39978             /* why no safari for fonts
39979             editor.fontSelect = tb.el.createChild({
39980                 tag:'select',
39981                 tabIndex: -1,
39982                 cls:'x-font-select',
39983                 html: editor.createFontOptions()
39984             });
39985             editor.fontSelect.on('change', function(){
39986                 var font = editor.fontSelect.dom.value;
39987                 editor.relayCmd('fontname', font);
39988                 editor.deferFocus();
39989             }, editor);
39990             tb.add(
39991                 editor.fontSelect.dom,
39992                 '-'
39993             );
39994             */
39995         };
39996         if(!this.disable.formats){
39997             this.formatCombo = new Roo.form.ComboBox({
39998                 store: new Roo.data.SimpleStore({
39999                     id : 'tag',
40000                     fields: ['tag'],
40001                     data : this.formats // from states.js
40002                 }),
40003                 blockFocus : true,
40004                 //autoCreate : {tag: "div",  size: "20"},
40005                 displayField:'tag',
40006                 typeAhead: false,
40007                 mode: 'local',
40008                 editable : false,
40009                 triggerAction: 'all',
40010                 emptyText:'Add tag',
40011                 selectOnFocus:true,
40012                 width:135,
40013                 listeners : {
40014                     'select': function(c, r, i) {
40015                         editor.insertTag(r.get('tag'));
40016                         editor.focus();
40017                     }
40018                 }
40019
40020             });
40021             tb.addField(this.formatCombo);
40022             
40023         }
40024         
40025         if(!this.disable.format){
40026             tb.add(
40027                 btn('bold'),
40028                 btn('italic'),
40029                 btn('underline')
40030             );
40031         };
40032         if(!this.disable.fontSize){
40033             tb.add(
40034                 '-',
40035                 
40036                 
40037                 btn('increasefontsize', false, editor.adjustFont),
40038                 btn('decreasefontsize', false, editor.adjustFont)
40039             );
40040         };
40041         
40042         
40043         if(!this.disable.colors){
40044             tb.add(
40045                 '-', {
40046                     id:editor.frameId +'-forecolor',
40047                     cls:'x-btn-icon x-edit-forecolor',
40048                     clickEvent:'mousedown',
40049                     tooltip: this.buttonTips['forecolor'] || undefined,
40050                     tabIndex:-1,
40051                     menu : new Roo.menu.ColorMenu({
40052                         allowReselect: true,
40053                         focus: Roo.emptyFn,
40054                         value:'000000',
40055                         plain:true,
40056                         selectHandler: function(cp, color){
40057                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40058                             editor.deferFocus();
40059                         },
40060                         scope: editor,
40061                         clickEvent:'mousedown'
40062                     })
40063                 }, {
40064                     id:editor.frameId +'backcolor',
40065                     cls:'x-btn-icon x-edit-backcolor',
40066                     clickEvent:'mousedown',
40067                     tooltip: this.buttonTips['backcolor'] || undefined,
40068                     tabIndex:-1,
40069                     menu : new Roo.menu.ColorMenu({
40070                         focus: Roo.emptyFn,
40071                         value:'FFFFFF',
40072                         plain:true,
40073                         allowReselect: true,
40074                         selectHandler: function(cp, color){
40075                             if(Roo.isGecko){
40076                                 editor.execCmd('useCSS', false);
40077                                 editor.execCmd('hilitecolor', color);
40078                                 editor.execCmd('useCSS', true);
40079                                 editor.deferFocus();
40080                             }else{
40081                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40082                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40083                                 editor.deferFocus();
40084                             }
40085                         },
40086                         scope:editor,
40087                         clickEvent:'mousedown'
40088                     })
40089                 }
40090             );
40091         };
40092         // now add all the items...
40093         
40094
40095         if(!this.disable.alignments){
40096             tb.add(
40097                 '-',
40098                 btn('justifyleft'),
40099                 btn('justifycenter'),
40100                 btn('justifyright')
40101             );
40102         };
40103
40104         //if(!Roo.isSafari){
40105             if(!this.disable.links){
40106                 tb.add(
40107                     '-',
40108                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40109                 );
40110             };
40111
40112             if(!this.disable.lists){
40113                 tb.add(
40114                     '-',
40115                     btn('insertorderedlist'),
40116                     btn('insertunorderedlist')
40117                 );
40118             }
40119             if(!this.disable.sourceEdit){
40120                 tb.add(
40121                     '-',
40122                     btn('sourceedit', true, function(btn){
40123                         this.toggleSourceEdit(btn.pressed);
40124                     })
40125                 );
40126             }
40127         //}
40128         
40129         var smenu = { };
40130         // special menu.. - needs to be tidied up..
40131         if (!this.disable.special) {
40132             smenu = {
40133                 text: "&#169;",
40134                 cls: 'x-edit-none',
40135                 
40136                 menu : {
40137                     items : []
40138                 }
40139             };
40140             for (var i =0; i < this.specialChars.length; i++) {
40141                 smenu.menu.items.push({
40142                     
40143                     html: this.specialChars[i],
40144                     handler: function(a,b) {
40145                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40146                         
40147                     },
40148                     tabIndex:-1
40149                 });
40150             }
40151             
40152             
40153             tb.add(smenu);
40154             
40155             
40156         }
40157          
40158         if (!this.disable.specialElements) {
40159             var semenu = {
40160                 text: "Other;",
40161                 cls: 'x-edit-none',
40162                 menu : {
40163                     items : []
40164                 }
40165             };
40166             for (var i =0; i < this.specialElements.length; i++) {
40167                 semenu.menu.items.push(
40168                     Roo.apply({ 
40169                         handler: function(a,b) {
40170                             editor.insertAtCursor(this.ihtml);
40171                         }
40172                     }, this.specialElements[i])
40173                 );
40174                     
40175             }
40176             
40177             tb.add(semenu);
40178             
40179             
40180         }
40181          
40182         
40183         if (this.btns) {
40184             for(var i =0; i< this.btns.length;i++) {
40185                 var b = this.btns[i];
40186                 b.cls =  'x-edit-none';
40187                 b.scope = editor;
40188                 tb.add(b);
40189             }
40190         
40191         }
40192         
40193         
40194         
40195         // disable everything...
40196         
40197         this.tb.items.each(function(item){
40198            if(item.id != editor.frameId+ '-sourceedit'){
40199                 item.disable();
40200             }
40201         });
40202         this.rendered = true;
40203         
40204         // the all the btns;
40205         editor.on('editorevent', this.updateToolbar, this);
40206         // other toolbars need to implement this..
40207         //editor.on('editmodechange', this.updateToolbar, this);
40208     },
40209     
40210     
40211     
40212     /**
40213      * Protected method that will not generally be called directly. It triggers
40214      * a toolbar update by reading the markup state of the current selection in the editor.
40215      */
40216     updateToolbar: function(){
40217
40218         if(!this.editor.activated){
40219             this.editor.onFirstFocus();
40220             return;
40221         }
40222
40223         var btns = this.tb.items.map, 
40224             doc = this.editor.doc,
40225             frameId = this.editor.frameId;
40226
40227         if(!this.disable.font && !Roo.isSafari){
40228             /*
40229             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40230             if(name != this.fontSelect.dom.value){
40231                 this.fontSelect.dom.value = name;
40232             }
40233             */
40234         }
40235         if(!this.disable.format){
40236             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40237             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40238             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40239         }
40240         if(!this.disable.alignments){
40241             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40242             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40243             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40244         }
40245         if(!Roo.isSafari && !this.disable.lists){
40246             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40247             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40248         }
40249         
40250         var ans = this.editor.getAllAncestors();
40251         if (this.formatCombo) {
40252             
40253             
40254             var store = this.formatCombo.store;
40255             this.formatCombo.setValue("");
40256             for (var i =0; i < ans.length;i++) {
40257                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40258                     // select it..
40259                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40260                     break;
40261                 }
40262             }
40263         }
40264         
40265         
40266         
40267         // hides menus... - so this cant be on a menu...
40268         Roo.menu.MenuMgr.hideAll();
40269
40270         //this.editorsyncValue();
40271     },
40272    
40273     
40274     createFontOptions : function(){
40275         var buf = [], fs = this.fontFamilies, ff, lc;
40276         for(var i = 0, len = fs.length; i< len; i++){
40277             ff = fs[i];
40278             lc = ff.toLowerCase();
40279             buf.push(
40280                 '<option value="',lc,'" style="font-family:',ff,';"',
40281                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40282                     ff,
40283                 '</option>'
40284             );
40285         }
40286         return buf.join('');
40287     },
40288     
40289     toggleSourceEdit : function(sourceEditMode){
40290         if(sourceEditMode === undefined){
40291             sourceEditMode = !this.sourceEditMode;
40292         }
40293         this.sourceEditMode = sourceEditMode === true;
40294         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40295         // just toggle the button?
40296         if(btn.pressed !== this.editor.sourceEditMode){
40297             btn.toggle(this.editor.sourceEditMode);
40298             return;
40299         }
40300         
40301         if(this.sourceEditMode){
40302             this.tb.items.each(function(item){
40303                 if(item.cmd != 'sourceedit'){
40304                     item.disable();
40305                 }
40306             });
40307           
40308         }else{
40309             if(this.initialized){
40310                 this.tb.items.each(function(item){
40311                     item.enable();
40312                 });
40313             }
40314             
40315         }
40316         // tell the editor that it's been pressed..
40317         this.editor.toggleSourceEdit(sourceEditMode);
40318        
40319     },
40320      /**
40321      * Object collection of toolbar tooltips for the buttons in the editor. The key
40322      * is the command id associated with that button and the value is a valid QuickTips object.
40323      * For example:
40324 <pre><code>
40325 {
40326     bold : {
40327         title: 'Bold (Ctrl+B)',
40328         text: 'Make the selected text bold.',
40329         cls: 'x-html-editor-tip'
40330     },
40331     italic : {
40332         title: 'Italic (Ctrl+I)',
40333         text: 'Make the selected text italic.',
40334         cls: 'x-html-editor-tip'
40335     },
40336     ...
40337 </code></pre>
40338     * @type Object
40339      */
40340     buttonTips : {
40341         bold : {
40342             title: 'Bold (Ctrl+B)',
40343             text: 'Make the selected text bold.',
40344             cls: 'x-html-editor-tip'
40345         },
40346         italic : {
40347             title: 'Italic (Ctrl+I)',
40348             text: 'Make the selected text italic.',
40349             cls: 'x-html-editor-tip'
40350         },
40351         underline : {
40352             title: 'Underline (Ctrl+U)',
40353             text: 'Underline the selected text.',
40354             cls: 'x-html-editor-tip'
40355         },
40356         increasefontsize : {
40357             title: 'Grow Text',
40358             text: 'Increase the font size.',
40359             cls: 'x-html-editor-tip'
40360         },
40361         decreasefontsize : {
40362             title: 'Shrink Text',
40363             text: 'Decrease the font size.',
40364             cls: 'x-html-editor-tip'
40365         },
40366         backcolor : {
40367             title: 'Text Highlight Color',
40368             text: 'Change the background color of the selected text.',
40369             cls: 'x-html-editor-tip'
40370         },
40371         forecolor : {
40372             title: 'Font Color',
40373             text: 'Change the color of the selected text.',
40374             cls: 'x-html-editor-tip'
40375         },
40376         justifyleft : {
40377             title: 'Align Text Left',
40378             text: 'Align text to the left.',
40379             cls: 'x-html-editor-tip'
40380         },
40381         justifycenter : {
40382             title: 'Center Text',
40383             text: 'Center text in the editor.',
40384             cls: 'x-html-editor-tip'
40385         },
40386         justifyright : {
40387             title: 'Align Text Right',
40388             text: 'Align text to the right.',
40389             cls: 'x-html-editor-tip'
40390         },
40391         insertunorderedlist : {
40392             title: 'Bullet List',
40393             text: 'Start a bulleted list.',
40394             cls: 'x-html-editor-tip'
40395         },
40396         insertorderedlist : {
40397             title: 'Numbered List',
40398             text: 'Start a numbered list.',
40399             cls: 'x-html-editor-tip'
40400         },
40401         createlink : {
40402             title: 'Hyperlink',
40403             text: 'Make the selected text a hyperlink.',
40404             cls: 'x-html-editor-tip'
40405         },
40406         sourceedit : {
40407             title: 'Source Edit',
40408             text: 'Switch to source editing mode.',
40409             cls: 'x-html-editor-tip'
40410         }
40411     },
40412     // private
40413     onDestroy : function(){
40414         if(this.rendered){
40415             
40416             this.tb.items.each(function(item){
40417                 if(item.menu){
40418                     item.menu.removeAll();
40419                     if(item.menu.el){
40420                         item.menu.el.destroy();
40421                     }
40422                 }
40423                 item.destroy();
40424             });
40425              
40426         }
40427     },
40428     onFirstFocus: function() {
40429         this.tb.items.each(function(item){
40430            item.enable();
40431         });
40432     }
40433 });
40434
40435
40436
40437
40438 // <script type="text/javascript">
40439 /*
40440  * Based on
40441  * Ext JS Library 1.1.1
40442  * Copyright(c) 2006-2007, Ext JS, LLC.
40443  *  
40444  
40445  */
40446
40447  
40448 /**
40449  * @class Roo.form.HtmlEditor.ToolbarContext
40450  * Context Toolbar
40451  * 
40452  * Usage:
40453  *
40454  new Roo.form.HtmlEditor({
40455     ....
40456     toolbars : [
40457         { xtype: 'ToolbarStandard', styles : {} }
40458         { xtype: 'ToolbarContext', disable : {} }
40459     ]
40460 })
40461
40462      
40463  * 
40464  * @config : {Object} disable List of elements to disable.. (not done yet.)
40465  * @config : {Object} styles  Map of styles available.
40466  * 
40467  */
40468
40469 Roo.form.HtmlEditor.ToolbarContext = function(config)
40470 {
40471     
40472     Roo.apply(this, config);
40473     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40474     // dont call parent... till later.
40475     this.styles = this.styles || {};
40476 }
40477 Roo.form.HtmlEditor.ToolbarContext.types = {
40478     'IMG' : {
40479         width : {
40480             title: "Width",
40481             width: 40
40482         },
40483         height:  {
40484             title: "Height",
40485             width: 40
40486         },
40487         align: {
40488             title: "Align",
40489             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40490             width : 80
40491             
40492         },
40493         border: {
40494             title: "Border",
40495             width: 40
40496         },
40497         alt: {
40498             title: "Alt",
40499             width: 120
40500         },
40501         src : {
40502             title: "Src",
40503             width: 220
40504         }
40505         
40506     },
40507     'A' : {
40508         name : {
40509             title: "Name",
40510             width: 50
40511         },
40512         href:  {
40513             title: "Href",
40514             width: 220
40515         } // border?
40516         
40517     },
40518     'TABLE' : {
40519         rows : {
40520             title: "Rows",
40521             width: 20
40522         },
40523         cols : {
40524             title: "Cols",
40525             width: 20
40526         },
40527         width : {
40528             title: "Width",
40529             width: 40
40530         },
40531         height : {
40532             title: "Height",
40533             width: 40
40534         },
40535         border : {
40536             title: "Border",
40537             width: 20
40538         }
40539     },
40540     'TD' : {
40541         width : {
40542             title: "Width",
40543             width: 40
40544         },
40545         height : {
40546             title: "Height",
40547             width: 40
40548         },   
40549         align: {
40550             title: "Align",
40551             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40552             width: 80
40553         },
40554         valign: {
40555             title: "Valign",
40556             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40557             width: 80
40558         },
40559         colspan: {
40560             title: "Colspan",
40561             width: 20
40562             
40563         }
40564     },
40565     'INPUT' : {
40566         name : {
40567             title: "name",
40568             width: 120
40569         },
40570         value : {
40571             title: "Value",
40572             width: 120
40573         },
40574         width : {
40575             title: "Width",
40576             width: 40
40577         }
40578     },
40579     'LABEL' : {
40580         'for' : {
40581             title: "For",
40582             width: 120
40583         }
40584     },
40585     'TEXTAREA' : {
40586           name : {
40587             title: "name",
40588             width: 120
40589         },
40590         rows : {
40591             title: "Rows",
40592             width: 20
40593         },
40594         cols : {
40595             title: "Cols",
40596             width: 20
40597         }
40598     },
40599     'SELECT' : {
40600         name : {
40601             title: "name",
40602             width: 120
40603         },
40604         selectoptions : {
40605             title: "Options",
40606             width: 200
40607         }
40608     },
40609     
40610     // should we really allow this??
40611     // should this just be 
40612     'BODY' : {
40613         title : {
40614             title: "title",
40615             width: 200,
40616             disabled : true
40617         }
40618     },
40619     '*' : {
40620         // empty..
40621     }
40622 };
40623
40624
40625
40626 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40627     
40628     tb: false,
40629     
40630     rendered: false,
40631     
40632     editor : false,
40633     /**
40634      * @cfg {Object} disable  List of toolbar elements to disable
40635          
40636      */
40637     disable : false,
40638     /**
40639      * @cfg {Object} styles List of styles 
40640      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40641      *
40642      * These must be defined in the page, so they get rendered correctly..
40643      * .headline { }
40644      * TD.underline { }
40645      * 
40646      */
40647     styles : false,
40648     
40649     
40650     
40651     toolbars : false,
40652     
40653     init : function(editor)
40654     {
40655         this.editor = editor;
40656         
40657         
40658         var fid = editor.frameId;
40659         var etb = this;
40660         function btn(id, toggle, handler){
40661             var xid = fid + '-'+ id ;
40662             return {
40663                 id : xid,
40664                 cmd : id,
40665                 cls : 'x-btn-icon x-edit-'+id,
40666                 enableToggle:toggle !== false,
40667                 scope: editor, // was editor...
40668                 handler:handler||editor.relayBtnCmd,
40669                 clickEvent:'mousedown',
40670                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40671                 tabIndex:-1
40672             };
40673         }
40674         // create a new element.
40675         var wdiv = editor.wrap.createChild({
40676                 tag: 'div'
40677             }, editor.wrap.dom.firstChild.nextSibling, true);
40678         
40679         // can we do this more than once??
40680         
40681          // stop form submits
40682       
40683  
40684         // disable everything...
40685         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40686         this.toolbars = {};
40687            
40688         for (var i in  ty) {
40689           
40690             this.toolbars[i] = this.buildToolbar(ty[i],i);
40691         }
40692         this.tb = this.toolbars.BODY;
40693         this.tb.el.show();
40694         this.buildFooter();
40695         this.footer.show();
40696          
40697         this.rendered = true;
40698         
40699         // the all the btns;
40700         editor.on('editorevent', this.updateToolbar, this);
40701         // other toolbars need to implement this..
40702         //editor.on('editmodechange', this.updateToolbar, this);
40703     },
40704     
40705     
40706     
40707     /**
40708      * Protected method that will not generally be called directly. It triggers
40709      * a toolbar update by reading the markup state of the current selection in the editor.
40710      */
40711     updateToolbar: function(ignore_a,ignore_b,sel){
40712
40713         
40714         if(!this.editor.activated){
40715              this.editor.onFirstFocus();
40716             return;
40717         }
40718         var updateFooter = sel ? false : true;
40719         
40720         
40721         var ans = this.editor.getAllAncestors();
40722         
40723         // pick
40724         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40725         
40726         if (!sel) { 
40727             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40728             sel = sel ? sel : this.editor.doc.body;
40729             sel = sel.tagName.length ? sel : this.editor.doc.body;
40730             
40731         }
40732         // pick a menu that exists..
40733         var tn = sel.tagName.toUpperCase();
40734         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40735         
40736         tn = sel.tagName.toUpperCase();
40737         
40738         var lastSel = this.tb.selectedNode
40739         
40740         this.tb.selectedNode = sel;
40741         
40742         // if current menu does not match..
40743         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40744                 
40745             this.tb.el.hide();
40746             ///console.log("show: " + tn);
40747             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40748             this.tb.el.show();
40749             // update name
40750             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40751             
40752             
40753             // update attributes
40754             if (this.tb.fields) {
40755                 this.tb.fields.each(function(e) {
40756                    e.setValue(sel.getAttribute(e.name));
40757                 });
40758             }
40759             
40760             // update styles
40761             var st = this.tb.fields.item(0);
40762             st.store.removeAll();
40763             var cn = sel.className.split(/\s+/);
40764             
40765             var avs = [];
40766             if (this.styles['*']) {
40767                 
40768                 Roo.each(this.styles['*'], function(v) {
40769                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40770                 });
40771             }
40772             if (this.styles[tn]) { 
40773                 Roo.each(this.styles[tn], function(v) {
40774                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40775                 });
40776             }
40777             
40778             st.store.loadData(avs);
40779             st.collapse();
40780             st.setValue(cn);
40781             
40782             // flag our selected Node.
40783             this.tb.selectedNode = sel;
40784            
40785            
40786             Roo.menu.MenuMgr.hideAll();
40787
40788         }
40789         
40790         if (!updateFooter) {
40791             return;
40792         }
40793         // update the footer
40794         //
40795         var html = '';
40796         
40797         this.footerEls = ans.reverse();
40798         Roo.each(this.footerEls, function(a,i) {
40799             if (!a) { return; }
40800             html += html.length ? ' &gt; '  :  '';
40801             
40802             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40803             
40804         });
40805        
40806         // 
40807         var sz = this.footDisp.up('td').getSize();
40808         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40809         this.footDisp.dom.style.marginLeft = '5px';
40810         
40811         this.footDisp.dom.style.overflow = 'hidden';
40812         
40813         this.footDisp.dom.innerHTML = html;
40814             
40815         //this.editorsyncValue();
40816     },
40817    
40818        
40819     // private
40820     onDestroy : function(){
40821         if(this.rendered){
40822             
40823             this.tb.items.each(function(item){
40824                 if(item.menu){
40825                     item.menu.removeAll();
40826                     if(item.menu.el){
40827                         item.menu.el.destroy();
40828                     }
40829                 }
40830                 item.destroy();
40831             });
40832              
40833         }
40834     },
40835     onFirstFocus: function() {
40836         // need to do this for all the toolbars..
40837         this.tb.items.each(function(item){
40838            item.enable();
40839         });
40840     },
40841     buildToolbar: function(tlist, nm)
40842     {
40843         var editor = this.editor;
40844          // create a new element.
40845         var wdiv = editor.wrap.createChild({
40846                 tag: 'div'
40847             }, editor.wrap.dom.firstChild.nextSibling, true);
40848         
40849        
40850         var tb = new Roo.Toolbar(wdiv);
40851         // add the name..
40852         
40853         tb.add(nm+ ":&nbsp;");
40854         
40855         // styles...
40856         if (this.styles) {
40857             
40858             // this needs a multi-select checkbox...
40859             tb.addField( new Roo.form.ComboBox({
40860                 store: new Roo.data.SimpleStore({
40861                     id : 'val',
40862                     fields: ['val', 'selected'],
40863                     data : [] 
40864                 }),
40865                 name : 'className',
40866                 displayField:'val',
40867                 typeAhead: false,
40868                 mode: 'local',
40869                 editable : false,
40870                 triggerAction: 'all',
40871                 emptyText:'Select Style',
40872                 selectOnFocus:true,
40873                 width: 130,
40874                 listeners : {
40875                     'select': function(c, r, i) {
40876                         // initial support only for on class per el..
40877                         tb.selectedNode.className =  r ? r.get('val') : '';
40878                     }
40879                 }
40880     
40881             }));
40882         }
40883             
40884         
40885         
40886         for (var i in tlist) {
40887             
40888             var item = tlist[i];
40889             tb.add(item.title + ":&nbsp;");
40890             
40891             
40892             
40893             
40894             if (item.opts) {
40895                 // opts == pulldown..
40896                 tb.addField( new Roo.form.ComboBox({
40897                     store: new Roo.data.SimpleStore({
40898                         id : 'val',
40899                         fields: ['val'],
40900                         data : item.opts  
40901                     }),
40902                     name : i,
40903                     displayField:'val',
40904                     typeAhead: false,
40905                     mode: 'local',
40906                     editable : false,
40907                     triggerAction: 'all',
40908                     emptyText:'Select',
40909                     selectOnFocus:true,
40910                     width: item.width ? item.width  : 130,
40911                     listeners : {
40912                         'select': function(c, r, i) {
40913                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40914                         }
40915                     }
40916
40917                 }));
40918                 continue;
40919                     
40920                  
40921                 
40922                 tb.addField( new Roo.form.TextField({
40923                     name: i,
40924                     width: 100,
40925                     //allowBlank:false,
40926                     value: ''
40927                 }));
40928                 continue;
40929             }
40930             tb.addField( new Roo.form.TextField({
40931                 name: i,
40932                 width: item.width,
40933                 //allowBlank:true,
40934                 value: '',
40935                 listeners: {
40936                     'change' : function(f, nv, ov) {
40937                         tb.selectedNode.setAttribute(f.name, nv);
40938                     }
40939                 }
40940             }));
40941              
40942         }
40943         tb.el.on('click', function(e){
40944             e.preventDefault(); // what does this do?
40945         });
40946         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40947         tb.el.hide();
40948         tb.name = nm;
40949         // dont need to disable them... as they will get hidden
40950         return tb;
40951          
40952         
40953     },
40954     buildFooter : function()
40955     {
40956         
40957         var fel = this.editor.wrap.createChild();
40958         this.footer = new Roo.Toolbar(fel);
40959         // toolbar has scrolly on left / right?
40960         var footDisp= new Roo.Toolbar.Fill();
40961         var _t = this;
40962         this.footer.add(
40963             {
40964                 text : '&lt;',
40965                 xtype: 'Button',
40966                 handler : function() {
40967                     _t.footDisp.scrollTo('left',0,true)
40968                 }
40969             }
40970         );
40971         this.footer.add( footDisp );
40972         this.footer.add( 
40973             {
40974                 text : '&gt;',
40975                 xtype: 'Button',
40976                 handler : function() {
40977                     // no animation..
40978                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40979                 }
40980             }
40981         );
40982         var fel = Roo.get(footDisp.el);
40983         fel.addClass('x-editor-context');
40984         this.footDispWrap = fel; 
40985         this.footDispWrap.overflow  = 'hidden';
40986         
40987         this.footDisp = fel.createChild();
40988         this.footDispWrap.on('click', this.onContextClick, this)
40989         
40990         
40991     },
40992     onContextClick : function (ev,dom)
40993     {
40994         ev.preventDefault();
40995         var  cn = dom.className;
40996         Roo.log(cn);
40997         if (!cn.match(/x-ed-loc-/)) {
40998             return;
40999         }
41000         var n = cn.split('-').pop();
41001         var ans = this.footerEls;
41002         var sel = ans[n];
41003         
41004          // pick
41005         var range = this.editor.createRange();
41006         
41007         range.selectNodeContents(sel);
41008         //range.selectNode(sel);
41009         
41010         
41011         var selection = this.editor.getSelection();
41012         selection.removeAllRanges();
41013         selection.addRange(range);
41014         
41015         
41016         
41017         this.updateToolbar(null, null, sel);
41018         
41019         
41020     }
41021     
41022     
41023     
41024     
41025     
41026 });
41027
41028
41029
41030
41031
41032 /*
41033  * Based on:
41034  * Ext JS Library 1.1.1
41035  * Copyright(c) 2006-2007, Ext JS, LLC.
41036  *
41037  * Originally Released Under LGPL - original licence link has changed is not relivant.
41038  *
41039  * Fork - LGPL
41040  * <script type="text/javascript">
41041  */
41042  
41043 /**
41044  * @class Roo.form.BasicForm
41045  * @extends Roo.util.Observable
41046  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41047  * @constructor
41048  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41049  * @param {Object} config Configuration options
41050  */
41051 Roo.form.BasicForm = function(el, config){
41052     this.allItems = [];
41053     this.childForms = [];
41054     Roo.apply(this, config);
41055     /*
41056      * The Roo.form.Field items in this form.
41057      * @type MixedCollection
41058      */
41059      
41060      
41061     this.items = new Roo.util.MixedCollection(false, function(o){
41062         return o.id || (o.id = Roo.id());
41063     });
41064     this.addEvents({
41065         /**
41066          * @event beforeaction
41067          * Fires before any action is performed. Return false to cancel the action.
41068          * @param {Form} this
41069          * @param {Action} action The action to be performed
41070          */
41071         beforeaction: true,
41072         /**
41073          * @event actionfailed
41074          * Fires when an action fails.
41075          * @param {Form} this
41076          * @param {Action} action The action that failed
41077          */
41078         actionfailed : true,
41079         /**
41080          * @event actioncomplete
41081          * Fires when an action is completed.
41082          * @param {Form} this
41083          * @param {Action} action The action that completed
41084          */
41085         actioncomplete : true
41086     });
41087     if(el){
41088         this.initEl(el);
41089     }
41090     Roo.form.BasicForm.superclass.constructor.call(this);
41091 };
41092
41093 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41094     /**
41095      * @cfg {String} method
41096      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41097      */
41098     /**
41099      * @cfg {DataReader} reader
41100      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41101      * This is optional as there is built-in support for processing JSON.
41102      */
41103     /**
41104      * @cfg {DataReader} errorReader
41105      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41106      * This is completely optional as there is built-in support for processing JSON.
41107      */
41108     /**
41109      * @cfg {String} url
41110      * The URL to use for form actions if one isn't supplied in the action options.
41111      */
41112     /**
41113      * @cfg {Boolean} fileUpload
41114      * Set to true if this form is a file upload.
41115      */
41116      
41117     /**
41118      * @cfg {Object} baseParams
41119      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41120      */
41121      /**
41122      
41123     /**
41124      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41125      */
41126     timeout: 30,
41127
41128     // private
41129     activeAction : null,
41130
41131     /**
41132      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41133      * or setValues() data instead of when the form was first created.
41134      */
41135     trackResetOnLoad : false,
41136     
41137     
41138     /**
41139      * childForms - used for multi-tab forms
41140      * @type {Array}
41141      */
41142     childForms : false,
41143     
41144     /**
41145      * allItems - full list of fields.
41146      * @type {Array}
41147      */
41148     allItems : false,
41149     
41150     /**
41151      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41152      * element by passing it or its id or mask the form itself by passing in true.
41153      * @type Mixed
41154      */
41155     waitMsgTarget : false,
41156
41157     // private
41158     initEl : function(el){
41159         this.el = Roo.get(el);
41160         this.id = this.el.id || Roo.id();
41161         this.el.on('submit', this.onSubmit, this);
41162         this.el.addClass('x-form');
41163     },
41164
41165     // private
41166     onSubmit : function(e){
41167         e.stopEvent();
41168     },
41169
41170     /**
41171      * Returns true if client-side validation on the form is successful.
41172      * @return Boolean
41173      */
41174     isValid : function(){
41175         var valid = true;
41176         this.items.each(function(f){
41177            if(!f.validate()){
41178                valid = false;
41179            }
41180         });
41181         return valid;
41182     },
41183
41184     /**
41185      * Returns true if any fields in this form have changed since their original load.
41186      * @return Boolean
41187      */
41188     isDirty : function(){
41189         var dirty = false;
41190         this.items.each(function(f){
41191            if(f.isDirty()){
41192                dirty = true;
41193                return false;
41194            }
41195         });
41196         return dirty;
41197     },
41198
41199     /**
41200      * Performs a predefined action (submit or load) or custom actions you define on this form.
41201      * @param {String} actionName The name of the action type
41202      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41203      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41204      * accept other config options):
41205      * <pre>
41206 Property          Type             Description
41207 ----------------  ---------------  ----------------------------------------------------------------------------------
41208 url               String           The url for the action (defaults to the form's url)
41209 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41210 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41211 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41212                                    validate the form on the client (defaults to false)
41213      * </pre>
41214      * @return {BasicForm} this
41215      */
41216     doAction : function(action, options){
41217         if(typeof action == 'string'){
41218             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41219         }
41220         if(this.fireEvent('beforeaction', this, action) !== false){
41221             this.beforeAction(action);
41222             action.run.defer(100, action);
41223         }
41224         return this;
41225     },
41226
41227     /**
41228      * Shortcut to do a submit action.
41229      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41230      * @return {BasicForm} this
41231      */
41232     submit : function(options){
41233         this.doAction('submit', options);
41234         return this;
41235     },
41236
41237     /**
41238      * Shortcut to do a load action.
41239      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41240      * @return {BasicForm} this
41241      */
41242     load : function(options){
41243         this.doAction('load', options);
41244         return this;
41245     },
41246
41247     /**
41248      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41249      * @param {Record} record The record to edit
41250      * @return {BasicForm} this
41251      */
41252     updateRecord : function(record){
41253         record.beginEdit();
41254         var fs = record.fields;
41255         fs.each(function(f){
41256             var field = this.findField(f.name);
41257             if(field){
41258                 record.set(f.name, field.getValue());
41259             }
41260         }, this);
41261         record.endEdit();
41262         return this;
41263     },
41264
41265     /**
41266      * Loads an Roo.data.Record into this form.
41267      * @param {Record} record The record to load
41268      * @return {BasicForm} this
41269      */
41270     loadRecord : function(record){
41271         this.setValues(record.data);
41272         return this;
41273     },
41274
41275     // private
41276     beforeAction : function(action){
41277         var o = action.options;
41278         
41279        
41280         if(this.waitMsgTarget === true){
41281             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41282         }else if(this.waitMsgTarget){
41283             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41284             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41285         }else {
41286             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41287         }
41288          
41289     },
41290
41291     // private
41292     afterAction : function(action, success){
41293         this.activeAction = null;
41294         var o = action.options;
41295         
41296         if(this.waitMsgTarget === true){
41297             this.el.unmask();
41298         }else if(this.waitMsgTarget){
41299             this.waitMsgTarget.unmask();
41300         }else{
41301             Roo.MessageBox.updateProgress(1);
41302             Roo.MessageBox.hide();
41303         }
41304          
41305         if(success){
41306             if(o.reset){
41307                 this.reset();
41308             }
41309             Roo.callback(o.success, o.scope, [this, action]);
41310             this.fireEvent('actioncomplete', this, action);
41311             
41312         }else{
41313             Roo.callback(o.failure, o.scope, [this, action]);
41314             // show an error message if no failed handler is set..
41315             if (!this.hasListener('actionfailed')) {
41316                 Roo.MessageBox.alert("Error",
41317                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41318                         action.result.errorMsg :
41319                         "Saving Failed, please check your entries"
41320                 );
41321             }
41322             
41323             this.fireEvent('actionfailed', this, action);
41324         }
41325         
41326     },
41327
41328     /**
41329      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41330      * @param {String} id The value to search for
41331      * @return Field
41332      */
41333     findField : function(id){
41334         var field = this.items.get(id);
41335         if(!field){
41336             this.items.each(function(f){
41337                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41338                     field = f;
41339                     return false;
41340                 }
41341             });
41342         }
41343         return field || null;
41344     },
41345
41346     /**
41347      * Add a secondary form to this one, 
41348      * Used to provide tabbed forms. One form is primary, with hidden values 
41349      * which mirror the elements from the other forms.
41350      * 
41351      * @param {Roo.form.Form} form to add.
41352      * 
41353      */
41354     addForm : function(form)
41355     {
41356        
41357         if (this.childForms.indexOf(form) > -1) {
41358             // already added..
41359             return;
41360         }
41361         this.childForms.push(form);
41362         var n = '';
41363         Roo.each(form.allItems, function (fe) {
41364             
41365             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41366             if (this.findField(n)) { // already added..
41367                 return;
41368             }
41369             var add = new Roo.form.Hidden({
41370                 name : n
41371             });
41372             add.render(this.el);
41373             
41374             this.add( add );
41375         }, this);
41376         
41377     },
41378     /**
41379      * Mark fields in this form invalid in bulk.
41380      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41381      * @return {BasicForm} this
41382      */
41383     markInvalid : function(errors){
41384         if(errors instanceof Array){
41385             for(var i = 0, len = errors.length; i < len; i++){
41386                 var fieldError = errors[i];
41387                 var f = this.findField(fieldError.id);
41388                 if(f){
41389                     f.markInvalid(fieldError.msg);
41390                 }
41391             }
41392         }else{
41393             var field, id;
41394             for(id in errors){
41395                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41396                     field.markInvalid(errors[id]);
41397                 }
41398             }
41399         }
41400         Roo.each(this.childForms || [], function (f) {
41401             f.markInvalid(errors);
41402         });
41403         
41404         return this;
41405     },
41406
41407     /**
41408      * Set values for fields in this form in bulk.
41409      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41410      * @return {BasicForm} this
41411      */
41412     setValues : function(values){
41413         if(values instanceof Array){ // array of objects
41414             for(var i = 0, len = values.length; i < len; i++){
41415                 var v = values[i];
41416                 var f = this.findField(v.id);
41417                 if(f){
41418                     f.setValue(v.value);
41419                     if(this.trackResetOnLoad){
41420                         f.originalValue = f.getValue();
41421                     }
41422                 }
41423             }
41424         }else{ // object hash
41425             var field, id;
41426             for(id in values){
41427                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41428                     
41429                     if (field.setFromData && 
41430                         field.valueField && 
41431                         field.displayField &&
41432                         // combos' with local stores can 
41433                         // be queried via setValue()
41434                         // to set their value..
41435                         (field.store && !field.store.isLocal)
41436                         ) {
41437                         // it's a combo
41438                         var sd = { };
41439                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41440                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41441                         field.setFromData(sd);
41442                         
41443                     } else {
41444                         field.setValue(values[id]);
41445                     }
41446                     
41447                     
41448                     if(this.trackResetOnLoad){
41449                         field.originalValue = field.getValue();
41450                     }
41451                 }
41452             }
41453         }
41454          
41455         Roo.each(this.childForms || [], function (f) {
41456             f.setValues(values);
41457         });
41458                 
41459         return this;
41460     },
41461
41462     /**
41463      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41464      * they are returned as an array.
41465      * @param {Boolean} asString
41466      * @return {Object}
41467      */
41468     getValues : function(asString){
41469         if (this.childForms) {
41470             // copy values from the child forms
41471             Roo.each(this.childForms, function (f) {
41472                 this.setValues(f.getValues());
41473             }, this);
41474         }
41475         
41476         
41477         
41478         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41479         if(asString === true){
41480             return fs;
41481         }
41482         return Roo.urlDecode(fs);
41483     },
41484     
41485     /**
41486      * Returns the fields in this form as an object with key/value pairs. 
41487      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41488      * @return {Object}
41489      */
41490     getFieldValues : function(with_hidden)
41491     {
41492         if (this.childForms) {
41493             // copy values from the child forms
41494             // should this call getFieldValues - probably not as we do not currently copy
41495             // hidden fields when we generate..
41496             Roo.each(this.childForms, function (f) {
41497                 this.setValues(f.getValues());
41498             }, this);
41499         }
41500         
41501         var ret = {};
41502         this.items.each(function(f){
41503             if (!f.getName()) {
41504                 return;
41505             }
41506             var v = f.getValue();
41507             // not sure if this supported any more..
41508             if ((typeof(v) == 'object') && f.getRawValue) {
41509                 v = f.getRawValue() ; // dates..
41510             }
41511             // combo boxes where name != hiddenName...
41512             if (f.name != f.getName()) {
41513                 ret[f.name] = f.getRawValue();
41514             }
41515             ret[f.getName()] = v;
41516         });
41517         
41518         return ret;
41519     },
41520
41521     /**
41522      * Clears all invalid messages in this form.
41523      * @return {BasicForm} this
41524      */
41525     clearInvalid : function(){
41526         this.items.each(function(f){
41527            f.clearInvalid();
41528         });
41529         
41530         Roo.each(this.childForms || [], function (f) {
41531             f.clearInvalid();
41532         });
41533         
41534         
41535         return this;
41536     },
41537
41538     /**
41539      * Resets this form.
41540      * @return {BasicForm} this
41541      */
41542     reset : function(){
41543         this.items.each(function(f){
41544             f.reset();
41545         });
41546         
41547         Roo.each(this.childForms || [], function (f) {
41548             f.reset();
41549         });
41550        
41551         
41552         return this;
41553     },
41554
41555     /**
41556      * Add Roo.form components to this form.
41557      * @param {Field} field1
41558      * @param {Field} field2 (optional)
41559      * @param {Field} etc (optional)
41560      * @return {BasicForm} this
41561      */
41562     add : function(){
41563         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41564         return this;
41565     },
41566
41567
41568     /**
41569      * Removes a field from the items collection (does NOT remove its markup).
41570      * @param {Field} field
41571      * @return {BasicForm} this
41572      */
41573     remove : function(field){
41574         this.items.remove(field);
41575         return this;
41576     },
41577
41578     /**
41579      * Looks at the fields in this form, checks them for an id attribute,
41580      * and calls applyTo on the existing dom element with that id.
41581      * @return {BasicForm} this
41582      */
41583     render : function(){
41584         this.items.each(function(f){
41585             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41586                 f.applyTo(f.id);
41587             }
41588         });
41589         return this;
41590     },
41591
41592     /**
41593      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41594      * @param {Object} values
41595      * @return {BasicForm} this
41596      */
41597     applyToFields : function(o){
41598         this.items.each(function(f){
41599            Roo.apply(f, o);
41600         });
41601         return this;
41602     },
41603
41604     /**
41605      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41606      * @param {Object} values
41607      * @return {BasicForm} this
41608      */
41609     applyIfToFields : function(o){
41610         this.items.each(function(f){
41611            Roo.applyIf(f, o);
41612         });
41613         return this;
41614     }
41615 });
41616
41617 // back compat
41618 Roo.BasicForm = Roo.form.BasicForm;/*
41619  * Based on:
41620  * Ext JS Library 1.1.1
41621  * Copyright(c) 2006-2007, Ext JS, LLC.
41622  *
41623  * Originally Released Under LGPL - original licence link has changed is not relivant.
41624  *
41625  * Fork - LGPL
41626  * <script type="text/javascript">
41627  */
41628
41629 /**
41630  * @class Roo.form.Form
41631  * @extends Roo.form.BasicForm
41632  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41633  * @constructor
41634  * @param {Object} config Configuration options
41635  */
41636 Roo.form.Form = function(config){
41637     var xitems =  [];
41638     if (config.items) {
41639         xitems = config.items;
41640         delete config.items;
41641     }
41642    
41643     
41644     Roo.form.Form.superclass.constructor.call(this, null, config);
41645     this.url = this.url || this.action;
41646     if(!this.root){
41647         this.root = new Roo.form.Layout(Roo.applyIf({
41648             id: Roo.id()
41649         }, config));
41650     }
41651     this.active = this.root;
41652     /**
41653      * Array of all the buttons that have been added to this form via {@link addButton}
41654      * @type Array
41655      */
41656     this.buttons = [];
41657     this.allItems = [];
41658     this.addEvents({
41659         /**
41660          * @event clientvalidation
41661          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41662          * @param {Form} this
41663          * @param {Boolean} valid true if the form has passed client-side validation
41664          */
41665         clientvalidation: true,
41666         /**
41667          * @event rendered
41668          * Fires when the form is rendered
41669          * @param {Roo.form.Form} form
41670          */
41671         rendered : true
41672     });
41673     
41674     if (this.progressUrl) {
41675             // push a hidden field onto the list of fields..
41676             this.addxtype( {
41677                     xns: Roo.form, 
41678                     xtype : 'Hidden', 
41679                     name : 'UPLOAD_IDENTIFIER' 
41680             });
41681         }
41682         
41683     
41684     Roo.each(xitems, this.addxtype, this);
41685     
41686     
41687     
41688 };
41689
41690 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41691     /**
41692      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41693      */
41694     /**
41695      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41696      */
41697     /**
41698      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41699      */
41700     buttonAlign:'center',
41701
41702     /**
41703      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41704      */
41705     minButtonWidth:75,
41706
41707     /**
41708      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41709      * This property cascades to child containers if not set.
41710      */
41711     labelAlign:'left',
41712
41713     /**
41714      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41715      * fires a looping event with that state. This is required to bind buttons to the valid
41716      * state using the config value formBind:true on the button.
41717      */
41718     monitorValid : false,
41719
41720     /**
41721      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41722      */
41723     monitorPoll : 200,
41724     
41725     /**
41726      * @cfg {String} progressUrl - Url to return progress data 
41727      */
41728     
41729     progressUrl : false,
41730   
41731     /**
41732      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41733      * fields are added and the column is closed. If no fields are passed the column remains open
41734      * until end() is called.
41735      * @param {Object} config The config to pass to the column
41736      * @param {Field} field1 (optional)
41737      * @param {Field} field2 (optional)
41738      * @param {Field} etc (optional)
41739      * @return Column The column container object
41740      */
41741     column : function(c){
41742         var col = new Roo.form.Column(c);
41743         this.start(col);
41744         if(arguments.length > 1){ // duplicate code required because of Opera
41745             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41746             this.end();
41747         }
41748         return col;
41749     },
41750
41751     /**
41752      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41753      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41754      * until end() is called.
41755      * @param {Object} config The config to pass to the fieldset
41756      * @param {Field} field1 (optional)
41757      * @param {Field} field2 (optional)
41758      * @param {Field} etc (optional)
41759      * @return FieldSet The fieldset container object
41760      */
41761     fieldset : function(c){
41762         var fs = new Roo.form.FieldSet(c);
41763         this.start(fs);
41764         if(arguments.length > 1){ // duplicate code required because of Opera
41765             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41766             this.end();
41767         }
41768         return fs;
41769     },
41770
41771     /**
41772      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41773      * fields are added and the container is closed. If no fields are passed the container remains open
41774      * until end() is called.
41775      * @param {Object} config The config to pass to the Layout
41776      * @param {Field} field1 (optional)
41777      * @param {Field} field2 (optional)
41778      * @param {Field} etc (optional)
41779      * @return Layout The container object
41780      */
41781     container : function(c){
41782         var l = new Roo.form.Layout(c);
41783         this.start(l);
41784         if(arguments.length > 1){ // duplicate code required because of Opera
41785             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41786             this.end();
41787         }
41788         return l;
41789     },
41790
41791     /**
41792      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41793      * @param {Object} container A Roo.form.Layout or subclass of Layout
41794      * @return {Form} this
41795      */
41796     start : function(c){
41797         // cascade label info
41798         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41799         this.active.stack.push(c);
41800         c.ownerCt = this.active;
41801         this.active = c;
41802         return this;
41803     },
41804
41805     /**
41806      * Closes the current open container
41807      * @return {Form} this
41808      */
41809     end : function(){
41810         if(this.active == this.root){
41811             return this;
41812         }
41813         this.active = this.active.ownerCt;
41814         return this;
41815     },
41816
41817     /**
41818      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41819      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41820      * as the label of the field.
41821      * @param {Field} field1
41822      * @param {Field} field2 (optional)
41823      * @param {Field} etc. (optional)
41824      * @return {Form} this
41825      */
41826     add : function(){
41827         this.active.stack.push.apply(this.active.stack, arguments);
41828         this.allItems.push.apply(this.allItems,arguments);
41829         var r = [];
41830         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41831             if(a[i].isFormField){
41832                 r.push(a[i]);
41833             }
41834         }
41835         if(r.length > 0){
41836             Roo.form.Form.superclass.add.apply(this, r);
41837         }
41838         return this;
41839     },
41840     
41841
41842     
41843     
41844     
41845      /**
41846      * Find any element that has been added to a form, using it's ID or name
41847      * This can include framesets, columns etc. along with regular fields..
41848      * @param {String} id - id or name to find.
41849      
41850      * @return {Element} e - or false if nothing found.
41851      */
41852     findbyId : function(id)
41853     {
41854         var ret = false;
41855         if (!id) {
41856             return ret;
41857         }
41858         Roo.each(this.allItems, function(f){
41859             if (f.id == id || f.name == id ){
41860                 ret = f;
41861                 return false;
41862             }
41863         });
41864         return ret;
41865     },
41866
41867     
41868     
41869     /**
41870      * Render this form into the passed container. This should only be called once!
41871      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41872      * @return {Form} this
41873      */
41874     render : function(ct)
41875     {
41876         
41877         
41878         
41879         ct = Roo.get(ct);
41880         var o = this.autoCreate || {
41881             tag: 'form',
41882             method : this.method || 'POST',
41883             id : this.id || Roo.id()
41884         };
41885         this.initEl(ct.createChild(o));
41886
41887         this.root.render(this.el);
41888         
41889        
41890              
41891         this.items.each(function(f){
41892             f.render('x-form-el-'+f.id);
41893         });
41894
41895         if(this.buttons.length > 0){
41896             // tables are required to maintain order and for correct IE layout
41897             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41898                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41899                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41900             }}, null, true);
41901             var tr = tb.getElementsByTagName('tr')[0];
41902             for(var i = 0, len = this.buttons.length; i < len; i++) {
41903                 var b = this.buttons[i];
41904                 var td = document.createElement('td');
41905                 td.className = 'x-form-btn-td';
41906                 b.render(tr.appendChild(td));
41907             }
41908         }
41909         if(this.monitorValid){ // initialize after render
41910             this.startMonitoring();
41911         }
41912         this.fireEvent('rendered', this);
41913         return this;
41914     },
41915
41916     /**
41917      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41918      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41919      * object or a valid Roo.DomHelper element config
41920      * @param {Function} handler The function called when the button is clicked
41921      * @param {Object} scope (optional) The scope of the handler function
41922      * @return {Roo.Button}
41923      */
41924     addButton : function(config, handler, scope){
41925         var bc = {
41926             handler: handler,
41927             scope: scope,
41928             minWidth: this.minButtonWidth,
41929             hideParent:true
41930         };
41931         if(typeof config == "string"){
41932             bc.text = config;
41933         }else{
41934             Roo.apply(bc, config);
41935         }
41936         var btn = new Roo.Button(null, bc);
41937         this.buttons.push(btn);
41938         return btn;
41939     },
41940
41941      /**
41942      * Adds a series of form elements (using the xtype property as the factory method.
41943      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41944      * @param {Object} config 
41945      */
41946     
41947     addxtype : function()
41948     {
41949         var ar = Array.prototype.slice.call(arguments, 0);
41950         var ret = false;
41951         for(var i = 0; i < ar.length; i++) {
41952             if (!ar[i]) {
41953                 continue; // skip -- if this happends something invalid got sent, we 
41954                 // should ignore it, as basically that interface element will not show up
41955                 // and that should be pretty obvious!!
41956             }
41957             
41958             if (Roo.form[ar[i].xtype]) {
41959                 ar[i].form = this;
41960                 var fe = Roo.factory(ar[i], Roo.form);
41961                 if (!ret) {
41962                     ret = fe;
41963                 }
41964                 fe.form = this;
41965                 if (fe.store) {
41966                     fe.store.form = this;
41967                 }
41968                 if (fe.isLayout) {  
41969                          
41970                     this.start(fe);
41971                     this.allItems.push(fe);
41972                     if (fe.items && fe.addxtype) {
41973                         fe.addxtype.apply(fe, fe.items);
41974                         delete fe.items;
41975                     }
41976                      this.end();
41977                     continue;
41978                 }
41979                 
41980                 
41981                  
41982                 this.add(fe);
41983               //  console.log('adding ' + ar[i].xtype);
41984             }
41985             if (ar[i].xtype == 'Button') {  
41986                 //console.log('adding button');
41987                 //console.log(ar[i]);
41988                 this.addButton(ar[i]);
41989                 this.allItems.push(fe);
41990                 continue;
41991             }
41992             
41993             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41994                 alert('end is not supported on xtype any more, use items');
41995             //    this.end();
41996             //    //console.log('adding end');
41997             }
41998             
41999         }
42000         return ret;
42001     },
42002     
42003     /**
42004      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42005      * option "monitorValid"
42006      */
42007     startMonitoring : function(){
42008         if(!this.bound){
42009             this.bound = true;
42010             Roo.TaskMgr.start({
42011                 run : this.bindHandler,
42012                 interval : this.monitorPoll || 200,
42013                 scope: this
42014             });
42015         }
42016     },
42017
42018     /**
42019      * Stops monitoring of the valid state of this form
42020      */
42021     stopMonitoring : function(){
42022         this.bound = false;
42023     },
42024
42025     // private
42026     bindHandler : function(){
42027         if(!this.bound){
42028             return false; // stops binding
42029         }
42030         var valid = true;
42031         this.items.each(function(f){
42032             if(!f.isValid(true)){
42033                 valid = false;
42034                 return false;
42035             }
42036         });
42037         for(var i = 0, len = this.buttons.length; i < len; i++){
42038             var btn = this.buttons[i];
42039             if(btn.formBind === true && btn.disabled === valid){
42040                 btn.setDisabled(!valid);
42041             }
42042         }
42043         this.fireEvent('clientvalidation', this, valid);
42044     }
42045     
42046     
42047     
42048     
42049     
42050     
42051     
42052     
42053 });
42054
42055
42056 // back compat
42057 Roo.Form = Roo.form.Form;
42058 /*
42059  * Based on:
42060  * Ext JS Library 1.1.1
42061  * Copyright(c) 2006-2007, Ext JS, LLC.
42062  *
42063  * Originally Released Under LGPL - original licence link has changed is not relivant.
42064  *
42065  * Fork - LGPL
42066  * <script type="text/javascript">
42067  */
42068  
42069  /**
42070  * @class Roo.form.Action
42071  * Internal Class used to handle form actions
42072  * @constructor
42073  * @param {Roo.form.BasicForm} el The form element or its id
42074  * @param {Object} config Configuration options
42075  */
42076  
42077  
42078 // define the action interface
42079 Roo.form.Action = function(form, options){
42080     this.form = form;
42081     this.options = options || {};
42082 };
42083 /**
42084  * Client Validation Failed
42085  * @const 
42086  */
42087 Roo.form.Action.CLIENT_INVALID = 'client';
42088 /**
42089  * Server Validation Failed
42090  * @const 
42091  */
42092  Roo.form.Action.SERVER_INVALID = 'server';
42093  /**
42094  * Connect to Server Failed
42095  * @const 
42096  */
42097 Roo.form.Action.CONNECT_FAILURE = 'connect';
42098 /**
42099  * Reading Data from Server Failed
42100  * @const 
42101  */
42102 Roo.form.Action.LOAD_FAILURE = 'load';
42103
42104 Roo.form.Action.prototype = {
42105     type : 'default',
42106     failureType : undefined,
42107     response : undefined,
42108     result : undefined,
42109
42110     // interface method
42111     run : function(options){
42112
42113     },
42114
42115     // interface method
42116     success : function(response){
42117
42118     },
42119
42120     // interface method
42121     handleResponse : function(response){
42122
42123     },
42124
42125     // default connection failure
42126     failure : function(response){
42127         
42128         this.response = response;
42129         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42130         this.form.afterAction(this, false);
42131     },
42132
42133     processResponse : function(response){
42134         this.response = response;
42135         if(!response.responseText){
42136             return true;
42137         }
42138         this.result = this.handleResponse(response);
42139         return this.result;
42140     },
42141
42142     // utility functions used internally
42143     getUrl : function(appendParams){
42144         var url = this.options.url || this.form.url || this.form.el.dom.action;
42145         if(appendParams){
42146             var p = this.getParams();
42147             if(p){
42148                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42149             }
42150         }
42151         return url;
42152     },
42153
42154     getMethod : function(){
42155         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42156     },
42157
42158     getParams : function(){
42159         var bp = this.form.baseParams;
42160         var p = this.options.params;
42161         if(p){
42162             if(typeof p == "object"){
42163                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42164             }else if(typeof p == 'string' && bp){
42165                 p += '&' + Roo.urlEncode(bp);
42166             }
42167         }else if(bp){
42168             p = Roo.urlEncode(bp);
42169         }
42170         return p;
42171     },
42172
42173     createCallback : function(){
42174         return {
42175             success: this.success,
42176             failure: this.failure,
42177             scope: this,
42178             timeout: (this.form.timeout*1000),
42179             upload: this.form.fileUpload ? this.success : undefined
42180         };
42181     }
42182 };
42183
42184 Roo.form.Action.Submit = function(form, options){
42185     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42186 };
42187
42188 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42189     type : 'submit',
42190
42191     haveProgress : false,
42192     uploadComplete : false,
42193     
42194     // uploadProgress indicator.
42195     uploadProgress : function()
42196     {
42197         if (!this.form.progressUrl) {
42198             return;
42199         }
42200         
42201         if (!this.haveProgress) {
42202             Roo.MessageBox.progress("Uploading", "Uploading");
42203         }
42204         if (this.uploadComplete) {
42205            Roo.MessageBox.hide();
42206            return;
42207         }
42208         
42209         this.haveProgress = true;
42210    
42211         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42212         
42213         var c = new Roo.data.Connection();
42214         c.request({
42215             url : this.form.progressUrl,
42216             params: {
42217                 id : uid
42218             },
42219             method: 'GET',
42220             success : function(req){
42221                //console.log(data);
42222                 var rdata = false;
42223                 var edata;
42224                 try  {
42225                    rdata = Roo.decode(req.responseText)
42226                 } catch (e) {
42227                     Roo.log("Invalid data from server..");
42228                     Roo.log(edata);
42229                     return;
42230                 }
42231                 if (!rdata || !rdata.success) {
42232                     Roo.log(rdata);
42233                     return;
42234                 }
42235                 var data = rdata.data;
42236                 
42237                 if (this.uploadComplete) {
42238                    Roo.MessageBox.hide();
42239                    return;
42240                 }
42241                    
42242                 if (data){
42243                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42244                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42245                     );
42246                 }
42247                 this.uploadProgress.defer(2000,this);
42248             },
42249        
42250             failure: function(data) {
42251                 Roo.log('progress url failed ');
42252                 Roo.log(data);
42253             },
42254             scope : this
42255         });
42256            
42257     },
42258     
42259     
42260     run : function()
42261     {
42262         // run get Values on the form, so it syncs any secondary forms.
42263         this.form.getValues();
42264         
42265         var o = this.options;
42266         var method = this.getMethod();
42267         var isPost = method == 'POST';
42268         if(o.clientValidation === false || this.form.isValid()){
42269             
42270             if (this.form.progressUrl) {
42271                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42272                     (new Date() * 1) + '' + Math.random());
42273                     
42274             } 
42275             
42276             
42277             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42278                 form:this.form.el.dom,
42279                 url:this.getUrl(!isPost),
42280                 method: method,
42281                 params:isPost ? this.getParams() : null,
42282                 isUpload: this.form.fileUpload
42283             }));
42284             
42285             this.uploadProgress();
42286
42287         }else if (o.clientValidation !== false){ // client validation failed
42288             this.failureType = Roo.form.Action.CLIENT_INVALID;
42289             this.form.afterAction(this, false);
42290         }
42291     },
42292
42293     success : function(response)
42294     {
42295         this.uploadComplete= true;
42296         if (this.haveProgress) {
42297             Roo.MessageBox.hide();
42298         }
42299         
42300         
42301         var result = this.processResponse(response);
42302         if(result === true || result.success){
42303             this.form.afterAction(this, true);
42304             return;
42305         }
42306         if(result.errors){
42307             this.form.markInvalid(result.errors);
42308             this.failureType = Roo.form.Action.SERVER_INVALID;
42309         }
42310         this.form.afterAction(this, false);
42311     },
42312     failure : function(response)
42313     {
42314         this.uploadComplete= true;
42315         if (this.haveProgress) {
42316             Roo.MessageBox.hide();
42317         }
42318         
42319         this.response = response;
42320         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42321         this.form.afterAction(this, false);
42322     },
42323     
42324     handleResponse : function(response){
42325         if(this.form.errorReader){
42326             var rs = this.form.errorReader.read(response);
42327             var errors = [];
42328             if(rs.records){
42329                 for(var i = 0, len = rs.records.length; i < len; i++) {
42330                     var r = rs.records[i];
42331                     errors[i] = r.data;
42332                 }
42333             }
42334             if(errors.length < 1){
42335                 errors = null;
42336             }
42337             return {
42338                 success : rs.success,
42339                 errors : errors
42340             };
42341         }
42342         var ret = false;
42343         try {
42344             ret = Roo.decode(response.responseText);
42345         } catch (e) {
42346             ret = {
42347                 success: false,
42348                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42349                 errors : []
42350             };
42351         }
42352         return ret;
42353         
42354     }
42355 });
42356
42357
42358 Roo.form.Action.Load = function(form, options){
42359     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42360     this.reader = this.form.reader;
42361 };
42362
42363 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42364     type : 'load',
42365
42366     run : function(){
42367         
42368         Roo.Ajax.request(Roo.apply(
42369                 this.createCallback(), {
42370                     method:this.getMethod(),
42371                     url:this.getUrl(false),
42372                     params:this.getParams()
42373         }));
42374     },
42375
42376     success : function(response){
42377         
42378         var result = this.processResponse(response);
42379         if(result === true || !result.success || !result.data){
42380             this.failureType = Roo.form.Action.LOAD_FAILURE;
42381             this.form.afterAction(this, false);
42382             return;
42383         }
42384         this.form.clearInvalid();
42385         this.form.setValues(result.data);
42386         this.form.afterAction(this, true);
42387     },
42388
42389     handleResponse : function(response){
42390         if(this.form.reader){
42391             var rs = this.form.reader.read(response);
42392             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42393             return {
42394                 success : rs.success,
42395                 data : data
42396             };
42397         }
42398         return Roo.decode(response.responseText);
42399     }
42400 });
42401
42402 Roo.form.Action.ACTION_TYPES = {
42403     'load' : Roo.form.Action.Load,
42404     'submit' : Roo.form.Action.Submit
42405 };/*
42406  * Based on:
42407  * Ext JS Library 1.1.1
42408  * Copyright(c) 2006-2007, Ext JS, LLC.
42409  *
42410  * Originally Released Under LGPL - original licence link has changed is not relivant.
42411  *
42412  * Fork - LGPL
42413  * <script type="text/javascript">
42414  */
42415  
42416 /**
42417  * @class Roo.form.Layout
42418  * @extends Roo.Component
42419  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42420  * @constructor
42421  * @param {Object} config Configuration options
42422  */
42423 Roo.form.Layout = function(config){
42424     var xitems = [];
42425     if (config.items) {
42426         xitems = config.items;
42427         delete config.items;
42428     }
42429     Roo.form.Layout.superclass.constructor.call(this, config);
42430     this.stack = [];
42431     Roo.each(xitems, this.addxtype, this);
42432      
42433 };
42434
42435 Roo.extend(Roo.form.Layout, Roo.Component, {
42436     /**
42437      * @cfg {String/Object} autoCreate
42438      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42439      */
42440     /**
42441      * @cfg {String/Object/Function} style
42442      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42443      * a function which returns such a specification.
42444      */
42445     /**
42446      * @cfg {String} labelAlign
42447      * Valid values are "left," "top" and "right" (defaults to "left")
42448      */
42449     /**
42450      * @cfg {Number} labelWidth
42451      * Fixed width in pixels of all field labels (defaults to undefined)
42452      */
42453     /**
42454      * @cfg {Boolean} clear
42455      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42456      */
42457     clear : true,
42458     /**
42459      * @cfg {String} labelSeparator
42460      * The separator to use after field labels (defaults to ':')
42461      */
42462     labelSeparator : ':',
42463     /**
42464      * @cfg {Boolean} hideLabels
42465      * True to suppress the display of field labels in this layout (defaults to false)
42466      */
42467     hideLabels : false,
42468
42469     // private
42470     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42471     
42472     isLayout : true,
42473     
42474     // private
42475     onRender : function(ct, position){
42476         if(this.el){ // from markup
42477             this.el = Roo.get(this.el);
42478         }else {  // generate
42479             var cfg = this.getAutoCreate();
42480             this.el = ct.createChild(cfg, position);
42481         }
42482         if(this.style){
42483             this.el.applyStyles(this.style);
42484         }
42485         if(this.labelAlign){
42486             this.el.addClass('x-form-label-'+this.labelAlign);
42487         }
42488         if(this.hideLabels){
42489             this.labelStyle = "display:none";
42490             this.elementStyle = "padding-left:0;";
42491         }else{
42492             if(typeof this.labelWidth == 'number'){
42493                 this.labelStyle = "width:"+this.labelWidth+"px;";
42494                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42495             }
42496             if(this.labelAlign == 'top'){
42497                 this.labelStyle = "width:auto;";
42498                 this.elementStyle = "padding-left:0;";
42499             }
42500         }
42501         var stack = this.stack;
42502         var slen = stack.length;
42503         if(slen > 0){
42504             if(!this.fieldTpl){
42505                 var t = new Roo.Template(
42506                     '<div class="x-form-item {5}">',
42507                         '<label for="{0}" style="{2}">{1}{4}</label>',
42508                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42509                         '</div>',
42510                     '</div><div class="x-form-clear-left"></div>'
42511                 );
42512                 t.disableFormats = true;
42513                 t.compile();
42514                 Roo.form.Layout.prototype.fieldTpl = t;
42515             }
42516             for(var i = 0; i < slen; i++) {
42517                 if(stack[i].isFormField){
42518                     this.renderField(stack[i]);
42519                 }else{
42520                     this.renderComponent(stack[i]);
42521                 }
42522             }
42523         }
42524         if(this.clear){
42525             this.el.createChild({cls:'x-form-clear'});
42526         }
42527     },
42528
42529     // private
42530     renderField : function(f){
42531         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42532                f.id, //0
42533                f.fieldLabel, //1
42534                f.labelStyle||this.labelStyle||'', //2
42535                this.elementStyle||'', //3
42536                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42537                f.itemCls||this.itemCls||''  //5
42538        ], true).getPrevSibling());
42539     },
42540
42541     // private
42542     renderComponent : function(c){
42543         c.render(c.isLayout ? this.el : this.el.createChild());    
42544     },
42545     /**
42546      * Adds a object form elements (using the xtype property as the factory method.)
42547      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42548      * @param {Object} config 
42549      */
42550     addxtype : function(o)
42551     {
42552         // create the lement.
42553         o.form = this.form;
42554         var fe = Roo.factory(o, Roo.form);
42555         this.form.allItems.push(fe);
42556         this.stack.push(fe);
42557         
42558         if (fe.isFormField) {
42559             this.form.items.add(fe);
42560         }
42561          
42562         return fe;
42563     }
42564 });
42565
42566 /**
42567  * @class Roo.form.Column
42568  * @extends Roo.form.Layout
42569  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42570  * @constructor
42571  * @param {Object} config Configuration options
42572  */
42573 Roo.form.Column = function(config){
42574     Roo.form.Column.superclass.constructor.call(this, config);
42575 };
42576
42577 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42578     /**
42579      * @cfg {Number/String} width
42580      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42581      */
42582     /**
42583      * @cfg {String/Object} autoCreate
42584      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42585      */
42586
42587     // private
42588     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42589
42590     // private
42591     onRender : function(ct, position){
42592         Roo.form.Column.superclass.onRender.call(this, ct, position);
42593         if(this.width){
42594             this.el.setWidth(this.width);
42595         }
42596     }
42597 });
42598
42599
42600 /**
42601  * @class Roo.form.Row
42602  * @extends Roo.form.Layout
42603  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42604  * @constructor
42605  * @param {Object} config Configuration options
42606  */
42607
42608  
42609 Roo.form.Row = function(config){
42610     Roo.form.Row.superclass.constructor.call(this, config);
42611 };
42612  
42613 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42614       /**
42615      * @cfg {Number/String} width
42616      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42617      */
42618     /**
42619      * @cfg {Number/String} height
42620      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42621      */
42622     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42623     
42624     padWidth : 20,
42625     // private
42626     onRender : function(ct, position){
42627         //console.log('row render');
42628         if(!this.rowTpl){
42629             var t = new Roo.Template(
42630                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42631                     '<label for="{0}" style="{2}">{1}{4}</label>',
42632                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42633                     '</div>',
42634                 '</div>'
42635             );
42636             t.disableFormats = true;
42637             t.compile();
42638             Roo.form.Layout.prototype.rowTpl = t;
42639         }
42640         this.fieldTpl = this.rowTpl;
42641         
42642         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42643         var labelWidth = 100;
42644         
42645         if ((this.labelAlign != 'top')) {
42646             if (typeof this.labelWidth == 'number') {
42647                 labelWidth = this.labelWidth
42648             }
42649             this.padWidth =  20 + labelWidth;
42650             
42651         }
42652         
42653         Roo.form.Column.superclass.onRender.call(this, ct, position);
42654         if(this.width){
42655             this.el.setWidth(this.width);
42656         }
42657         if(this.height){
42658             this.el.setHeight(this.height);
42659         }
42660     },
42661     
42662     // private
42663     renderField : function(f){
42664         f.fieldEl = this.fieldTpl.append(this.el, [
42665                f.id, f.fieldLabel,
42666                f.labelStyle||this.labelStyle||'',
42667                this.elementStyle||'',
42668                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42669                f.itemCls||this.itemCls||'',
42670                f.width ? f.width + this.padWidth : 160 + this.padWidth
42671        ],true);
42672     }
42673 });
42674  
42675
42676 /**
42677  * @class Roo.form.FieldSet
42678  * @extends Roo.form.Layout
42679  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42680  * @constructor
42681  * @param {Object} config Configuration options
42682  */
42683 Roo.form.FieldSet = function(config){
42684     Roo.form.FieldSet.superclass.constructor.call(this, config);
42685 };
42686
42687 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42688     /**
42689      * @cfg {String} legend
42690      * The text to display as the legend for the FieldSet (defaults to '')
42691      */
42692     /**
42693      * @cfg {String/Object} autoCreate
42694      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42695      */
42696
42697     // private
42698     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42699
42700     // private
42701     onRender : function(ct, position){
42702         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42703         if(this.legend){
42704             this.setLegend(this.legend);
42705         }
42706     },
42707
42708     // private
42709     setLegend : function(text){
42710         if(this.rendered){
42711             this.el.child('legend').update(text);
42712         }
42713     }
42714 });/*
42715  * Based on:
42716  * Ext JS Library 1.1.1
42717  * Copyright(c) 2006-2007, Ext JS, LLC.
42718  *
42719  * Originally Released Under LGPL - original licence link has changed is not relivant.
42720  *
42721  * Fork - LGPL
42722  * <script type="text/javascript">
42723  */
42724 /**
42725  * @class Roo.form.VTypes
42726  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42727  * @singleton
42728  */
42729 Roo.form.VTypes = function(){
42730     // closure these in so they are only created once.
42731     var alpha = /^[a-zA-Z_]+$/;
42732     var alphanum = /^[a-zA-Z0-9_]+$/;
42733     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42734     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42735
42736     // All these messages and functions are configurable
42737     return {
42738         /**
42739          * The function used to validate email addresses
42740          * @param {String} value The email address
42741          */
42742         'email' : function(v){
42743             return email.test(v);
42744         },
42745         /**
42746          * The error text to display when the email validation function returns false
42747          * @type String
42748          */
42749         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42750         /**
42751          * The keystroke filter mask to be applied on email input
42752          * @type RegExp
42753          */
42754         'emailMask' : /[a-z0-9_\.\-@]/i,
42755
42756         /**
42757          * The function used to validate URLs
42758          * @param {String} value The URL
42759          */
42760         'url' : function(v){
42761             return url.test(v);
42762         },
42763         /**
42764          * The error text to display when the url validation function returns false
42765          * @type String
42766          */
42767         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42768         
42769         /**
42770          * The function used to validate alpha values
42771          * @param {String} value The value
42772          */
42773         'alpha' : function(v){
42774             return alpha.test(v);
42775         },
42776         /**
42777          * The error text to display when the alpha validation function returns false
42778          * @type String
42779          */
42780         'alphaText' : 'This field should only contain letters and _',
42781         /**
42782          * The keystroke filter mask to be applied on alpha input
42783          * @type RegExp
42784          */
42785         'alphaMask' : /[a-z_]/i,
42786
42787         /**
42788          * The function used to validate alphanumeric values
42789          * @param {String} value The value
42790          */
42791         'alphanum' : function(v){
42792             return alphanum.test(v);
42793         },
42794         /**
42795          * The error text to display when the alphanumeric validation function returns false
42796          * @type String
42797          */
42798         'alphanumText' : 'This field should only contain letters, numbers and _',
42799         /**
42800          * The keystroke filter mask to be applied on alphanumeric input
42801          * @type RegExp
42802          */
42803         'alphanumMask' : /[a-z0-9_]/i
42804     };
42805 }();//<script type="text/javascript">
42806
42807 /**
42808  * @class Roo.form.FCKeditor
42809  * @extends Roo.form.TextArea
42810  * Wrapper around the FCKEditor http://www.fckeditor.net
42811  * @constructor
42812  * Creates a new FCKeditor
42813  * @param {Object} config Configuration options
42814  */
42815 Roo.form.FCKeditor = function(config){
42816     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42817     this.addEvents({
42818          /**
42819          * @event editorinit
42820          * Fired when the editor is initialized - you can add extra handlers here..
42821          * @param {FCKeditor} this
42822          * @param {Object} the FCK object.
42823          */
42824         editorinit : true
42825     });
42826     
42827     
42828 };
42829 Roo.form.FCKeditor.editors = { };
42830 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42831 {
42832     //defaultAutoCreate : {
42833     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42834     //},
42835     // private
42836     /**
42837      * @cfg {Object} fck options - see fck manual for details.
42838      */
42839     fckconfig : false,
42840     
42841     /**
42842      * @cfg {Object} fck toolbar set (Basic or Default)
42843      */
42844     toolbarSet : 'Basic',
42845     /**
42846      * @cfg {Object} fck BasePath
42847      */ 
42848     basePath : '/fckeditor/',
42849     
42850     
42851     frame : false,
42852     
42853     value : '',
42854     
42855    
42856     onRender : function(ct, position)
42857     {
42858         if(!this.el){
42859             this.defaultAutoCreate = {
42860                 tag: "textarea",
42861                 style:"width:300px;height:60px;",
42862                 autocomplete: "off"
42863             };
42864         }
42865         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42866         /*
42867         if(this.grow){
42868             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42869             if(this.preventScrollbars){
42870                 this.el.setStyle("overflow", "hidden");
42871             }
42872             this.el.setHeight(this.growMin);
42873         }
42874         */
42875         //console.log('onrender' + this.getId() );
42876         Roo.form.FCKeditor.editors[this.getId()] = this;
42877          
42878
42879         this.replaceTextarea() ;
42880         
42881     },
42882     
42883     getEditor : function() {
42884         return this.fckEditor;
42885     },
42886     /**
42887      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42888      * @param {Mixed} value The value to set
42889      */
42890     
42891     
42892     setValue : function(value)
42893     {
42894         //console.log('setValue: ' + value);
42895         
42896         if(typeof(value) == 'undefined') { // not sure why this is happending...
42897             return;
42898         }
42899         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42900         
42901         //if(!this.el || !this.getEditor()) {
42902         //    this.value = value;
42903             //this.setValue.defer(100,this,[value]);    
42904         //    return;
42905         //} 
42906         
42907         if(!this.getEditor()) {
42908             return;
42909         }
42910         
42911         this.getEditor().SetData(value);
42912         
42913         //
42914
42915     },
42916
42917     /**
42918      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42919      * @return {Mixed} value The field value
42920      */
42921     getValue : function()
42922     {
42923         
42924         if (this.frame && this.frame.dom.style.display == 'none') {
42925             return Roo.form.FCKeditor.superclass.getValue.call(this);
42926         }
42927         
42928         if(!this.el || !this.getEditor()) {
42929            
42930            // this.getValue.defer(100,this); 
42931             return this.value;
42932         }
42933        
42934         
42935         var value=this.getEditor().GetData();
42936         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42937         return Roo.form.FCKeditor.superclass.getValue.call(this);
42938         
42939
42940     },
42941
42942     /**
42943      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42944      * @return {Mixed} value The field value
42945      */
42946     getRawValue : function()
42947     {
42948         if (this.frame && this.frame.dom.style.display == 'none') {
42949             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42950         }
42951         
42952         if(!this.el || !this.getEditor()) {
42953             //this.getRawValue.defer(100,this); 
42954             return this.value;
42955             return;
42956         }
42957         
42958         
42959         
42960         var value=this.getEditor().GetData();
42961         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42962         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42963          
42964     },
42965     
42966     setSize : function(w,h) {
42967         
42968         
42969         
42970         //if (this.frame && this.frame.dom.style.display == 'none') {
42971         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42972         //    return;
42973         //}
42974         //if(!this.el || !this.getEditor()) {
42975         //    this.setSize.defer(100,this, [w,h]); 
42976         //    return;
42977         //}
42978         
42979         
42980         
42981         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42982         
42983         this.frame.dom.setAttribute('width', w);
42984         this.frame.dom.setAttribute('height', h);
42985         this.frame.setSize(w,h);
42986         
42987     },
42988     
42989     toggleSourceEdit : function(value) {
42990         
42991       
42992          
42993         this.el.dom.style.display = value ? '' : 'none';
42994         this.frame.dom.style.display = value ?  'none' : '';
42995         
42996     },
42997     
42998     
42999     focus: function(tag)
43000     {
43001         if (this.frame.dom.style.display == 'none') {
43002             return Roo.form.FCKeditor.superclass.focus.call(this);
43003         }
43004         if(!this.el || !this.getEditor()) {
43005             this.focus.defer(100,this, [tag]); 
43006             return;
43007         }
43008         
43009         
43010         
43011         
43012         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43013         this.getEditor().Focus();
43014         if (tgs.length) {
43015             if (!this.getEditor().Selection.GetSelection()) {
43016                 this.focus.defer(100,this, [tag]); 
43017                 return;
43018             }
43019             
43020             
43021             var r = this.getEditor().EditorDocument.createRange();
43022             r.setStart(tgs[0],0);
43023             r.setEnd(tgs[0],0);
43024             this.getEditor().Selection.GetSelection().removeAllRanges();
43025             this.getEditor().Selection.GetSelection().addRange(r);
43026             this.getEditor().Focus();
43027         }
43028         
43029     },
43030     
43031     
43032     
43033     replaceTextarea : function()
43034     {
43035         if ( document.getElementById( this.getId() + '___Frame' ) )
43036             return ;
43037         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43038         //{
43039             // We must check the elements firstly using the Id and then the name.
43040         var oTextarea = document.getElementById( this.getId() );
43041         
43042         var colElementsByName = document.getElementsByName( this.getId() ) ;
43043          
43044         oTextarea.style.display = 'none' ;
43045
43046         if ( oTextarea.tabIndex ) {            
43047             this.TabIndex = oTextarea.tabIndex ;
43048         }
43049         
43050         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43051         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43052         this.frame = Roo.get(this.getId() + '___Frame')
43053     },
43054     
43055     _getConfigHtml : function()
43056     {
43057         var sConfig = '' ;
43058
43059         for ( var o in this.fckconfig ) {
43060             sConfig += sConfig.length > 0  ? '&amp;' : '';
43061             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43062         }
43063
43064         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43065     },
43066     
43067     
43068     _getIFrameHtml : function()
43069     {
43070         var sFile = 'fckeditor.html' ;
43071         /* no idea what this is about..
43072         try
43073         {
43074             if ( (/fcksource=true/i).test( window.top.location.search ) )
43075                 sFile = 'fckeditor.original.html' ;
43076         }
43077         catch (e) { 
43078         */
43079
43080         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43081         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43082         
43083         
43084         var html = '<iframe id="' + this.getId() +
43085             '___Frame" src="' + sLink +
43086             '" width="' + this.width +
43087             '" height="' + this.height + '"' +
43088             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43089             ' frameborder="0" scrolling="no"></iframe>' ;
43090
43091         return html ;
43092     },
43093     
43094     _insertHtmlBefore : function( html, element )
43095     {
43096         if ( element.insertAdjacentHTML )       {
43097             // IE
43098             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43099         } else { // Gecko
43100             var oRange = document.createRange() ;
43101             oRange.setStartBefore( element ) ;
43102             var oFragment = oRange.createContextualFragment( html );
43103             element.parentNode.insertBefore( oFragment, element ) ;
43104         }
43105     }
43106     
43107     
43108   
43109     
43110     
43111     
43112     
43113
43114 });
43115
43116 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43117
43118 function FCKeditor_OnComplete(editorInstance){
43119     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43120     f.fckEditor = editorInstance;
43121     //console.log("loaded");
43122     f.fireEvent('editorinit', f, editorInstance);
43123
43124   
43125
43126  
43127
43128
43129
43130
43131
43132
43133
43134
43135
43136
43137
43138
43139
43140
43141
43142 //<script type="text/javascript">
43143 /**
43144  * @class Roo.form.GridField
43145  * @extends Roo.form.Field
43146  * Embed a grid (or editable grid into a form)
43147  * STATUS ALPHA
43148  * 
43149  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43150  * it needs 
43151  * xgrid.store = Roo.data.Store
43152  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43153  * xgrid.store.reader = Roo.data.JsonReader 
43154  * 
43155  * 
43156  * @constructor
43157  * Creates a new GridField
43158  * @param {Object} config Configuration options
43159  */
43160 Roo.form.GridField = function(config){
43161     Roo.form.GridField.superclass.constructor.call(this, config);
43162      
43163 };
43164
43165 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43166     /**
43167      * @cfg {Number} width  - used to restrict width of grid..
43168      */
43169     width : 100,
43170     /**
43171      * @cfg {Number} height - used to restrict height of grid..
43172      */
43173     height : 50,
43174      /**
43175      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43176          * 
43177          *}
43178      */
43179     xgrid : false, 
43180     /**
43181      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43182      * {tag: "input", type: "checkbox", autocomplete: "off"})
43183      */
43184    // defaultAutoCreate : { tag: 'div' },
43185     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43186     /**
43187      * @cfg {String} addTitle Text to include for adding a title.
43188      */
43189     addTitle : false,
43190     //
43191     onResize : function(){
43192         Roo.form.Field.superclass.onResize.apply(this, arguments);
43193     },
43194
43195     initEvents : function(){
43196         // Roo.form.Checkbox.superclass.initEvents.call(this);
43197         // has no events...
43198        
43199     },
43200
43201
43202     getResizeEl : function(){
43203         return this.wrap;
43204     },
43205
43206     getPositionEl : function(){
43207         return this.wrap;
43208     },
43209
43210     // private
43211     onRender : function(ct, position){
43212         
43213         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43214         var style = this.style;
43215         delete this.style;
43216         
43217         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43218         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43219         this.viewEl = this.wrap.createChild({ tag: 'div' });
43220         if (style) {
43221             this.viewEl.applyStyles(style);
43222         }
43223         if (this.width) {
43224             this.viewEl.setWidth(this.width);
43225         }
43226         if (this.height) {
43227             this.viewEl.setHeight(this.height);
43228         }
43229         //if(this.inputValue !== undefined){
43230         //this.setValue(this.value);
43231         
43232         
43233         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43234         
43235         
43236         this.grid.render();
43237         this.grid.getDataSource().on('remove', this.refreshValue, this);
43238         this.grid.getDataSource().on('update', this.refreshValue, this);
43239         this.grid.on('afteredit', this.refreshValue, this);
43240  
43241     },
43242      
43243     
43244     /**
43245      * Sets the value of the item. 
43246      * @param {String} either an object  or a string..
43247      */
43248     setValue : function(v){
43249         //this.value = v;
43250         v = v || []; // empty set..
43251         // this does not seem smart - it really only affects memoryproxy grids..
43252         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43253             var ds = this.grid.getDataSource();
43254             // assumes a json reader..
43255             var data = {}
43256             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43257             ds.loadData( data);
43258         }
43259         // clear selection so it does not get stale.
43260         if (this.grid.sm) { 
43261             this.grid.sm.clearSelections();
43262         }
43263         
43264         Roo.form.GridField.superclass.setValue.call(this, v);
43265         this.refreshValue();
43266         // should load data in the grid really....
43267     },
43268     
43269     // private
43270     refreshValue: function() {
43271          var val = [];
43272         this.grid.getDataSource().each(function(r) {
43273             val.push(r.data);
43274         });
43275         this.el.dom.value = Roo.encode(val);
43276     }
43277     
43278      
43279     
43280     
43281 });/*
43282  * Based on:
43283  * Ext JS Library 1.1.1
43284  * Copyright(c) 2006-2007, Ext JS, LLC.
43285  *
43286  * Originally Released Under LGPL - original licence link has changed is not relivant.
43287  *
43288  * Fork - LGPL
43289  * <script type="text/javascript">
43290  */
43291 /**
43292  * @class Roo.form.DisplayField
43293  * @extends Roo.form.Field
43294  * A generic Field to display non-editable data.
43295  * @constructor
43296  * Creates a new Display Field item.
43297  * @param {Object} config Configuration options
43298  */
43299 Roo.form.DisplayField = function(config){
43300     Roo.form.DisplayField.superclass.constructor.call(this, config);
43301     
43302 };
43303
43304 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43305     inputType:      'hidden',
43306     allowBlank:     true,
43307     readOnly:         true,
43308     
43309  
43310     /**
43311      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43312      */
43313     focusClass : undefined,
43314     /**
43315      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43316      */
43317     fieldClass: 'x-form-field',
43318     
43319      /**
43320      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43321      */
43322     valueRenderer: undefined,
43323     
43324     width: 100,
43325     /**
43326      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43327      * {tag: "input", type: "checkbox", autocomplete: "off"})
43328      */
43329      
43330  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43331
43332     onResize : function(){
43333         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43334         
43335     },
43336
43337     initEvents : function(){
43338         // Roo.form.Checkbox.superclass.initEvents.call(this);
43339         // has no events...
43340        
43341     },
43342
43343
43344     getResizeEl : function(){
43345         return this.wrap;
43346     },
43347
43348     getPositionEl : function(){
43349         return this.wrap;
43350     },
43351
43352     // private
43353     onRender : function(ct, position){
43354         
43355         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43356         //if(this.inputValue !== undefined){
43357         this.wrap = this.el.wrap();
43358         
43359         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43360         
43361         if (this.bodyStyle) {
43362             this.viewEl.applyStyles(this.bodyStyle);
43363         }
43364         //this.viewEl.setStyle('padding', '2px');
43365         
43366         this.setValue(this.value);
43367         
43368     },
43369 /*
43370     // private
43371     initValue : Roo.emptyFn,
43372
43373   */
43374
43375         // private
43376     onClick : function(){
43377         
43378     },
43379
43380     /**
43381      * Sets the checked state of the checkbox.
43382      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43383      */
43384     setValue : function(v){
43385         this.value = v;
43386         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43387         // this might be called before we have a dom element..
43388         if (!this.viewEl) {
43389             return;
43390         }
43391         this.viewEl.dom.innerHTML = html;
43392         Roo.form.DisplayField.superclass.setValue.call(this, v);
43393
43394     }
43395 });/*
43396  * 
43397  * Licence- LGPL
43398  * 
43399  */
43400
43401 /**
43402  * @class Roo.form.DayPicker
43403  * @extends Roo.form.Field
43404  * A Day picker show [M] [T] [W] ....
43405  * @constructor
43406  * Creates a new Day Picker
43407  * @param {Object} config Configuration options
43408  */
43409 Roo.form.DayPicker= function(config){
43410     Roo.form.DayPicker.superclass.constructor.call(this, config);
43411      
43412 };
43413
43414 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43415     /**
43416      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43417      */
43418     focusClass : undefined,
43419     /**
43420      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43421      */
43422     fieldClass: "x-form-field",
43423    
43424     /**
43425      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43426      * {tag: "input", type: "checkbox", autocomplete: "off"})
43427      */
43428     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43429     
43430    
43431     actionMode : 'viewEl', 
43432     //
43433     // private
43434  
43435     inputType : 'hidden',
43436     
43437      
43438     inputElement: false, // real input element?
43439     basedOn: false, // ????
43440     
43441     isFormField: true, // not sure where this is needed!!!!
43442
43443     onResize : function(){
43444         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43445         if(!this.boxLabel){
43446             this.el.alignTo(this.wrap, 'c-c');
43447         }
43448     },
43449
43450     initEvents : function(){
43451         Roo.form.Checkbox.superclass.initEvents.call(this);
43452         this.el.on("click", this.onClick,  this);
43453         this.el.on("change", this.onClick,  this);
43454     },
43455
43456
43457     getResizeEl : function(){
43458         return this.wrap;
43459     },
43460
43461     getPositionEl : function(){
43462         return this.wrap;
43463     },
43464
43465     
43466     // private
43467     onRender : function(ct, position){
43468         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43469        
43470         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43471         
43472         var r1 = '<table><tr>';
43473         var r2 = '<tr class="x-form-daypick-icons">';
43474         for (var i=0; i < 7; i++) {
43475             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43476             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43477         }
43478         
43479         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43480         viewEl.select('img').on('click', this.onClick, this);
43481         this.viewEl = viewEl;   
43482         
43483         
43484         // this will not work on Chrome!!!
43485         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43486         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43487         
43488         
43489           
43490
43491     },
43492
43493     // private
43494     initValue : Roo.emptyFn,
43495
43496     /**
43497      * Returns the checked state of the checkbox.
43498      * @return {Boolean} True if checked, else false
43499      */
43500     getValue : function(){
43501         return this.el.dom.value;
43502         
43503     },
43504
43505         // private
43506     onClick : function(e){ 
43507         //this.setChecked(!this.checked);
43508         Roo.get(e.target).toggleClass('x-menu-item-checked');
43509         this.refreshValue();
43510         //if(this.el.dom.checked != this.checked){
43511         //    this.setValue(this.el.dom.checked);
43512        // }
43513     },
43514     
43515     // private
43516     refreshValue : function()
43517     {
43518         var val = '';
43519         this.viewEl.select('img',true).each(function(e,i,n)  {
43520             val += e.is(".x-menu-item-checked") ? String(n) : '';
43521         });
43522         this.setValue(val, true);
43523     },
43524
43525     /**
43526      * Sets the checked state of the checkbox.
43527      * On is always based on a string comparison between inputValue and the param.
43528      * @param {Boolean/String} value - the value to set 
43529      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43530      */
43531     setValue : function(v,suppressEvent){
43532         if (!this.el.dom) {
43533             return;
43534         }
43535         var old = this.el.dom.value ;
43536         this.el.dom.value = v;
43537         if (suppressEvent) {
43538             return ;
43539         }
43540          
43541         // update display..
43542         this.viewEl.select('img',true).each(function(e,i,n)  {
43543             
43544             var on = e.is(".x-menu-item-checked");
43545             var newv = v.indexOf(String(n)) > -1;
43546             if (on != newv) {
43547                 e.toggleClass('x-menu-item-checked');
43548             }
43549             
43550         });
43551         
43552         
43553         this.fireEvent('change', this, v, old);
43554         
43555         
43556     },
43557    
43558     // handle setting of hidden value by some other method!!?!?
43559     setFromHidden: function()
43560     {
43561         if(!this.el){
43562             return;
43563         }
43564         //console.log("SET FROM HIDDEN");
43565         //alert('setFrom hidden');
43566         this.setValue(this.el.dom.value);
43567     },
43568     
43569     onDestroy : function()
43570     {
43571         if(this.viewEl){
43572             Roo.get(this.viewEl).remove();
43573         }
43574          
43575         Roo.form.DayPicker.superclass.onDestroy.call(this);
43576     }
43577
43578 });/*
43579  * RooJS Library 1.1.1
43580  * Copyright(c) 2008-2011  Alan Knowles
43581  *
43582  * License - LGPL
43583  */
43584  
43585
43586 /**
43587  * @class Roo.form.ComboCheck
43588  * @extends Roo.form.ComboBox
43589  * A combobox for multiple select items.
43590  *
43591  * FIXME - could do with a reset button..
43592  * 
43593  * @constructor
43594  * Create a new ComboCheck
43595  * @param {Object} config Configuration options
43596  */
43597 Roo.form.ComboCheck = function(config){
43598     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43599     // should verify some data...
43600     // like
43601     // hiddenName = required..
43602     // displayField = required
43603     // valudField == required
43604     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43605     var _t = this;
43606     Roo.each(req, function(e) {
43607         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43608             throw "Roo.form.ComboCheck : missing value for: " + e;
43609         }
43610     });
43611     
43612     
43613 };
43614
43615 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43616      
43617      
43618     editable : false,
43619      
43620     selectedClass: 'x-menu-item-checked', 
43621     
43622     // private
43623     onRender : function(ct, position){
43624         var _t = this;
43625         
43626         
43627         
43628         if(!this.tpl){
43629             var cls = 'x-combo-list';
43630
43631             
43632             this.tpl =  new Roo.Template({
43633                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43634                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43635                    '<span>{' + this.displayField + '}</span>' +
43636                     '</div>' 
43637                 
43638             });
43639         }
43640  
43641         
43642         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43643         this.view.singleSelect = false;
43644         this.view.multiSelect = true;
43645         this.view.toggleSelect = true;
43646         this.pageTb.add(new Roo.Toolbar.Fill(), {
43647             
43648             text: 'Done',
43649             handler: function()
43650             {
43651                 _t.collapse();
43652             }
43653         });
43654     },
43655     
43656     onViewOver : function(e, t){
43657         // do nothing...
43658         return;
43659         
43660     },
43661     
43662     onViewClick : function(doFocus,index){
43663         return;
43664         
43665     },
43666     select: function () {
43667         //Roo.log("SELECT CALLED");
43668     },
43669      
43670     selectByValue : function(xv, scrollIntoView){
43671         var ar = this.getValueArray();
43672         var sels = [];
43673         
43674         Roo.each(ar, function(v) {
43675             if(v === undefined || v === null){
43676                 return;
43677             }
43678             var r = this.findRecord(this.valueField, v);
43679             if(r){
43680                 sels.push(this.store.indexOf(r))
43681                 
43682             }
43683         },this);
43684         this.view.select(sels);
43685         return false;
43686     },
43687     
43688     
43689     
43690     onSelect : function(record, index){
43691        // Roo.log("onselect Called");
43692        // this is only called by the clear button now..
43693         this.view.clearSelections();
43694         this.setValue('[]');
43695         if (this.value != this.valueBefore) {
43696             this.fireEvent('change', this, this.value, this.valueBefore);
43697         }
43698     },
43699     getValueArray : function()
43700     {
43701         var ar = [] ;
43702         
43703         try {
43704             //Roo.log(this.value);
43705             if (typeof(this.value) == 'undefined') {
43706                 return [];
43707             }
43708             var ar = Roo.decode(this.value);
43709             return  ar instanceof Array ? ar : []; //?? valid?
43710             
43711         } catch(e) {
43712             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43713             return [];
43714         }
43715          
43716     },
43717     expand : function ()
43718     {
43719         Roo.form.ComboCheck.superclass.expand.call(this);
43720         this.valueBefore = this.value;
43721         
43722
43723     },
43724     
43725     collapse : function(){
43726         Roo.form.ComboCheck.superclass.collapse.call(this);
43727         var sl = this.view.getSelectedIndexes();
43728         var st = this.store;
43729         var nv = [];
43730         var tv = [];
43731         var r;
43732         Roo.each(sl, function(i) {
43733             r = st.getAt(i);
43734             nv.push(r.get(this.valueField));
43735         },this);
43736         this.setValue(Roo.encode(nv));
43737         if (this.value != this.valueBefore) {
43738
43739             this.fireEvent('change', this, this.value, this.valueBefore);
43740         }
43741         
43742     },
43743     
43744     setValue : function(v){
43745         // Roo.log(v);
43746         this.value = v;
43747         
43748         var vals = this.getValueArray();
43749         var tv = [];
43750         Roo.each(vals, function(k) {
43751             var r = this.findRecord(this.valueField, k);
43752             if(r){
43753                 tv.push(r.data[this.displayField]);
43754             }else if(this.valueNotFoundText !== undefined){
43755                 tv.push( this.valueNotFoundText );
43756             }
43757         },this);
43758        // Roo.log(tv);
43759         
43760         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43761         this.hiddenField.value = v;
43762         this.value = v;
43763     }
43764     
43765 });//<script type="text/javasscript">
43766  
43767
43768 /**
43769  * @class Roo.DDView
43770  * A DnD enabled version of Roo.View.
43771  * @param {Element/String} container The Element in which to create the View.
43772  * @param {String} tpl The template string used to create the markup for each element of the View
43773  * @param {Object} config The configuration properties. These include all the config options of
43774  * {@link Roo.View} plus some specific to this class.<br>
43775  * <p>
43776  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43777  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43778  * <p>
43779  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43780 .x-view-drag-insert-above {
43781         border-top:1px dotted #3366cc;
43782 }
43783 .x-view-drag-insert-below {
43784         border-bottom:1px dotted #3366cc;
43785 }
43786 </code></pre>
43787  * 
43788  */
43789  
43790 Roo.DDView = function(container, tpl, config) {
43791     Roo.DDView.superclass.constructor.apply(this, arguments);
43792     this.getEl().setStyle("outline", "0px none");
43793     this.getEl().unselectable();
43794     if (this.dragGroup) {
43795                 this.setDraggable(this.dragGroup.split(","));
43796     }
43797     if (this.dropGroup) {
43798                 this.setDroppable(this.dropGroup.split(","));
43799     }
43800     if (this.deletable) {
43801         this.setDeletable();
43802     }
43803     this.isDirtyFlag = false;
43804         this.addEvents({
43805                 "drop" : true
43806         });
43807 };
43808
43809 Roo.extend(Roo.DDView, Roo.View, {
43810 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43811 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43812 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43813 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43814
43815         isFormField: true,
43816
43817         reset: Roo.emptyFn,
43818         
43819         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43820
43821         validate: function() {
43822                 return true;
43823         },
43824         
43825         destroy: function() {
43826                 this.purgeListeners();
43827                 this.getEl.removeAllListeners();
43828                 this.getEl().remove();
43829                 if (this.dragZone) {
43830                         if (this.dragZone.destroy) {
43831                                 this.dragZone.destroy();
43832                         }
43833                 }
43834                 if (this.dropZone) {
43835                         if (this.dropZone.destroy) {
43836                                 this.dropZone.destroy();
43837                         }
43838                 }
43839         },
43840
43841 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43842         getName: function() {
43843                 return this.name;
43844         },
43845
43846 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43847         setValue: function(v) {
43848                 if (!this.store) {
43849                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43850                 }
43851                 var data = {};
43852                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43853                 this.store.proxy = new Roo.data.MemoryProxy(data);
43854                 this.store.load();
43855         },
43856
43857 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43858         getValue: function() {
43859                 var result = '(';
43860                 this.store.each(function(rec) {
43861                         result += rec.id + ',';
43862                 });
43863                 return result.substr(0, result.length - 1) + ')';
43864         },
43865         
43866         getIds: function() {
43867                 var i = 0, result = new Array(this.store.getCount());
43868                 this.store.each(function(rec) {
43869                         result[i++] = rec.id;
43870                 });
43871                 return result;
43872         },
43873         
43874         isDirty: function() {
43875                 return this.isDirtyFlag;
43876         },
43877
43878 /**
43879  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43880  *      whole Element becomes the target, and this causes the drop gesture to append.
43881  */
43882     getTargetFromEvent : function(e) {
43883                 var target = e.getTarget();
43884                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43885                 target = target.parentNode;
43886                 }
43887                 if (!target) {
43888                         target = this.el.dom.lastChild || this.el.dom;
43889                 }
43890                 return target;
43891     },
43892
43893 /**
43894  *      Create the drag data which consists of an object which has the property "ddel" as
43895  *      the drag proxy element. 
43896  */
43897     getDragData : function(e) {
43898         var target = this.findItemFromChild(e.getTarget());
43899                 if(target) {
43900                         this.handleSelection(e);
43901                         var selNodes = this.getSelectedNodes();
43902             var dragData = {
43903                 source: this,
43904                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43905                 nodes: selNodes,
43906                 records: []
43907                         };
43908                         var selectedIndices = this.getSelectedIndexes();
43909                         for (var i = 0; i < selectedIndices.length; i++) {
43910                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43911                         }
43912                         if (selNodes.length == 1) {
43913                                 dragData.ddel = target.cloneNode(true); // the div element
43914                         } else {
43915                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43916                                 div.className = 'multi-proxy';
43917                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43918                                         div.appendChild(selNodes[i].cloneNode(true));
43919                                 }
43920                                 dragData.ddel = div;
43921                         }
43922             //console.log(dragData)
43923             //console.log(dragData.ddel.innerHTML)
43924                         return dragData;
43925                 }
43926         //console.log('nodragData')
43927                 return false;
43928     },
43929     
43930 /**     Specify to which ddGroup items in this DDView may be dragged. */
43931     setDraggable: function(ddGroup) {
43932         if (ddGroup instanceof Array) {
43933                 Roo.each(ddGroup, this.setDraggable, this);
43934                 return;
43935         }
43936         if (this.dragZone) {
43937                 this.dragZone.addToGroup(ddGroup);
43938         } else {
43939                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43940                                 containerScroll: true,
43941                                 ddGroup: ddGroup 
43942
43943                         });
43944 //                      Draggability implies selection. DragZone's mousedown selects the element.
43945                         if (!this.multiSelect) { this.singleSelect = true; }
43946
43947 //                      Wire the DragZone's handlers up to methods in *this*
43948                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43949                 }
43950     },
43951
43952 /**     Specify from which ddGroup this DDView accepts drops. */
43953     setDroppable: function(ddGroup) {
43954         if (ddGroup instanceof Array) {
43955                 Roo.each(ddGroup, this.setDroppable, this);
43956                 return;
43957         }
43958         if (this.dropZone) {
43959                 this.dropZone.addToGroup(ddGroup);
43960         } else {
43961                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43962                                 containerScroll: true,
43963                                 ddGroup: ddGroup
43964                         });
43965
43966 //                      Wire the DropZone's handlers up to methods in *this*
43967                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43968                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43969                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43970                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43971                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43972                 }
43973     },
43974
43975 /**     Decide whether to drop above or below a View node. */
43976     getDropPoint : function(e, n, dd){
43977         if (n == this.el.dom) { return "above"; }
43978                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43979                 var c = t + (b - t) / 2;
43980                 var y = Roo.lib.Event.getPageY(e);
43981                 if(y <= c) {
43982                         return "above";
43983                 }else{
43984                         return "below";
43985                 }
43986     },
43987
43988     onNodeEnter : function(n, dd, e, data){
43989                 return false;
43990     },
43991     
43992     onNodeOver : function(n, dd, e, data){
43993                 var pt = this.getDropPoint(e, n, dd);
43994                 // set the insert point style on the target node
43995                 var dragElClass = this.dropNotAllowed;
43996                 if (pt) {
43997                         var targetElClass;
43998                         if (pt == "above"){
43999                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44000                                 targetElClass = "x-view-drag-insert-above";
44001                         } else {
44002                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44003                                 targetElClass = "x-view-drag-insert-below";
44004                         }
44005                         if (this.lastInsertClass != targetElClass){
44006                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44007                                 this.lastInsertClass = targetElClass;
44008                         }
44009                 }
44010                 return dragElClass;
44011         },
44012
44013     onNodeOut : function(n, dd, e, data){
44014                 this.removeDropIndicators(n);
44015     },
44016
44017     onNodeDrop : function(n, dd, e, data){
44018         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44019                 return false;
44020         }
44021         var pt = this.getDropPoint(e, n, dd);
44022                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44023                 if (pt == "below") { insertAt++; }
44024                 for (var i = 0; i < data.records.length; i++) {
44025                         var r = data.records[i];
44026                         var dup = this.store.getById(r.id);
44027                         if (dup && (dd != this.dragZone)) {
44028                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44029                         } else {
44030                                 if (data.copy) {
44031                                         this.store.insert(insertAt++, r.copy());
44032                                 } else {
44033                                         data.source.isDirtyFlag = true;
44034                                         r.store.remove(r);
44035                                         this.store.insert(insertAt++, r);
44036                                 }
44037                                 this.isDirtyFlag = true;
44038                         }
44039                 }
44040                 this.dragZone.cachedTarget = null;
44041                 return true;
44042     },
44043
44044     removeDropIndicators : function(n){
44045                 if(n){
44046                         Roo.fly(n).removeClass([
44047                                 "x-view-drag-insert-above",
44048                                 "x-view-drag-insert-below"]);
44049                         this.lastInsertClass = "_noclass";
44050                 }
44051     },
44052
44053 /**
44054  *      Utility method. Add a delete option to the DDView's context menu.
44055  *      @param {String} imageUrl The URL of the "delete" icon image.
44056  */
44057         setDeletable: function(imageUrl) {
44058                 if (!this.singleSelect && !this.multiSelect) {
44059                         this.singleSelect = true;
44060                 }
44061                 var c = this.getContextMenu();
44062                 this.contextMenu.on("itemclick", function(item) {
44063                         switch (item.id) {
44064                                 case "delete":
44065                                         this.remove(this.getSelectedIndexes());
44066                                         break;
44067                         }
44068                 }, this);
44069                 this.contextMenu.add({
44070                         icon: imageUrl,
44071                         id: "delete",
44072                         text: 'Delete'
44073                 });
44074         },
44075         
44076 /**     Return the context menu for this DDView. */
44077         getContextMenu: function() {
44078                 if (!this.contextMenu) {
44079 //                      Create the View's context menu
44080                         this.contextMenu = new Roo.menu.Menu({
44081                                 id: this.id + "-contextmenu"
44082                         });
44083                         this.el.on("contextmenu", this.showContextMenu, this);
44084                 }
44085                 return this.contextMenu;
44086         },
44087         
44088         disableContextMenu: function() {
44089                 if (this.contextMenu) {
44090                         this.el.un("contextmenu", this.showContextMenu, this);
44091                 }
44092         },
44093
44094         showContextMenu: function(e, item) {
44095         item = this.findItemFromChild(e.getTarget());
44096                 if (item) {
44097                         e.stopEvent();
44098                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44099                         this.contextMenu.showAt(e.getXY());
44100             }
44101     },
44102
44103 /**
44104  *      Remove {@link Roo.data.Record}s at the specified indices.
44105  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44106  */
44107     remove: function(selectedIndices) {
44108                 selectedIndices = [].concat(selectedIndices);
44109                 for (var i = 0; i < selectedIndices.length; i++) {
44110                         var rec = this.store.getAt(selectedIndices[i]);
44111                         this.store.remove(rec);
44112                 }
44113     },
44114
44115 /**
44116  *      Double click fires the event, but also, if this is draggable, and there is only one other
44117  *      related DropZone, it transfers the selected node.
44118  */
44119     onDblClick : function(e){
44120         var item = this.findItemFromChild(e.getTarget());
44121         if(item){
44122             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44123                 return false;
44124             }
44125             if (this.dragGroup) {
44126                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44127                     while (targets.indexOf(this.dropZone) > -1) {
44128                             targets.remove(this.dropZone);
44129                                 }
44130                     if (targets.length == 1) {
44131                                         this.dragZone.cachedTarget = null;
44132                         var el = Roo.get(targets[0].getEl());
44133                         var box = el.getBox(true);
44134                         targets[0].onNodeDrop(el.dom, {
44135                                 target: el.dom,
44136                                 xy: [box.x, box.y + box.height - 1]
44137                         }, null, this.getDragData(e));
44138                     }
44139                 }
44140         }
44141     },
44142     
44143     handleSelection: function(e) {
44144                 this.dragZone.cachedTarget = null;
44145         var item = this.findItemFromChild(e.getTarget());
44146         if (!item) {
44147                 this.clearSelections(true);
44148                 return;
44149         }
44150                 if (item && (this.multiSelect || this.singleSelect)){
44151                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44152                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44153                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44154                                 this.unselect(item);
44155                         } else {
44156                                 this.select(item, this.multiSelect && e.ctrlKey);
44157                                 this.lastSelection = item;
44158                         }
44159                 }
44160     },
44161
44162     onItemClick : function(item, index, e){
44163                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44164                         return false;
44165                 }
44166                 return true;
44167     },
44168
44169     unselect : function(nodeInfo, suppressEvent){
44170                 var node = this.getNode(nodeInfo);
44171                 if(node && this.isSelected(node)){
44172                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44173                                 Roo.fly(node).removeClass(this.selectedClass);
44174                                 this.selections.remove(node);
44175                                 if(!suppressEvent){
44176                                         this.fireEvent("selectionchange", this, this.selections);
44177                                 }
44178                         }
44179                 }
44180     }
44181 });
44182 /*
44183  * Based on:
44184  * Ext JS Library 1.1.1
44185  * Copyright(c) 2006-2007, Ext JS, LLC.
44186  *
44187  * Originally Released Under LGPL - original licence link has changed is not relivant.
44188  *
44189  * Fork - LGPL
44190  * <script type="text/javascript">
44191  */
44192  
44193 /**
44194  * @class Roo.LayoutManager
44195  * @extends Roo.util.Observable
44196  * Base class for layout managers.
44197  */
44198 Roo.LayoutManager = function(container, config){
44199     Roo.LayoutManager.superclass.constructor.call(this);
44200     this.el = Roo.get(container);
44201     // ie scrollbar fix
44202     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44203         document.body.scroll = "no";
44204     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44205         this.el.position('relative');
44206     }
44207     this.id = this.el.id;
44208     this.el.addClass("x-layout-container");
44209     /** false to disable window resize monitoring @type Boolean */
44210     this.monitorWindowResize = true;
44211     this.regions = {};
44212     this.addEvents({
44213         /**
44214          * @event layout
44215          * Fires when a layout is performed. 
44216          * @param {Roo.LayoutManager} this
44217          */
44218         "layout" : true,
44219         /**
44220          * @event regionresized
44221          * Fires when the user resizes a region. 
44222          * @param {Roo.LayoutRegion} region The resized region
44223          * @param {Number} newSize The new size (width for east/west, height for north/south)
44224          */
44225         "regionresized" : true,
44226         /**
44227          * @event regioncollapsed
44228          * Fires when a region is collapsed. 
44229          * @param {Roo.LayoutRegion} region The collapsed region
44230          */
44231         "regioncollapsed" : true,
44232         /**
44233          * @event regionexpanded
44234          * Fires when a region is expanded.  
44235          * @param {Roo.LayoutRegion} region The expanded region
44236          */
44237         "regionexpanded" : true
44238     });
44239     this.updating = false;
44240     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44241 };
44242
44243 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44244     /**
44245      * Returns true if this layout is currently being updated
44246      * @return {Boolean}
44247      */
44248     isUpdating : function(){
44249         return this.updating; 
44250     },
44251     
44252     /**
44253      * Suspend the LayoutManager from doing auto-layouts while
44254      * making multiple add or remove calls
44255      */
44256     beginUpdate : function(){
44257         this.updating = true;    
44258     },
44259     
44260     /**
44261      * Restore auto-layouts and optionally disable the manager from performing a layout
44262      * @param {Boolean} noLayout true to disable a layout update 
44263      */
44264     endUpdate : function(noLayout){
44265         this.updating = false;
44266         if(!noLayout){
44267             this.layout();
44268         }    
44269     },
44270     
44271     layout: function(){
44272         
44273     },
44274     
44275     onRegionResized : function(region, newSize){
44276         this.fireEvent("regionresized", region, newSize);
44277         this.layout();
44278     },
44279     
44280     onRegionCollapsed : function(region){
44281         this.fireEvent("regioncollapsed", region);
44282     },
44283     
44284     onRegionExpanded : function(region){
44285         this.fireEvent("regionexpanded", region);
44286     },
44287         
44288     /**
44289      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44290      * performs box-model adjustments.
44291      * @return {Object} The size as an object {width: (the width), height: (the height)}
44292      */
44293     getViewSize : function(){
44294         var size;
44295         if(this.el.dom != document.body){
44296             size = this.el.getSize();
44297         }else{
44298             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44299         }
44300         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44301         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44302         return size;
44303     },
44304     
44305     /**
44306      * Returns the Element this layout is bound to.
44307      * @return {Roo.Element}
44308      */
44309     getEl : function(){
44310         return this.el;
44311     },
44312     
44313     /**
44314      * Returns the specified region.
44315      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44316      * @return {Roo.LayoutRegion}
44317      */
44318     getRegion : function(target){
44319         return this.regions[target.toLowerCase()];
44320     },
44321     
44322     onWindowResize : function(){
44323         if(this.monitorWindowResize){
44324             this.layout();
44325         }
44326     }
44327 });/*
44328  * Based on:
44329  * Ext JS Library 1.1.1
44330  * Copyright(c) 2006-2007, Ext JS, LLC.
44331  *
44332  * Originally Released Under LGPL - original licence link has changed is not relivant.
44333  *
44334  * Fork - LGPL
44335  * <script type="text/javascript">
44336  */
44337 /**
44338  * @class Roo.BorderLayout
44339  * @extends Roo.LayoutManager
44340  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44341  * please see: <br><br>
44342  * <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>
44343  * <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>
44344  * Example:
44345  <pre><code>
44346  var layout = new Roo.BorderLayout(document.body, {
44347     north: {
44348         initialSize: 25,
44349         titlebar: false
44350     },
44351     west: {
44352         split:true,
44353         initialSize: 200,
44354         minSize: 175,
44355         maxSize: 400,
44356         titlebar: true,
44357         collapsible: true
44358     },
44359     east: {
44360         split:true,
44361         initialSize: 202,
44362         minSize: 175,
44363         maxSize: 400,
44364         titlebar: true,
44365         collapsible: true
44366     },
44367     south: {
44368         split:true,
44369         initialSize: 100,
44370         minSize: 100,
44371         maxSize: 200,
44372         titlebar: true,
44373         collapsible: true
44374     },
44375     center: {
44376         titlebar: true,
44377         autoScroll:true,
44378         resizeTabs: true,
44379         minTabWidth: 50,
44380         preferredTabWidth: 150
44381     }
44382 });
44383
44384 // shorthand
44385 var CP = Roo.ContentPanel;
44386
44387 layout.beginUpdate();
44388 layout.add("north", new CP("north", "North"));
44389 layout.add("south", new CP("south", {title: "South", closable: true}));
44390 layout.add("west", new CP("west", {title: "West"}));
44391 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44392 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44393 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44394 layout.getRegion("center").showPanel("center1");
44395 layout.endUpdate();
44396 </code></pre>
44397
44398 <b>The container the layout is rendered into can be either the body element or any other element.
44399 If it is not the body element, the container needs to either be an absolute positioned element,
44400 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44401 the container size if it is not the body element.</b>
44402
44403 * @constructor
44404 * Create a new BorderLayout
44405 * @param {String/HTMLElement/Element} container The container this layout is bound to
44406 * @param {Object} config Configuration options
44407  */
44408 Roo.BorderLayout = function(container, config){
44409     config = config || {};
44410     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44411     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44412     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44413         var target = this.factory.validRegions[i];
44414         if(config[target]){
44415             this.addRegion(target, config[target]);
44416         }
44417     }
44418 };
44419
44420 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44421     /**
44422      * Creates and adds a new region if it doesn't already exist.
44423      * @param {String} target The target region key (north, south, east, west or center).
44424      * @param {Object} config The regions config object
44425      * @return {BorderLayoutRegion} The new region
44426      */
44427     addRegion : function(target, config){
44428         if(!this.regions[target]){
44429             var r = this.factory.create(target, this, config);
44430             this.bindRegion(target, r);
44431         }
44432         return this.regions[target];
44433     },
44434
44435     // private (kinda)
44436     bindRegion : function(name, r){
44437         this.regions[name] = r;
44438         r.on("visibilitychange", this.layout, this);
44439         r.on("paneladded", this.layout, this);
44440         r.on("panelremoved", this.layout, this);
44441         r.on("invalidated", this.layout, this);
44442         r.on("resized", this.onRegionResized, this);
44443         r.on("collapsed", this.onRegionCollapsed, this);
44444         r.on("expanded", this.onRegionExpanded, this);
44445     },
44446
44447     /**
44448      * Performs a layout update.
44449      */
44450     layout : function(){
44451         if(this.updating) return;
44452         var size = this.getViewSize();
44453         var w = size.width;
44454         var h = size.height;
44455         var centerW = w;
44456         var centerH = h;
44457         var centerY = 0;
44458         var centerX = 0;
44459         //var x = 0, y = 0;
44460
44461         var rs = this.regions;
44462         var north = rs["north"];
44463         var south = rs["south"]; 
44464         var west = rs["west"];
44465         var east = rs["east"];
44466         var center = rs["center"];
44467         //if(this.hideOnLayout){ // not supported anymore
44468             //c.el.setStyle("display", "none");
44469         //}
44470         if(north && north.isVisible()){
44471             var b = north.getBox();
44472             var m = north.getMargins();
44473             b.width = w - (m.left+m.right);
44474             b.x = m.left;
44475             b.y = m.top;
44476             centerY = b.height + b.y + m.bottom;
44477             centerH -= centerY;
44478             north.updateBox(this.safeBox(b));
44479         }
44480         if(south && south.isVisible()){
44481             var b = south.getBox();
44482             var m = south.getMargins();
44483             b.width = w - (m.left+m.right);
44484             b.x = m.left;
44485             var totalHeight = (b.height + m.top + m.bottom);
44486             b.y = h - totalHeight + m.top;
44487             centerH -= totalHeight;
44488             south.updateBox(this.safeBox(b));
44489         }
44490         if(west && west.isVisible()){
44491             var b = west.getBox();
44492             var m = west.getMargins();
44493             b.height = centerH - (m.top+m.bottom);
44494             b.x = m.left;
44495             b.y = centerY + m.top;
44496             var totalWidth = (b.width + m.left + m.right);
44497             centerX += totalWidth;
44498             centerW -= totalWidth;
44499             west.updateBox(this.safeBox(b));
44500         }
44501         if(east && east.isVisible()){
44502             var b = east.getBox();
44503             var m = east.getMargins();
44504             b.height = centerH - (m.top+m.bottom);
44505             var totalWidth = (b.width + m.left + m.right);
44506             b.x = w - totalWidth + m.left;
44507             b.y = centerY + m.top;
44508             centerW -= totalWidth;
44509             east.updateBox(this.safeBox(b));
44510         }
44511         if(center){
44512             var m = center.getMargins();
44513             var centerBox = {
44514                 x: centerX + m.left,
44515                 y: centerY + m.top,
44516                 width: centerW - (m.left+m.right),
44517                 height: centerH - (m.top+m.bottom)
44518             };
44519             //if(this.hideOnLayout){
44520                 //center.el.setStyle("display", "block");
44521             //}
44522             center.updateBox(this.safeBox(centerBox));
44523         }
44524         this.el.repaint();
44525         this.fireEvent("layout", this);
44526     },
44527
44528     // private
44529     safeBox : function(box){
44530         box.width = Math.max(0, box.width);
44531         box.height = Math.max(0, box.height);
44532         return box;
44533     },
44534
44535     /**
44536      * Adds a ContentPanel (or subclass) to this layout.
44537      * @param {String} target The target region key (north, south, east, west or center).
44538      * @param {Roo.ContentPanel} panel The panel to add
44539      * @return {Roo.ContentPanel} The added panel
44540      */
44541     add : function(target, panel){
44542          
44543         target = target.toLowerCase();
44544         return this.regions[target].add(panel);
44545     },
44546
44547     /**
44548      * Remove a ContentPanel (or subclass) to this layout.
44549      * @param {String} target The target region key (north, south, east, west or center).
44550      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44551      * @return {Roo.ContentPanel} The removed panel
44552      */
44553     remove : function(target, panel){
44554         target = target.toLowerCase();
44555         return this.regions[target].remove(panel);
44556     },
44557
44558     /**
44559      * Searches all regions for a panel with the specified id
44560      * @param {String} panelId
44561      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44562      */
44563     findPanel : function(panelId){
44564         var rs = this.regions;
44565         for(var target in rs){
44566             if(typeof rs[target] != "function"){
44567                 var p = rs[target].getPanel(panelId);
44568                 if(p){
44569                     return p;
44570                 }
44571             }
44572         }
44573         return null;
44574     },
44575
44576     /**
44577      * Searches all regions for a panel with the specified id and activates (shows) it.
44578      * @param {String/ContentPanel} panelId The panels id or the panel itself
44579      * @return {Roo.ContentPanel} The shown panel or null
44580      */
44581     showPanel : function(panelId) {
44582       var rs = this.regions;
44583       for(var target in rs){
44584          var r = rs[target];
44585          if(typeof r != "function"){
44586             if(r.hasPanel(panelId)){
44587                return r.showPanel(panelId);
44588             }
44589          }
44590       }
44591       return null;
44592    },
44593
44594    /**
44595      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44596      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44597      */
44598     restoreState : function(provider){
44599         if(!provider){
44600             provider = Roo.state.Manager;
44601         }
44602         var sm = new Roo.LayoutStateManager();
44603         sm.init(this, provider);
44604     },
44605
44606     /**
44607      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44608      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44609      * a valid ContentPanel config object.  Example:
44610      * <pre><code>
44611 // Create the main layout
44612 var layout = new Roo.BorderLayout('main-ct', {
44613     west: {
44614         split:true,
44615         minSize: 175,
44616         titlebar: true
44617     },
44618     center: {
44619         title:'Components'
44620     }
44621 }, 'main-ct');
44622
44623 // Create and add multiple ContentPanels at once via configs
44624 layout.batchAdd({
44625    west: {
44626        id: 'source-files',
44627        autoCreate:true,
44628        title:'Ext Source Files',
44629        autoScroll:true,
44630        fitToFrame:true
44631    },
44632    center : {
44633        el: cview,
44634        autoScroll:true,
44635        fitToFrame:true,
44636        toolbar: tb,
44637        resizeEl:'cbody'
44638    }
44639 });
44640 </code></pre>
44641      * @param {Object} regions An object containing ContentPanel configs by region name
44642      */
44643     batchAdd : function(regions){
44644         this.beginUpdate();
44645         for(var rname in regions){
44646             var lr = this.regions[rname];
44647             if(lr){
44648                 this.addTypedPanels(lr, regions[rname]);
44649             }
44650         }
44651         this.endUpdate();
44652     },
44653
44654     // private
44655     addTypedPanels : function(lr, ps){
44656         if(typeof ps == 'string'){
44657             lr.add(new Roo.ContentPanel(ps));
44658         }
44659         else if(ps instanceof Array){
44660             for(var i =0, len = ps.length; i < len; i++){
44661                 this.addTypedPanels(lr, ps[i]);
44662             }
44663         }
44664         else if(!ps.events){ // raw config?
44665             var el = ps.el;
44666             delete ps.el; // prevent conflict
44667             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44668         }
44669         else {  // panel object assumed!
44670             lr.add(ps);
44671         }
44672     },
44673     /**
44674      * Adds a xtype elements to the layout.
44675      * <pre><code>
44676
44677 layout.addxtype({
44678        xtype : 'ContentPanel',
44679        region: 'west',
44680        items: [ .... ]
44681    }
44682 );
44683
44684 layout.addxtype({
44685         xtype : 'NestedLayoutPanel',
44686         region: 'west',
44687         layout: {
44688            center: { },
44689            west: { }   
44690         },
44691         items : [ ... list of content panels or nested layout panels.. ]
44692    }
44693 );
44694 </code></pre>
44695      * @param {Object} cfg Xtype definition of item to add.
44696      */
44697     addxtype : function(cfg)
44698     {
44699         // basically accepts a pannel...
44700         // can accept a layout region..!?!?
44701         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44702         
44703         if (!cfg.xtype.match(/Panel$/)) {
44704             return false;
44705         }
44706         var ret = false;
44707         
44708         if (typeof(cfg.region) == 'undefined') {
44709             Roo.log("Failed to add Panel, region was not set");
44710             Roo.log(cfg);
44711             return false;
44712         }
44713         var region = cfg.region;
44714         delete cfg.region;
44715         
44716           
44717         var xitems = [];
44718         if (cfg.items) {
44719             xitems = cfg.items;
44720             delete cfg.items;
44721         }
44722         var nb = false;
44723         
44724         switch(cfg.xtype) 
44725         {
44726             case 'ContentPanel':  // ContentPanel (el, cfg)
44727             case 'ScrollPanel':  // ContentPanel (el, cfg)
44728                 if(cfg.autoCreate) {
44729                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44730                 } else {
44731                     var el = this.el.createChild();
44732                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44733                 }
44734                 
44735                 this.add(region, ret);
44736                 break;
44737             
44738             
44739             case 'TreePanel': // our new panel!
44740                 cfg.el = this.el.createChild();
44741                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44742                 this.add(region, ret);
44743                 break;
44744             
44745             case 'NestedLayoutPanel': 
44746                 // create a new Layout (which is  a Border Layout...
44747                 var el = this.el.createChild();
44748                 var clayout = cfg.layout;
44749                 delete cfg.layout;
44750                 clayout.items   = clayout.items  || [];
44751                 // replace this exitems with the clayout ones..
44752                 xitems = clayout.items;
44753                  
44754                 
44755                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44756                     cfg.background = false;
44757                 }
44758                 var layout = new Roo.BorderLayout(el, clayout);
44759                 
44760                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44761                 //console.log('adding nested layout panel '  + cfg.toSource());
44762                 this.add(region, ret);
44763                 nb = {}; /// find first...
44764                 break;
44765                 
44766             case 'GridPanel': 
44767             
44768                 // needs grid and region
44769                 
44770                 //var el = this.getRegion(region).el.createChild();
44771                 var el = this.el.createChild();
44772                 // create the grid first...
44773                 
44774                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44775                 delete cfg.grid;
44776                 if (region == 'center' && this.active ) {
44777                     cfg.background = false;
44778                 }
44779                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44780                 
44781                 this.add(region, ret);
44782                 if (cfg.background) {
44783                     ret.on('activate', function(gp) {
44784                         if (!gp.grid.rendered) {
44785                             gp.grid.render();
44786                         }
44787                     });
44788                 } else {
44789                     grid.render();
44790                 }
44791                 break;
44792            
44793                
44794                 
44795                 
44796             default: 
44797                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44798                 return null;
44799              // GridPanel (grid, cfg)
44800             
44801         }
44802         this.beginUpdate();
44803         // add children..
44804         var region = '';
44805         var abn = {};
44806         Roo.each(xitems, function(i)  {
44807             region = nb && i.region ? i.region : false;
44808             
44809             var add = ret.addxtype(i);
44810            
44811             if (region) {
44812                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44813                 if (!i.background) {
44814                     abn[region] = nb[region] ;
44815                 }
44816             }
44817             
44818         });
44819         this.endUpdate();
44820
44821         // make the last non-background panel active..
44822         //if (nb) { Roo.log(abn); }
44823         if (nb) {
44824             
44825             for(var r in abn) {
44826                 region = this.getRegion(r);
44827                 if (region) {
44828                     // tried using nb[r], but it does not work..
44829                      
44830                     region.showPanel(abn[r]);
44831                    
44832                 }
44833             }
44834         }
44835         return ret;
44836         
44837     }
44838 });
44839
44840 /**
44841  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44842  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44843  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44844  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44845  * <pre><code>
44846 // shorthand
44847 var CP = Roo.ContentPanel;
44848
44849 var layout = Roo.BorderLayout.create({
44850     north: {
44851         initialSize: 25,
44852         titlebar: false,
44853         panels: [new CP("north", "North")]
44854     },
44855     west: {
44856         split:true,
44857         initialSize: 200,
44858         minSize: 175,
44859         maxSize: 400,
44860         titlebar: true,
44861         collapsible: true,
44862         panels: [new CP("west", {title: "West"})]
44863     },
44864     east: {
44865         split:true,
44866         initialSize: 202,
44867         minSize: 175,
44868         maxSize: 400,
44869         titlebar: true,
44870         collapsible: true,
44871         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44872     },
44873     south: {
44874         split:true,
44875         initialSize: 100,
44876         minSize: 100,
44877         maxSize: 200,
44878         titlebar: true,
44879         collapsible: true,
44880         panels: [new CP("south", {title: "South", closable: true})]
44881     },
44882     center: {
44883         titlebar: true,
44884         autoScroll:true,
44885         resizeTabs: true,
44886         minTabWidth: 50,
44887         preferredTabWidth: 150,
44888         panels: [
44889             new CP("center1", {title: "Close Me", closable: true}),
44890             new CP("center2", {title: "Center Panel", closable: false})
44891         ]
44892     }
44893 }, document.body);
44894
44895 layout.getRegion("center").showPanel("center1");
44896 </code></pre>
44897  * @param config
44898  * @param targetEl
44899  */
44900 Roo.BorderLayout.create = function(config, targetEl){
44901     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44902     layout.beginUpdate();
44903     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44904     for(var j = 0, jlen = regions.length; j < jlen; j++){
44905         var lr = regions[j];
44906         if(layout.regions[lr] && config[lr].panels){
44907             var r = layout.regions[lr];
44908             var ps = config[lr].panels;
44909             layout.addTypedPanels(r, ps);
44910         }
44911     }
44912     layout.endUpdate();
44913     return layout;
44914 };
44915
44916 // private
44917 Roo.BorderLayout.RegionFactory = {
44918     // private
44919     validRegions : ["north","south","east","west","center"],
44920
44921     // private
44922     create : function(target, mgr, config){
44923         target = target.toLowerCase();
44924         if(config.lightweight || config.basic){
44925             return new Roo.BasicLayoutRegion(mgr, config, target);
44926         }
44927         switch(target){
44928             case "north":
44929                 return new Roo.NorthLayoutRegion(mgr, config);
44930             case "south":
44931                 return new Roo.SouthLayoutRegion(mgr, config);
44932             case "east":
44933                 return new Roo.EastLayoutRegion(mgr, config);
44934             case "west":
44935                 return new Roo.WestLayoutRegion(mgr, config);
44936             case "center":
44937                 return new Roo.CenterLayoutRegion(mgr, config);
44938         }
44939         throw 'Layout region "'+target+'" not supported.';
44940     }
44941 };/*
44942  * Based on:
44943  * Ext JS Library 1.1.1
44944  * Copyright(c) 2006-2007, Ext JS, LLC.
44945  *
44946  * Originally Released Under LGPL - original licence link has changed is not relivant.
44947  *
44948  * Fork - LGPL
44949  * <script type="text/javascript">
44950  */
44951  
44952 /**
44953  * @class Roo.BasicLayoutRegion
44954  * @extends Roo.util.Observable
44955  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44956  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44957  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44958  */
44959 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44960     this.mgr = mgr;
44961     this.position  = pos;
44962     this.events = {
44963         /**
44964          * @scope Roo.BasicLayoutRegion
44965          */
44966         
44967         /**
44968          * @event beforeremove
44969          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44970          * @param {Roo.LayoutRegion} this
44971          * @param {Roo.ContentPanel} panel The panel
44972          * @param {Object} e The cancel event object
44973          */
44974         "beforeremove" : true,
44975         /**
44976          * @event invalidated
44977          * Fires when the layout for this region is changed.
44978          * @param {Roo.LayoutRegion} this
44979          */
44980         "invalidated" : true,
44981         /**
44982          * @event visibilitychange
44983          * Fires when this region is shown or hidden 
44984          * @param {Roo.LayoutRegion} this
44985          * @param {Boolean} visibility true or false
44986          */
44987         "visibilitychange" : true,
44988         /**
44989          * @event paneladded
44990          * Fires when a panel is added. 
44991          * @param {Roo.LayoutRegion} this
44992          * @param {Roo.ContentPanel} panel The panel
44993          */
44994         "paneladded" : true,
44995         /**
44996          * @event panelremoved
44997          * Fires when a panel is removed. 
44998          * @param {Roo.LayoutRegion} this
44999          * @param {Roo.ContentPanel} panel The panel
45000          */
45001         "panelremoved" : true,
45002         /**
45003          * @event collapsed
45004          * Fires when this region is collapsed.
45005          * @param {Roo.LayoutRegion} this
45006          */
45007         "collapsed" : true,
45008         /**
45009          * @event expanded
45010          * Fires when this region is expanded.
45011          * @param {Roo.LayoutRegion} this
45012          */
45013         "expanded" : true,
45014         /**
45015          * @event slideshow
45016          * Fires when this region is slid into view.
45017          * @param {Roo.LayoutRegion} this
45018          */
45019         "slideshow" : true,
45020         /**
45021          * @event slidehide
45022          * Fires when this region slides out of view. 
45023          * @param {Roo.LayoutRegion} this
45024          */
45025         "slidehide" : true,
45026         /**
45027          * @event panelactivated
45028          * Fires when a panel is activated. 
45029          * @param {Roo.LayoutRegion} this
45030          * @param {Roo.ContentPanel} panel The activated panel
45031          */
45032         "panelactivated" : true,
45033         /**
45034          * @event resized
45035          * Fires when the user resizes this region. 
45036          * @param {Roo.LayoutRegion} this
45037          * @param {Number} newSize The new size (width for east/west, height for north/south)
45038          */
45039         "resized" : true
45040     };
45041     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45042     this.panels = new Roo.util.MixedCollection();
45043     this.panels.getKey = this.getPanelId.createDelegate(this);
45044     this.box = null;
45045     this.activePanel = null;
45046     // ensure listeners are added...
45047     
45048     if (config.listeners || config.events) {
45049         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45050             listeners : config.listeners || {},
45051             events : config.events || {}
45052         });
45053     }
45054     
45055     if(skipConfig !== true){
45056         this.applyConfig(config);
45057     }
45058 };
45059
45060 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45061     getPanelId : function(p){
45062         return p.getId();
45063     },
45064     
45065     applyConfig : function(config){
45066         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45067         this.config = config;
45068         
45069     },
45070     
45071     /**
45072      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45073      * the width, for horizontal (north, south) the height.
45074      * @param {Number} newSize The new width or height
45075      */
45076     resizeTo : function(newSize){
45077         var el = this.el ? this.el :
45078                  (this.activePanel ? this.activePanel.getEl() : null);
45079         if(el){
45080             switch(this.position){
45081                 case "east":
45082                 case "west":
45083                     el.setWidth(newSize);
45084                     this.fireEvent("resized", this, newSize);
45085                 break;
45086                 case "north":
45087                 case "south":
45088                     el.setHeight(newSize);
45089                     this.fireEvent("resized", this, newSize);
45090                 break;                
45091             }
45092         }
45093     },
45094     
45095     getBox : function(){
45096         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45097     },
45098     
45099     getMargins : function(){
45100         return this.margins;
45101     },
45102     
45103     updateBox : function(box){
45104         this.box = box;
45105         var el = this.activePanel.getEl();
45106         el.dom.style.left = box.x + "px";
45107         el.dom.style.top = box.y + "px";
45108         this.activePanel.setSize(box.width, box.height);
45109     },
45110     
45111     /**
45112      * Returns the container element for this region.
45113      * @return {Roo.Element}
45114      */
45115     getEl : function(){
45116         return this.activePanel;
45117     },
45118     
45119     /**
45120      * Returns true if this region is currently visible.
45121      * @return {Boolean}
45122      */
45123     isVisible : function(){
45124         return this.activePanel ? true : false;
45125     },
45126     
45127     setActivePanel : function(panel){
45128         panel = this.getPanel(panel);
45129         if(this.activePanel && this.activePanel != panel){
45130             this.activePanel.setActiveState(false);
45131             this.activePanel.getEl().setLeftTop(-10000,-10000);
45132         }
45133         this.activePanel = panel;
45134         panel.setActiveState(true);
45135         if(this.box){
45136             panel.setSize(this.box.width, this.box.height);
45137         }
45138         this.fireEvent("panelactivated", this, panel);
45139         this.fireEvent("invalidated");
45140     },
45141     
45142     /**
45143      * Show the specified panel.
45144      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45145      * @return {Roo.ContentPanel} The shown panel or null
45146      */
45147     showPanel : function(panel){
45148         if(panel = this.getPanel(panel)){
45149             this.setActivePanel(panel);
45150         }
45151         return panel;
45152     },
45153     
45154     /**
45155      * Get the active panel for this region.
45156      * @return {Roo.ContentPanel} The active panel or null
45157      */
45158     getActivePanel : function(){
45159         return this.activePanel;
45160     },
45161     
45162     /**
45163      * Add the passed ContentPanel(s)
45164      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45165      * @return {Roo.ContentPanel} The panel added (if only one was added)
45166      */
45167     add : function(panel){
45168         if(arguments.length > 1){
45169             for(var i = 0, len = arguments.length; i < len; i++) {
45170                 this.add(arguments[i]);
45171             }
45172             return null;
45173         }
45174         if(this.hasPanel(panel)){
45175             this.showPanel(panel);
45176             return panel;
45177         }
45178         var el = panel.getEl();
45179         if(el.dom.parentNode != this.mgr.el.dom){
45180             this.mgr.el.dom.appendChild(el.dom);
45181         }
45182         if(panel.setRegion){
45183             panel.setRegion(this);
45184         }
45185         this.panels.add(panel);
45186         el.setStyle("position", "absolute");
45187         if(!panel.background){
45188             this.setActivePanel(panel);
45189             if(this.config.initialSize && this.panels.getCount()==1){
45190                 this.resizeTo(this.config.initialSize);
45191             }
45192         }
45193         this.fireEvent("paneladded", this, panel);
45194         return panel;
45195     },
45196     
45197     /**
45198      * Returns true if the panel is in this region.
45199      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45200      * @return {Boolean}
45201      */
45202     hasPanel : function(panel){
45203         if(typeof panel == "object"){ // must be panel obj
45204             panel = panel.getId();
45205         }
45206         return this.getPanel(panel) ? true : false;
45207     },
45208     
45209     /**
45210      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45211      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45212      * @param {Boolean} preservePanel Overrides the config preservePanel option
45213      * @return {Roo.ContentPanel} The panel that was removed
45214      */
45215     remove : function(panel, preservePanel){
45216         panel = this.getPanel(panel);
45217         if(!panel){
45218             return null;
45219         }
45220         var e = {};
45221         this.fireEvent("beforeremove", this, panel, e);
45222         if(e.cancel === true){
45223             return null;
45224         }
45225         var panelId = panel.getId();
45226         this.panels.removeKey(panelId);
45227         return panel;
45228     },
45229     
45230     /**
45231      * Returns the panel specified or null if it's not in this region.
45232      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45233      * @return {Roo.ContentPanel}
45234      */
45235     getPanel : function(id){
45236         if(typeof id == "object"){ // must be panel obj
45237             return id;
45238         }
45239         return this.panels.get(id);
45240     },
45241     
45242     /**
45243      * Returns this regions position (north/south/east/west/center).
45244      * @return {String} 
45245      */
45246     getPosition: function(){
45247         return this.position;    
45248     }
45249 });/*
45250  * Based on:
45251  * Ext JS Library 1.1.1
45252  * Copyright(c) 2006-2007, Ext JS, LLC.
45253  *
45254  * Originally Released Under LGPL - original licence link has changed is not relivant.
45255  *
45256  * Fork - LGPL
45257  * <script type="text/javascript">
45258  */
45259  
45260 /**
45261  * @class Roo.LayoutRegion
45262  * @extends Roo.BasicLayoutRegion
45263  * This class represents a region in a layout manager.
45264  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45265  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45266  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45267  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45268  * @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})
45269  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45270  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45271  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45272  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45273  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45274  * @cfg {String}    title           The title for the region (overrides panel titles)
45275  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45276  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45277  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45278  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45279  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45280  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45281  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45282  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45283  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45284  * @cfg {Boolean}   showPin         True to show a pin button
45285  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45286  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45287  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45288  * @cfg {Number}    width           For East/West panels
45289  * @cfg {Number}    height          For North/South panels
45290  * @cfg {Boolean}   split           To show the splitter
45291  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45292  */
45293 Roo.LayoutRegion = function(mgr, config, pos){
45294     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45295     var dh = Roo.DomHelper;
45296     /** This region's container element 
45297     * @type Roo.Element */
45298     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45299     /** This region's title element 
45300     * @type Roo.Element */
45301
45302     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45303         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45304         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45305     ]}, true);
45306     this.titleEl.enableDisplayMode();
45307     /** This region's title text element 
45308     * @type HTMLElement */
45309     this.titleTextEl = this.titleEl.dom.firstChild;
45310     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45311     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45312     this.closeBtn.enableDisplayMode();
45313     this.closeBtn.on("click", this.closeClicked, this);
45314     this.closeBtn.hide();
45315
45316     this.createBody(config);
45317     this.visible = true;
45318     this.collapsed = false;
45319
45320     if(config.hideWhenEmpty){
45321         this.hide();
45322         this.on("paneladded", this.validateVisibility, this);
45323         this.on("panelremoved", this.validateVisibility, this);
45324     }
45325     this.applyConfig(config);
45326 };
45327
45328 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45329
45330     createBody : function(){
45331         /** This region's body element 
45332         * @type Roo.Element */
45333         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45334     },
45335
45336     applyConfig : function(c){
45337         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45338             var dh = Roo.DomHelper;
45339             if(c.titlebar !== false){
45340                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45341                 this.collapseBtn.on("click", this.collapse, this);
45342                 this.collapseBtn.enableDisplayMode();
45343
45344                 if(c.showPin === true || this.showPin){
45345                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45346                     this.stickBtn.enableDisplayMode();
45347                     this.stickBtn.on("click", this.expand, this);
45348                     this.stickBtn.hide();
45349                 }
45350             }
45351             /** This region's collapsed element
45352             * @type Roo.Element */
45353             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45354                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45355             ]}, true);
45356             if(c.floatable !== false){
45357                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45358                this.collapsedEl.on("click", this.collapseClick, this);
45359             }
45360
45361             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45362                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45363                    id: "message", unselectable: "on", style:{"float":"left"}});
45364                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45365              }
45366             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45367             this.expandBtn.on("click", this.expand, this);
45368         }
45369         if(this.collapseBtn){
45370             this.collapseBtn.setVisible(c.collapsible == true);
45371         }
45372         this.cmargins = c.cmargins || this.cmargins ||
45373                          (this.position == "west" || this.position == "east" ?
45374                              {top: 0, left: 2, right:2, bottom: 0} :
45375                              {top: 2, left: 0, right:0, bottom: 2});
45376         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45377         this.bottomTabs = c.tabPosition != "top";
45378         this.autoScroll = c.autoScroll || false;
45379         if(this.autoScroll){
45380             this.bodyEl.setStyle("overflow", "auto");
45381         }else{
45382             this.bodyEl.setStyle("overflow", "hidden");
45383         }
45384         //if(c.titlebar !== false){
45385             if((!c.titlebar && !c.title) || c.titlebar === false){
45386                 this.titleEl.hide();
45387             }else{
45388                 this.titleEl.show();
45389                 if(c.title){
45390                     this.titleTextEl.innerHTML = c.title;
45391                 }
45392             }
45393         //}
45394         this.duration = c.duration || .30;
45395         this.slideDuration = c.slideDuration || .45;
45396         this.config = c;
45397         if(c.collapsed){
45398             this.collapse(true);
45399         }
45400         if(c.hidden){
45401             this.hide();
45402         }
45403     },
45404     /**
45405      * Returns true if this region is currently visible.
45406      * @return {Boolean}
45407      */
45408     isVisible : function(){
45409         return this.visible;
45410     },
45411
45412     /**
45413      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45414      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45415      */
45416     setCollapsedTitle : function(title){
45417         title = title || "&#160;";
45418         if(this.collapsedTitleTextEl){
45419             this.collapsedTitleTextEl.innerHTML = title;
45420         }
45421     },
45422
45423     getBox : function(){
45424         var b;
45425         if(!this.collapsed){
45426             b = this.el.getBox(false, true);
45427         }else{
45428             b = this.collapsedEl.getBox(false, true);
45429         }
45430         return b;
45431     },
45432
45433     getMargins : function(){
45434         return this.collapsed ? this.cmargins : this.margins;
45435     },
45436
45437     highlight : function(){
45438         this.el.addClass("x-layout-panel-dragover");
45439     },
45440
45441     unhighlight : function(){
45442         this.el.removeClass("x-layout-panel-dragover");
45443     },
45444
45445     updateBox : function(box){
45446         this.box = box;
45447         if(!this.collapsed){
45448             this.el.dom.style.left = box.x + "px";
45449             this.el.dom.style.top = box.y + "px";
45450             this.updateBody(box.width, box.height);
45451         }else{
45452             this.collapsedEl.dom.style.left = box.x + "px";
45453             this.collapsedEl.dom.style.top = box.y + "px";
45454             this.collapsedEl.setSize(box.width, box.height);
45455         }
45456         if(this.tabs){
45457             this.tabs.autoSizeTabs();
45458         }
45459     },
45460
45461     updateBody : function(w, h){
45462         if(w !== null){
45463             this.el.setWidth(w);
45464             w -= this.el.getBorderWidth("rl");
45465             if(this.config.adjustments){
45466                 w += this.config.adjustments[0];
45467             }
45468         }
45469         if(h !== null){
45470             this.el.setHeight(h);
45471             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45472             h -= this.el.getBorderWidth("tb");
45473             if(this.config.adjustments){
45474                 h += this.config.adjustments[1];
45475             }
45476             this.bodyEl.setHeight(h);
45477             if(this.tabs){
45478                 h = this.tabs.syncHeight(h);
45479             }
45480         }
45481         if(this.panelSize){
45482             w = w !== null ? w : this.panelSize.width;
45483             h = h !== null ? h : this.panelSize.height;
45484         }
45485         if(this.activePanel){
45486             var el = this.activePanel.getEl();
45487             w = w !== null ? w : el.getWidth();
45488             h = h !== null ? h : el.getHeight();
45489             this.panelSize = {width: w, height: h};
45490             this.activePanel.setSize(w, h);
45491         }
45492         if(Roo.isIE && this.tabs){
45493             this.tabs.el.repaint();
45494         }
45495     },
45496
45497     /**
45498      * Returns the container element for this region.
45499      * @return {Roo.Element}
45500      */
45501     getEl : function(){
45502         return this.el;
45503     },
45504
45505     /**
45506      * Hides this region.
45507      */
45508     hide : function(){
45509         if(!this.collapsed){
45510             this.el.dom.style.left = "-2000px";
45511             this.el.hide();
45512         }else{
45513             this.collapsedEl.dom.style.left = "-2000px";
45514             this.collapsedEl.hide();
45515         }
45516         this.visible = false;
45517         this.fireEvent("visibilitychange", this, false);
45518     },
45519
45520     /**
45521      * Shows this region if it was previously hidden.
45522      */
45523     show : function(){
45524         if(!this.collapsed){
45525             this.el.show();
45526         }else{
45527             this.collapsedEl.show();
45528         }
45529         this.visible = true;
45530         this.fireEvent("visibilitychange", this, true);
45531     },
45532
45533     closeClicked : function(){
45534         if(this.activePanel){
45535             this.remove(this.activePanel);
45536         }
45537     },
45538
45539     collapseClick : function(e){
45540         if(this.isSlid){
45541            e.stopPropagation();
45542            this.slideIn();
45543         }else{
45544            e.stopPropagation();
45545            this.slideOut();
45546         }
45547     },
45548
45549     /**
45550      * Collapses this region.
45551      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45552      */
45553     collapse : function(skipAnim){
45554         if(this.collapsed) return;
45555         this.collapsed = true;
45556         if(this.split){
45557             this.split.el.hide();
45558         }
45559         if(this.config.animate && skipAnim !== true){
45560             this.fireEvent("invalidated", this);
45561             this.animateCollapse();
45562         }else{
45563             this.el.setLocation(-20000,-20000);
45564             this.el.hide();
45565             this.collapsedEl.show();
45566             this.fireEvent("collapsed", this);
45567             this.fireEvent("invalidated", this);
45568         }
45569     },
45570
45571     animateCollapse : function(){
45572         // overridden
45573     },
45574
45575     /**
45576      * Expands this region if it was previously collapsed.
45577      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45578      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45579      */
45580     expand : function(e, skipAnim){
45581         if(e) e.stopPropagation();
45582         if(!this.collapsed || this.el.hasActiveFx()) return;
45583         if(this.isSlid){
45584             this.afterSlideIn();
45585             skipAnim = true;
45586         }
45587         this.collapsed = false;
45588         if(this.config.animate && skipAnim !== true){
45589             this.animateExpand();
45590         }else{
45591             this.el.show();
45592             if(this.split){
45593                 this.split.el.show();
45594             }
45595             this.collapsedEl.setLocation(-2000,-2000);
45596             this.collapsedEl.hide();
45597             this.fireEvent("invalidated", this);
45598             this.fireEvent("expanded", this);
45599         }
45600     },
45601
45602     animateExpand : function(){
45603         // overridden
45604     },
45605
45606     initTabs : function()
45607     {
45608         this.bodyEl.setStyle("overflow", "hidden");
45609         var ts = new Roo.TabPanel(
45610                 this.bodyEl.dom,
45611                 {
45612                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45613                     disableTooltips: this.config.disableTabTips,
45614                     toolbar : this.config.toolbar
45615                 }
45616         );
45617         if(this.config.hideTabs){
45618             ts.stripWrap.setDisplayed(false);
45619         }
45620         this.tabs = ts;
45621         ts.resizeTabs = this.config.resizeTabs === true;
45622         ts.minTabWidth = this.config.minTabWidth || 40;
45623         ts.maxTabWidth = this.config.maxTabWidth || 250;
45624         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45625         ts.monitorResize = false;
45626         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45627         ts.bodyEl.addClass('x-layout-tabs-body');
45628         this.panels.each(this.initPanelAsTab, this);
45629     },
45630
45631     initPanelAsTab : function(panel){
45632         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45633                     this.config.closeOnTab && panel.isClosable());
45634         if(panel.tabTip !== undefined){
45635             ti.setTooltip(panel.tabTip);
45636         }
45637         ti.on("activate", function(){
45638               this.setActivePanel(panel);
45639         }, this);
45640         if(this.config.closeOnTab){
45641             ti.on("beforeclose", function(t, e){
45642                 e.cancel = true;
45643                 this.remove(panel);
45644             }, this);
45645         }
45646         return ti;
45647     },
45648
45649     updatePanelTitle : function(panel, title){
45650         if(this.activePanel == panel){
45651             this.updateTitle(title);
45652         }
45653         if(this.tabs){
45654             var ti = this.tabs.getTab(panel.getEl().id);
45655             ti.setText(title);
45656             if(panel.tabTip !== undefined){
45657                 ti.setTooltip(panel.tabTip);
45658             }
45659         }
45660     },
45661
45662     updateTitle : function(title){
45663         if(this.titleTextEl && !this.config.title){
45664             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45665         }
45666     },
45667
45668     setActivePanel : function(panel){
45669         panel = this.getPanel(panel);
45670         if(this.activePanel && this.activePanel != panel){
45671             this.activePanel.setActiveState(false);
45672         }
45673         this.activePanel = panel;
45674         panel.setActiveState(true);
45675         if(this.panelSize){
45676             panel.setSize(this.panelSize.width, this.panelSize.height);
45677         }
45678         if(this.closeBtn){
45679             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45680         }
45681         this.updateTitle(panel.getTitle());
45682         if(this.tabs){
45683             this.fireEvent("invalidated", this);
45684         }
45685         this.fireEvent("panelactivated", this, panel);
45686     },
45687
45688     /**
45689      * Shows the specified panel.
45690      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45691      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45692      */
45693     showPanel : function(panel){
45694         if(panel = this.getPanel(panel)){
45695             if(this.tabs){
45696                 var tab = this.tabs.getTab(panel.getEl().id);
45697                 if(tab.isHidden()){
45698                     this.tabs.unhideTab(tab.id);
45699                 }
45700                 tab.activate();
45701             }else{
45702                 this.setActivePanel(panel);
45703             }
45704         }
45705         return panel;
45706     },
45707
45708     /**
45709      * Get the active panel for this region.
45710      * @return {Roo.ContentPanel} The active panel or null
45711      */
45712     getActivePanel : function(){
45713         return this.activePanel;
45714     },
45715
45716     validateVisibility : function(){
45717         if(this.panels.getCount() < 1){
45718             this.updateTitle("&#160;");
45719             this.closeBtn.hide();
45720             this.hide();
45721         }else{
45722             if(!this.isVisible()){
45723                 this.show();
45724             }
45725         }
45726     },
45727
45728     /**
45729      * Adds the passed ContentPanel(s) to this region.
45730      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45731      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45732      */
45733     add : function(panel){
45734         if(arguments.length > 1){
45735             for(var i = 0, len = arguments.length; i < len; i++) {
45736                 this.add(arguments[i]);
45737             }
45738             return null;
45739         }
45740         if(this.hasPanel(panel)){
45741             this.showPanel(panel);
45742             return panel;
45743         }
45744         panel.setRegion(this);
45745         this.panels.add(panel);
45746         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45747             this.bodyEl.dom.appendChild(panel.getEl().dom);
45748             if(panel.background !== true){
45749                 this.setActivePanel(panel);
45750             }
45751             this.fireEvent("paneladded", this, panel);
45752             return panel;
45753         }
45754         if(!this.tabs){
45755             this.initTabs();
45756         }else{
45757             this.initPanelAsTab(panel);
45758         }
45759         if(panel.background !== true){
45760             this.tabs.activate(panel.getEl().id);
45761         }
45762         this.fireEvent("paneladded", this, panel);
45763         return panel;
45764     },
45765
45766     /**
45767      * Hides the tab for the specified panel.
45768      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45769      */
45770     hidePanel : function(panel){
45771         if(this.tabs && (panel = this.getPanel(panel))){
45772             this.tabs.hideTab(panel.getEl().id);
45773         }
45774     },
45775
45776     /**
45777      * Unhides the tab for a previously hidden panel.
45778      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45779      */
45780     unhidePanel : function(panel){
45781         if(this.tabs && (panel = this.getPanel(panel))){
45782             this.tabs.unhideTab(panel.getEl().id);
45783         }
45784     },
45785
45786     clearPanels : function(){
45787         while(this.panels.getCount() > 0){
45788              this.remove(this.panels.first());
45789         }
45790     },
45791
45792     /**
45793      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45794      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45795      * @param {Boolean} preservePanel Overrides the config preservePanel option
45796      * @return {Roo.ContentPanel} The panel that was removed
45797      */
45798     remove : function(panel, preservePanel){
45799         panel = this.getPanel(panel);
45800         if(!panel){
45801             return null;
45802         }
45803         var e = {};
45804         this.fireEvent("beforeremove", this, panel, e);
45805         if(e.cancel === true){
45806             return null;
45807         }
45808         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45809         var panelId = panel.getId();
45810         this.panels.removeKey(panelId);
45811         if(preservePanel){
45812             document.body.appendChild(panel.getEl().dom);
45813         }
45814         if(this.tabs){
45815             this.tabs.removeTab(panel.getEl().id);
45816         }else if (!preservePanel){
45817             this.bodyEl.dom.removeChild(panel.getEl().dom);
45818         }
45819         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45820             var p = this.panels.first();
45821             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45822             tempEl.appendChild(p.getEl().dom);
45823             this.bodyEl.update("");
45824             this.bodyEl.dom.appendChild(p.getEl().dom);
45825             tempEl = null;
45826             this.updateTitle(p.getTitle());
45827             this.tabs = null;
45828             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45829             this.setActivePanel(p);
45830         }
45831         panel.setRegion(null);
45832         if(this.activePanel == panel){
45833             this.activePanel = null;
45834         }
45835         if(this.config.autoDestroy !== false && preservePanel !== true){
45836             try{panel.destroy();}catch(e){}
45837         }
45838         this.fireEvent("panelremoved", this, panel);
45839         return panel;
45840     },
45841
45842     /**
45843      * Returns the TabPanel component used by this region
45844      * @return {Roo.TabPanel}
45845      */
45846     getTabs : function(){
45847         return this.tabs;
45848     },
45849
45850     createTool : function(parentEl, className){
45851         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45852             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45853         btn.addClassOnOver("x-layout-tools-button-over");
45854         return btn;
45855     }
45856 });/*
45857  * Based on:
45858  * Ext JS Library 1.1.1
45859  * Copyright(c) 2006-2007, Ext JS, LLC.
45860  *
45861  * Originally Released Under LGPL - original licence link has changed is not relivant.
45862  *
45863  * Fork - LGPL
45864  * <script type="text/javascript">
45865  */
45866  
45867
45868
45869 /**
45870  * @class Roo.SplitLayoutRegion
45871  * @extends Roo.LayoutRegion
45872  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45873  */
45874 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45875     this.cursor = cursor;
45876     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45877 };
45878
45879 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45880     splitTip : "Drag to resize.",
45881     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45882     useSplitTips : false,
45883
45884     applyConfig : function(config){
45885         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45886         if(config.split){
45887             if(!this.split){
45888                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45889                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45890                 /** The SplitBar for this region 
45891                 * @type Roo.SplitBar */
45892                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45893                 this.split.on("moved", this.onSplitMove, this);
45894                 this.split.useShim = config.useShim === true;
45895                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45896                 if(this.useSplitTips){
45897                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45898                 }
45899                 if(config.collapsible){
45900                     this.split.el.on("dblclick", this.collapse,  this);
45901                 }
45902             }
45903             if(typeof config.minSize != "undefined"){
45904                 this.split.minSize = config.minSize;
45905             }
45906             if(typeof config.maxSize != "undefined"){
45907                 this.split.maxSize = config.maxSize;
45908             }
45909             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45910                 this.hideSplitter();
45911             }
45912         }
45913     },
45914
45915     getHMaxSize : function(){
45916          var cmax = this.config.maxSize || 10000;
45917          var center = this.mgr.getRegion("center");
45918          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45919     },
45920
45921     getVMaxSize : function(){
45922          var cmax = this.config.maxSize || 10000;
45923          var center = this.mgr.getRegion("center");
45924          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45925     },
45926
45927     onSplitMove : function(split, newSize){
45928         this.fireEvent("resized", this, newSize);
45929     },
45930     
45931     /** 
45932      * Returns the {@link Roo.SplitBar} for this region.
45933      * @return {Roo.SplitBar}
45934      */
45935     getSplitBar : function(){
45936         return this.split;
45937     },
45938     
45939     hide : function(){
45940         this.hideSplitter();
45941         Roo.SplitLayoutRegion.superclass.hide.call(this);
45942     },
45943
45944     hideSplitter : function(){
45945         if(this.split){
45946             this.split.el.setLocation(-2000,-2000);
45947             this.split.el.hide();
45948         }
45949     },
45950
45951     show : function(){
45952         if(this.split){
45953             this.split.el.show();
45954         }
45955         Roo.SplitLayoutRegion.superclass.show.call(this);
45956     },
45957     
45958     beforeSlide: function(){
45959         if(Roo.isGecko){// firefox overflow auto bug workaround
45960             this.bodyEl.clip();
45961             if(this.tabs) this.tabs.bodyEl.clip();
45962             if(this.activePanel){
45963                 this.activePanel.getEl().clip();
45964                 
45965                 if(this.activePanel.beforeSlide){
45966                     this.activePanel.beforeSlide();
45967                 }
45968             }
45969         }
45970     },
45971     
45972     afterSlide : function(){
45973         if(Roo.isGecko){// firefox overflow auto bug workaround
45974             this.bodyEl.unclip();
45975             if(this.tabs) this.tabs.bodyEl.unclip();
45976             if(this.activePanel){
45977                 this.activePanel.getEl().unclip();
45978                 if(this.activePanel.afterSlide){
45979                     this.activePanel.afterSlide();
45980                 }
45981             }
45982         }
45983     },
45984
45985     initAutoHide : function(){
45986         if(this.autoHide !== false){
45987             if(!this.autoHideHd){
45988                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45989                 this.autoHideHd = {
45990                     "mouseout": function(e){
45991                         if(!e.within(this.el, true)){
45992                             st.delay(500);
45993                         }
45994                     },
45995                     "mouseover" : function(e){
45996                         st.cancel();
45997                     },
45998                     scope : this
45999                 };
46000             }
46001             this.el.on(this.autoHideHd);
46002         }
46003     },
46004
46005     clearAutoHide : function(){
46006         if(this.autoHide !== false){
46007             this.el.un("mouseout", this.autoHideHd.mouseout);
46008             this.el.un("mouseover", this.autoHideHd.mouseover);
46009         }
46010     },
46011
46012     clearMonitor : function(){
46013         Roo.get(document).un("click", this.slideInIf, this);
46014     },
46015
46016     // these names are backwards but not changed for compat
46017     slideOut : function(){
46018         if(this.isSlid || this.el.hasActiveFx()){
46019             return;
46020         }
46021         this.isSlid = true;
46022         if(this.collapseBtn){
46023             this.collapseBtn.hide();
46024         }
46025         this.closeBtnState = this.closeBtn.getStyle('display');
46026         this.closeBtn.hide();
46027         if(this.stickBtn){
46028             this.stickBtn.show();
46029         }
46030         this.el.show();
46031         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46032         this.beforeSlide();
46033         this.el.setStyle("z-index", 10001);
46034         this.el.slideIn(this.getSlideAnchor(), {
46035             callback: function(){
46036                 this.afterSlide();
46037                 this.initAutoHide();
46038                 Roo.get(document).on("click", this.slideInIf, this);
46039                 this.fireEvent("slideshow", this);
46040             },
46041             scope: this,
46042             block: true
46043         });
46044     },
46045
46046     afterSlideIn : function(){
46047         this.clearAutoHide();
46048         this.isSlid = false;
46049         this.clearMonitor();
46050         this.el.setStyle("z-index", "");
46051         if(this.collapseBtn){
46052             this.collapseBtn.show();
46053         }
46054         this.closeBtn.setStyle('display', this.closeBtnState);
46055         if(this.stickBtn){
46056             this.stickBtn.hide();
46057         }
46058         this.fireEvent("slidehide", this);
46059     },
46060
46061     slideIn : function(cb){
46062         if(!this.isSlid || this.el.hasActiveFx()){
46063             Roo.callback(cb);
46064             return;
46065         }
46066         this.isSlid = false;
46067         this.beforeSlide();
46068         this.el.slideOut(this.getSlideAnchor(), {
46069             callback: function(){
46070                 this.el.setLeftTop(-10000, -10000);
46071                 this.afterSlide();
46072                 this.afterSlideIn();
46073                 Roo.callback(cb);
46074             },
46075             scope: this,
46076             block: true
46077         });
46078     },
46079     
46080     slideInIf : function(e){
46081         if(!e.within(this.el)){
46082             this.slideIn();
46083         }
46084     },
46085
46086     animateCollapse : function(){
46087         this.beforeSlide();
46088         this.el.setStyle("z-index", 20000);
46089         var anchor = this.getSlideAnchor();
46090         this.el.slideOut(anchor, {
46091             callback : function(){
46092                 this.el.setStyle("z-index", "");
46093                 this.collapsedEl.slideIn(anchor, {duration:.3});
46094                 this.afterSlide();
46095                 this.el.setLocation(-10000,-10000);
46096                 this.el.hide();
46097                 this.fireEvent("collapsed", this);
46098             },
46099             scope: this,
46100             block: true
46101         });
46102     },
46103
46104     animateExpand : function(){
46105         this.beforeSlide();
46106         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46107         this.el.setStyle("z-index", 20000);
46108         this.collapsedEl.hide({
46109             duration:.1
46110         });
46111         this.el.slideIn(this.getSlideAnchor(), {
46112             callback : function(){
46113                 this.el.setStyle("z-index", "");
46114                 this.afterSlide();
46115                 if(this.split){
46116                     this.split.el.show();
46117                 }
46118                 this.fireEvent("invalidated", this);
46119                 this.fireEvent("expanded", this);
46120             },
46121             scope: this,
46122             block: true
46123         });
46124     },
46125
46126     anchors : {
46127         "west" : "left",
46128         "east" : "right",
46129         "north" : "top",
46130         "south" : "bottom"
46131     },
46132
46133     sanchors : {
46134         "west" : "l",
46135         "east" : "r",
46136         "north" : "t",
46137         "south" : "b"
46138     },
46139
46140     canchors : {
46141         "west" : "tl-tr",
46142         "east" : "tr-tl",
46143         "north" : "tl-bl",
46144         "south" : "bl-tl"
46145     },
46146
46147     getAnchor : function(){
46148         return this.anchors[this.position];
46149     },
46150
46151     getCollapseAnchor : function(){
46152         return this.canchors[this.position];
46153     },
46154
46155     getSlideAnchor : function(){
46156         return this.sanchors[this.position];
46157     },
46158
46159     getAlignAdj : function(){
46160         var cm = this.cmargins;
46161         switch(this.position){
46162             case "west":
46163                 return [0, 0];
46164             break;
46165             case "east":
46166                 return [0, 0];
46167             break;
46168             case "north":
46169                 return [0, 0];
46170             break;
46171             case "south":
46172                 return [0, 0];
46173             break;
46174         }
46175     },
46176
46177     getExpandAdj : function(){
46178         var c = this.collapsedEl, cm = this.cmargins;
46179         switch(this.position){
46180             case "west":
46181                 return [-(cm.right+c.getWidth()+cm.left), 0];
46182             break;
46183             case "east":
46184                 return [cm.right+c.getWidth()+cm.left, 0];
46185             break;
46186             case "north":
46187                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46188             break;
46189             case "south":
46190                 return [0, cm.top+cm.bottom+c.getHeight()];
46191             break;
46192         }
46193     }
46194 });/*
46195  * Based on:
46196  * Ext JS Library 1.1.1
46197  * Copyright(c) 2006-2007, Ext JS, LLC.
46198  *
46199  * Originally Released Under LGPL - original licence link has changed is not relivant.
46200  *
46201  * Fork - LGPL
46202  * <script type="text/javascript">
46203  */
46204 /*
46205  * These classes are private internal classes
46206  */
46207 Roo.CenterLayoutRegion = function(mgr, config){
46208     Roo.LayoutRegion.call(this, mgr, config, "center");
46209     this.visible = true;
46210     this.minWidth = config.minWidth || 20;
46211     this.minHeight = config.minHeight || 20;
46212 };
46213
46214 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46215     hide : function(){
46216         // center panel can't be hidden
46217     },
46218     
46219     show : function(){
46220         // center panel can't be hidden
46221     },
46222     
46223     getMinWidth: function(){
46224         return this.minWidth;
46225     },
46226     
46227     getMinHeight: function(){
46228         return this.minHeight;
46229     }
46230 });
46231
46232
46233 Roo.NorthLayoutRegion = function(mgr, config){
46234     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46235     if(this.split){
46236         this.split.placement = Roo.SplitBar.TOP;
46237         this.split.orientation = Roo.SplitBar.VERTICAL;
46238         this.split.el.addClass("x-layout-split-v");
46239     }
46240     var size = config.initialSize || config.height;
46241     if(typeof size != "undefined"){
46242         this.el.setHeight(size);
46243     }
46244 };
46245 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46246     orientation: Roo.SplitBar.VERTICAL,
46247     getBox : function(){
46248         if(this.collapsed){
46249             return this.collapsedEl.getBox();
46250         }
46251         var box = this.el.getBox();
46252         if(this.split){
46253             box.height += this.split.el.getHeight();
46254         }
46255         return box;
46256     },
46257     
46258     updateBox : function(box){
46259         if(this.split && !this.collapsed){
46260             box.height -= this.split.el.getHeight();
46261             this.split.el.setLeft(box.x);
46262             this.split.el.setTop(box.y+box.height);
46263             this.split.el.setWidth(box.width);
46264         }
46265         if(this.collapsed){
46266             this.updateBody(box.width, null);
46267         }
46268         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46269     }
46270 });
46271
46272 Roo.SouthLayoutRegion = function(mgr, config){
46273     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46274     if(this.split){
46275         this.split.placement = Roo.SplitBar.BOTTOM;
46276         this.split.orientation = Roo.SplitBar.VERTICAL;
46277         this.split.el.addClass("x-layout-split-v");
46278     }
46279     var size = config.initialSize || config.height;
46280     if(typeof size != "undefined"){
46281         this.el.setHeight(size);
46282     }
46283 };
46284 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46285     orientation: Roo.SplitBar.VERTICAL,
46286     getBox : function(){
46287         if(this.collapsed){
46288             return this.collapsedEl.getBox();
46289         }
46290         var box = this.el.getBox();
46291         if(this.split){
46292             var sh = this.split.el.getHeight();
46293             box.height += sh;
46294             box.y -= sh;
46295         }
46296         return box;
46297     },
46298     
46299     updateBox : function(box){
46300         if(this.split && !this.collapsed){
46301             var sh = this.split.el.getHeight();
46302             box.height -= sh;
46303             box.y += sh;
46304             this.split.el.setLeft(box.x);
46305             this.split.el.setTop(box.y-sh);
46306             this.split.el.setWidth(box.width);
46307         }
46308         if(this.collapsed){
46309             this.updateBody(box.width, null);
46310         }
46311         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46312     }
46313 });
46314
46315 Roo.EastLayoutRegion = function(mgr, config){
46316     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46317     if(this.split){
46318         this.split.placement = Roo.SplitBar.RIGHT;
46319         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46320         this.split.el.addClass("x-layout-split-h");
46321     }
46322     var size = config.initialSize || config.width;
46323     if(typeof size != "undefined"){
46324         this.el.setWidth(size);
46325     }
46326 };
46327 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46328     orientation: Roo.SplitBar.HORIZONTAL,
46329     getBox : function(){
46330         if(this.collapsed){
46331             return this.collapsedEl.getBox();
46332         }
46333         var box = this.el.getBox();
46334         if(this.split){
46335             var sw = this.split.el.getWidth();
46336             box.width += sw;
46337             box.x -= sw;
46338         }
46339         return box;
46340     },
46341
46342     updateBox : function(box){
46343         if(this.split && !this.collapsed){
46344             var sw = this.split.el.getWidth();
46345             box.width -= sw;
46346             this.split.el.setLeft(box.x);
46347             this.split.el.setTop(box.y);
46348             this.split.el.setHeight(box.height);
46349             box.x += sw;
46350         }
46351         if(this.collapsed){
46352             this.updateBody(null, box.height);
46353         }
46354         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46355     }
46356 });
46357
46358 Roo.WestLayoutRegion = function(mgr, config){
46359     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46360     if(this.split){
46361         this.split.placement = Roo.SplitBar.LEFT;
46362         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46363         this.split.el.addClass("x-layout-split-h");
46364     }
46365     var size = config.initialSize || config.width;
46366     if(typeof size != "undefined"){
46367         this.el.setWidth(size);
46368     }
46369 };
46370 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46371     orientation: Roo.SplitBar.HORIZONTAL,
46372     getBox : function(){
46373         if(this.collapsed){
46374             return this.collapsedEl.getBox();
46375         }
46376         var box = this.el.getBox();
46377         if(this.split){
46378             box.width += this.split.el.getWidth();
46379         }
46380         return box;
46381     },
46382     
46383     updateBox : function(box){
46384         if(this.split && !this.collapsed){
46385             var sw = this.split.el.getWidth();
46386             box.width -= sw;
46387             this.split.el.setLeft(box.x+box.width);
46388             this.split.el.setTop(box.y);
46389             this.split.el.setHeight(box.height);
46390         }
46391         if(this.collapsed){
46392             this.updateBody(null, box.height);
46393         }
46394         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46395     }
46396 });
46397 /*
46398  * Based on:
46399  * Ext JS Library 1.1.1
46400  * Copyright(c) 2006-2007, Ext JS, LLC.
46401  *
46402  * Originally Released Under LGPL - original licence link has changed is not relivant.
46403  *
46404  * Fork - LGPL
46405  * <script type="text/javascript">
46406  */
46407  
46408  
46409 /*
46410  * Private internal class for reading and applying state
46411  */
46412 Roo.LayoutStateManager = function(layout){
46413      // default empty state
46414      this.state = {
46415         north: {},
46416         south: {},
46417         east: {},
46418         west: {}       
46419     };
46420 };
46421
46422 Roo.LayoutStateManager.prototype = {
46423     init : function(layout, provider){
46424         this.provider = provider;
46425         var state = provider.get(layout.id+"-layout-state");
46426         if(state){
46427             var wasUpdating = layout.isUpdating();
46428             if(!wasUpdating){
46429                 layout.beginUpdate();
46430             }
46431             for(var key in state){
46432                 if(typeof state[key] != "function"){
46433                     var rstate = state[key];
46434                     var r = layout.getRegion(key);
46435                     if(r && rstate){
46436                         if(rstate.size){
46437                             r.resizeTo(rstate.size);
46438                         }
46439                         if(rstate.collapsed == true){
46440                             r.collapse(true);
46441                         }else{
46442                             r.expand(null, true);
46443                         }
46444                     }
46445                 }
46446             }
46447             if(!wasUpdating){
46448                 layout.endUpdate();
46449             }
46450             this.state = state; 
46451         }
46452         this.layout = layout;
46453         layout.on("regionresized", this.onRegionResized, this);
46454         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46455         layout.on("regionexpanded", this.onRegionExpanded, this);
46456     },
46457     
46458     storeState : function(){
46459         this.provider.set(this.layout.id+"-layout-state", this.state);
46460     },
46461     
46462     onRegionResized : function(region, newSize){
46463         this.state[region.getPosition()].size = newSize;
46464         this.storeState();
46465     },
46466     
46467     onRegionCollapsed : function(region){
46468         this.state[region.getPosition()].collapsed = true;
46469         this.storeState();
46470     },
46471     
46472     onRegionExpanded : function(region){
46473         this.state[region.getPosition()].collapsed = false;
46474         this.storeState();
46475     }
46476 };/*
46477  * Based on:
46478  * Ext JS Library 1.1.1
46479  * Copyright(c) 2006-2007, Ext JS, LLC.
46480  *
46481  * Originally Released Under LGPL - original licence link has changed is not relivant.
46482  *
46483  * Fork - LGPL
46484  * <script type="text/javascript">
46485  */
46486 /**
46487  * @class Roo.ContentPanel
46488  * @extends Roo.util.Observable
46489  * A basic ContentPanel element.
46490  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46491  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46492  * @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
46493  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46494  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46495  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46496  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46497  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46498  * @cfg {String} title          The title for this panel
46499  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46500  * @cfg {String} url            Calls {@link #setUrl} with this value
46501  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46502  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46503  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46504  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46505
46506  * @constructor
46507  * Create a new ContentPanel.
46508  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46509  * @param {String/Object} config A string to set only the title or a config object
46510  * @param {String} content (optional) Set the HTML content for this panel
46511  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46512  */
46513 Roo.ContentPanel = function(el, config, content){
46514     
46515      
46516     /*
46517     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46518         config = el;
46519         el = Roo.id();
46520     }
46521     if (config && config.parentLayout) { 
46522         el = config.parentLayout.el.createChild(); 
46523     }
46524     */
46525     if(el.autoCreate){ // xtype is available if this is called from factory
46526         config = el;
46527         el = Roo.id();
46528     }
46529     this.el = Roo.get(el);
46530     if(!this.el && config && config.autoCreate){
46531         if(typeof config.autoCreate == "object"){
46532             if(!config.autoCreate.id){
46533                 config.autoCreate.id = config.id||el;
46534             }
46535             this.el = Roo.DomHelper.append(document.body,
46536                         config.autoCreate, true);
46537         }else{
46538             this.el = Roo.DomHelper.append(document.body,
46539                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46540         }
46541     }
46542     this.closable = false;
46543     this.loaded = false;
46544     this.active = false;
46545     if(typeof config == "string"){
46546         this.title = config;
46547     }else{
46548         Roo.apply(this, config);
46549     }
46550     
46551     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46552         this.wrapEl = this.el.wrap();
46553         this.toolbar.container = this.el.insertSibling(false, 'before');
46554         this.toolbar = new Roo.Toolbar(this.toolbar);
46555     }
46556     
46557     
46558     
46559     if(this.resizeEl){
46560         this.resizeEl = Roo.get(this.resizeEl, true);
46561     }else{
46562         this.resizeEl = this.el;
46563     }
46564     this.addEvents({
46565         /**
46566          * @event activate
46567          * Fires when this panel is activated. 
46568          * @param {Roo.ContentPanel} this
46569          */
46570         "activate" : true,
46571         /**
46572          * @event deactivate
46573          * Fires when this panel is activated. 
46574          * @param {Roo.ContentPanel} this
46575          */
46576         "deactivate" : true,
46577
46578         /**
46579          * @event resize
46580          * Fires when this panel is resized if fitToFrame is true.
46581          * @param {Roo.ContentPanel} this
46582          * @param {Number} width The width after any component adjustments
46583          * @param {Number} height The height after any component adjustments
46584          */
46585         "resize" : true,
46586         
46587          /**
46588          * @event render
46589          * Fires when this tab is created
46590          * @param {Roo.ContentPanel} this
46591          */
46592         "render" : true
46593         
46594         
46595         
46596     });
46597     if(this.autoScroll){
46598         this.resizeEl.setStyle("overflow", "auto");
46599     } else {
46600         // fix randome scrolling
46601         this.el.on('scroll', function() {
46602             Roo.log('fix random scolling');
46603             this.scrollTo('top',0); 
46604         });
46605     }
46606     content = content || this.content;
46607     if(content){
46608         this.setContent(content);
46609     }
46610     if(config && config.url){
46611         this.setUrl(this.url, this.params, this.loadOnce);
46612     }
46613     
46614     
46615     
46616     Roo.ContentPanel.superclass.constructor.call(this);
46617     
46618     this.fireEvent('render', this);
46619 };
46620
46621 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46622     tabTip:'',
46623     setRegion : function(region){
46624         this.region = region;
46625         if(region){
46626            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46627         }else{
46628            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46629         } 
46630     },
46631     
46632     /**
46633      * Returns the toolbar for this Panel if one was configured. 
46634      * @return {Roo.Toolbar} 
46635      */
46636     getToolbar : function(){
46637         return this.toolbar;
46638     },
46639     
46640     setActiveState : function(active){
46641         this.active = active;
46642         if(!active){
46643             this.fireEvent("deactivate", this);
46644         }else{
46645             this.fireEvent("activate", this);
46646         }
46647     },
46648     /**
46649      * Updates this panel's element
46650      * @param {String} content The new content
46651      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46652     */
46653     setContent : function(content, loadScripts){
46654         this.el.update(content, loadScripts);
46655     },
46656
46657     ignoreResize : function(w, h){
46658         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46659             return true;
46660         }else{
46661             this.lastSize = {width: w, height: h};
46662             return false;
46663         }
46664     },
46665     /**
46666      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46667      * @return {Roo.UpdateManager} The UpdateManager
46668      */
46669     getUpdateManager : function(){
46670         return this.el.getUpdateManager();
46671     },
46672      /**
46673      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46674      * @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:
46675 <pre><code>
46676 panel.load({
46677     url: "your-url.php",
46678     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46679     callback: yourFunction,
46680     scope: yourObject, //(optional scope)
46681     discardUrl: false,
46682     nocache: false,
46683     text: "Loading...",
46684     timeout: 30,
46685     scripts: false
46686 });
46687 </code></pre>
46688      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46689      * 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.
46690      * @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}
46691      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46692      * @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.
46693      * @return {Roo.ContentPanel} this
46694      */
46695     load : function(){
46696         var um = this.el.getUpdateManager();
46697         um.update.apply(um, arguments);
46698         return this;
46699     },
46700
46701
46702     /**
46703      * 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.
46704      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46705      * @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)
46706      * @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)
46707      * @return {Roo.UpdateManager} The UpdateManager
46708      */
46709     setUrl : function(url, params, loadOnce){
46710         if(this.refreshDelegate){
46711             this.removeListener("activate", this.refreshDelegate);
46712         }
46713         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46714         this.on("activate", this.refreshDelegate);
46715         return this.el.getUpdateManager();
46716     },
46717     
46718     _handleRefresh : function(url, params, loadOnce){
46719         if(!loadOnce || !this.loaded){
46720             var updater = this.el.getUpdateManager();
46721             updater.update(url, params, this._setLoaded.createDelegate(this));
46722         }
46723     },
46724     
46725     _setLoaded : function(){
46726         this.loaded = true;
46727     }, 
46728     
46729     /**
46730      * Returns this panel's id
46731      * @return {String} 
46732      */
46733     getId : function(){
46734         return this.el.id;
46735     },
46736     
46737     /** 
46738      * Returns this panel's element - used by regiosn to add.
46739      * @return {Roo.Element} 
46740      */
46741     getEl : function(){
46742         return this.wrapEl || this.el;
46743     },
46744     
46745     adjustForComponents : function(width, height){
46746         if(this.resizeEl != this.el){
46747             width -= this.el.getFrameWidth('lr');
46748             height -= this.el.getFrameWidth('tb');
46749         }
46750         if(this.toolbar){
46751             var te = this.toolbar.getEl();
46752             height -= te.getHeight();
46753             te.setWidth(width);
46754         }
46755         if(this.adjustments){
46756             width += this.adjustments[0];
46757             height += this.adjustments[1];
46758         }
46759         return {"width": width, "height": height};
46760     },
46761     
46762     setSize : function(width, height){
46763         if(this.fitToFrame && !this.ignoreResize(width, height)){
46764             if(this.fitContainer && this.resizeEl != this.el){
46765                 this.el.setSize(width, height);
46766             }
46767             var size = this.adjustForComponents(width, height);
46768             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46769             this.fireEvent('resize', this, size.width, size.height);
46770         }
46771     },
46772     
46773     /**
46774      * Returns this panel's title
46775      * @return {String} 
46776      */
46777     getTitle : function(){
46778         return this.title;
46779     },
46780     
46781     /**
46782      * Set this panel's title
46783      * @param {String} title
46784      */
46785     setTitle : function(title){
46786         this.title = title;
46787         if(this.region){
46788             this.region.updatePanelTitle(this, title);
46789         }
46790     },
46791     
46792     /**
46793      * Returns true is this panel was configured to be closable
46794      * @return {Boolean} 
46795      */
46796     isClosable : function(){
46797         return this.closable;
46798     },
46799     
46800     beforeSlide : function(){
46801         this.el.clip();
46802         this.resizeEl.clip();
46803     },
46804     
46805     afterSlide : function(){
46806         this.el.unclip();
46807         this.resizeEl.unclip();
46808     },
46809     
46810     /**
46811      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46812      *   Will fail silently if the {@link #setUrl} method has not been called.
46813      *   This does not activate the panel, just updates its content.
46814      */
46815     refresh : function(){
46816         if(this.refreshDelegate){
46817            this.loaded = false;
46818            this.refreshDelegate();
46819         }
46820     },
46821     
46822     /**
46823      * Destroys this panel
46824      */
46825     destroy : function(){
46826         this.el.removeAllListeners();
46827         var tempEl = document.createElement("span");
46828         tempEl.appendChild(this.el.dom);
46829         tempEl.innerHTML = "";
46830         this.el.remove();
46831         this.el = null;
46832     },
46833     
46834     /**
46835      * form - if the content panel contains a form - this is a reference to it.
46836      * @type {Roo.form.Form}
46837      */
46838     form : false,
46839     /**
46840      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46841      *    This contains a reference to it.
46842      * @type {Roo.View}
46843      */
46844     view : false,
46845     
46846       /**
46847      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46848      * <pre><code>
46849
46850 layout.addxtype({
46851        xtype : 'Form',
46852        items: [ .... ]
46853    }
46854 );
46855
46856 </code></pre>
46857      * @param {Object} cfg Xtype definition of item to add.
46858      */
46859     
46860     addxtype : function(cfg) {
46861         // add form..
46862         if (cfg.xtype.match(/^Form$/)) {
46863             var el = this.el.createChild();
46864
46865             this.form = new  Roo.form.Form(cfg);
46866             
46867             
46868             if ( this.form.allItems.length) this.form.render(el.dom);
46869             return this.form;
46870         }
46871         // should only have one of theses..
46872         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46873             // views..
46874             cfg.el = this.el.appendChild(document.createElement("div"));
46875             // factory?
46876             
46877             var ret = new Roo.factory(cfg);
46878             ret.render && ret.render(false, ''); // render blank..
46879             this.view = ret;
46880             return ret;
46881         }
46882         return false;
46883     }
46884 });
46885
46886 /**
46887  * @class Roo.GridPanel
46888  * @extends Roo.ContentPanel
46889  * @constructor
46890  * Create a new GridPanel.
46891  * @param {Roo.grid.Grid} grid The grid for this panel
46892  * @param {String/Object} config A string to set only the panel's title, or a config object
46893  */
46894 Roo.GridPanel = function(grid, config){
46895     
46896   
46897     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46898         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46899         
46900     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46901     
46902     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46903     
46904     if(this.toolbar){
46905         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46906     }
46907     // xtype created footer. - not sure if will work as we normally have to render first..
46908     if (this.footer && !this.footer.el && this.footer.xtype) {
46909         
46910         this.footer.container = this.grid.getView().getFooterPanel(true);
46911         this.footer.dataSource = this.grid.dataSource;
46912         this.footer = Roo.factory(this.footer, Roo);
46913         
46914     }
46915     
46916     grid.monitorWindowResize = false; // turn off autosizing
46917     grid.autoHeight = false;
46918     grid.autoWidth = false;
46919     this.grid = grid;
46920     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46921 };
46922
46923 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46924     getId : function(){
46925         return this.grid.id;
46926     },
46927     
46928     /**
46929      * Returns the grid for this panel
46930      * @return {Roo.grid.Grid} 
46931      */
46932     getGrid : function(){
46933         return this.grid;    
46934     },
46935     
46936     setSize : function(width, height){
46937         if(!this.ignoreResize(width, height)){
46938             var grid = this.grid;
46939             var size = this.adjustForComponents(width, height);
46940             grid.getGridEl().setSize(size.width, size.height);
46941             grid.autoSize();
46942         }
46943     },
46944     
46945     beforeSlide : function(){
46946         this.grid.getView().scroller.clip();
46947     },
46948     
46949     afterSlide : function(){
46950         this.grid.getView().scroller.unclip();
46951     },
46952     
46953     destroy : function(){
46954         this.grid.destroy();
46955         delete this.grid;
46956         Roo.GridPanel.superclass.destroy.call(this); 
46957     }
46958 });
46959
46960
46961 /**
46962  * @class Roo.NestedLayoutPanel
46963  * @extends Roo.ContentPanel
46964  * @constructor
46965  * Create a new NestedLayoutPanel.
46966  * 
46967  * 
46968  * @param {Roo.BorderLayout} layout The layout for this panel
46969  * @param {String/Object} config A string to set only the title or a config object
46970  */
46971 Roo.NestedLayoutPanel = function(layout, config)
46972 {
46973     // construct with only one argument..
46974     /* FIXME - implement nicer consturctors
46975     if (layout.layout) {
46976         config = layout;
46977         layout = config.layout;
46978         delete config.layout;
46979     }
46980     if (layout.xtype && !layout.getEl) {
46981         // then layout needs constructing..
46982         layout = Roo.factory(layout, Roo);
46983     }
46984     */
46985     
46986     
46987     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46988     
46989     layout.monitorWindowResize = false; // turn off autosizing
46990     this.layout = layout;
46991     this.layout.getEl().addClass("x-layout-nested-layout");
46992     
46993     
46994     
46995     
46996 };
46997
46998 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46999
47000     setSize : function(width, height){
47001         if(!this.ignoreResize(width, height)){
47002             var size = this.adjustForComponents(width, height);
47003             var el = this.layout.getEl();
47004             el.setSize(size.width, size.height);
47005             var touch = el.dom.offsetWidth;
47006             this.layout.layout();
47007             // ie requires a double layout on the first pass
47008             if(Roo.isIE && !this.initialized){
47009                 this.initialized = true;
47010                 this.layout.layout();
47011             }
47012         }
47013     },
47014     
47015     // activate all subpanels if not currently active..
47016     
47017     setActiveState : function(active){
47018         this.active = active;
47019         if(!active){
47020             this.fireEvent("deactivate", this);
47021             return;
47022         }
47023         
47024         this.fireEvent("activate", this);
47025         // not sure if this should happen before or after..
47026         if (!this.layout) {
47027             return; // should not happen..
47028         }
47029         var reg = false;
47030         for (var r in this.layout.regions) {
47031             reg = this.layout.getRegion(r);
47032             if (reg.getActivePanel()) {
47033                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47034                 reg.setActivePanel(reg.getActivePanel());
47035                 continue;
47036             }
47037             if (!reg.panels.length) {
47038                 continue;
47039             }
47040             reg.showPanel(reg.getPanel(0));
47041         }
47042         
47043         
47044         
47045         
47046     },
47047     
47048     /**
47049      * Returns the nested BorderLayout for this panel
47050      * @return {Roo.BorderLayout} 
47051      */
47052     getLayout : function(){
47053         return this.layout;
47054     },
47055     
47056      /**
47057      * Adds a xtype elements to the layout of the nested panel
47058      * <pre><code>
47059
47060 panel.addxtype({
47061        xtype : 'ContentPanel',
47062        region: 'west',
47063        items: [ .... ]
47064    }
47065 );
47066
47067 panel.addxtype({
47068         xtype : 'NestedLayoutPanel',
47069         region: 'west',
47070         layout: {
47071            center: { },
47072            west: { }   
47073         },
47074         items : [ ... list of content panels or nested layout panels.. ]
47075    }
47076 );
47077 </code></pre>
47078      * @param {Object} cfg Xtype definition of item to add.
47079      */
47080     addxtype : function(cfg) {
47081         return this.layout.addxtype(cfg);
47082     
47083     }
47084 });
47085
47086 Roo.ScrollPanel = function(el, config, content){
47087     config = config || {};
47088     config.fitToFrame = true;
47089     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47090     
47091     this.el.dom.style.overflow = "hidden";
47092     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47093     this.el.removeClass("x-layout-inactive-content");
47094     this.el.on("mousewheel", this.onWheel, this);
47095
47096     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47097     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47098     up.unselectable(); down.unselectable();
47099     up.on("click", this.scrollUp, this);
47100     down.on("click", this.scrollDown, this);
47101     up.addClassOnOver("x-scroller-btn-over");
47102     down.addClassOnOver("x-scroller-btn-over");
47103     up.addClassOnClick("x-scroller-btn-click");
47104     down.addClassOnClick("x-scroller-btn-click");
47105     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47106
47107     this.resizeEl = this.el;
47108     this.el = wrap; this.up = up; this.down = down;
47109 };
47110
47111 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47112     increment : 100,
47113     wheelIncrement : 5,
47114     scrollUp : function(){
47115         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47116     },
47117
47118     scrollDown : function(){
47119         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47120     },
47121
47122     afterScroll : function(){
47123         var el = this.resizeEl;
47124         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47125         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47126         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47127     },
47128
47129     setSize : function(){
47130         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47131         this.afterScroll();
47132     },
47133
47134     onWheel : function(e){
47135         var d = e.getWheelDelta();
47136         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47137         this.afterScroll();
47138         e.stopEvent();
47139     },
47140
47141     setContent : function(content, loadScripts){
47142         this.resizeEl.update(content, loadScripts);
47143     }
47144
47145 });
47146
47147
47148
47149
47150
47151
47152
47153
47154
47155 /**
47156  * @class Roo.TreePanel
47157  * @extends Roo.ContentPanel
47158  * @constructor
47159  * Create a new TreePanel. - defaults to fit/scoll contents.
47160  * @param {String/Object} config A string to set only the panel's title, or a config object
47161  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47162  */
47163 Roo.TreePanel = function(config){
47164     var el = config.el;
47165     var tree = config.tree;
47166     delete config.tree; 
47167     delete config.el; // hopefull!
47168     
47169     // wrapper for IE7 strict & safari scroll issue
47170     
47171     var treeEl = el.createChild();
47172     config.resizeEl = treeEl;
47173     
47174     
47175     
47176     Roo.TreePanel.superclass.constructor.call(this, el, config);
47177  
47178  
47179     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47180     //console.log(tree);
47181     this.on('activate', function()
47182     {
47183         if (this.tree.rendered) {
47184             return;
47185         }
47186         //console.log('render tree');
47187         this.tree.render();
47188     });
47189     
47190     this.on('resize',  function (cp, w, h) {
47191             this.tree.innerCt.setWidth(w);
47192             this.tree.innerCt.setHeight(h);
47193             this.tree.innerCt.setStyle('overflow-y', 'auto');
47194     });
47195
47196         
47197     
47198 };
47199
47200 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47201     fitToFrame : true,
47202     autoScroll : true
47203 });
47204
47205
47206
47207
47208
47209
47210
47211
47212
47213
47214
47215 /*
47216  * Based on:
47217  * Ext JS Library 1.1.1
47218  * Copyright(c) 2006-2007, Ext JS, LLC.
47219  *
47220  * Originally Released Under LGPL - original licence link has changed is not relivant.
47221  *
47222  * Fork - LGPL
47223  * <script type="text/javascript">
47224  */
47225  
47226
47227 /**
47228  * @class Roo.ReaderLayout
47229  * @extends Roo.BorderLayout
47230  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47231  * center region containing two nested regions (a top one for a list view and one for item preview below),
47232  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47233  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47234  * expedites the setup of the overall layout and regions for this common application style.
47235  * Example:
47236  <pre><code>
47237 var reader = new Roo.ReaderLayout();
47238 var CP = Roo.ContentPanel;  // shortcut for adding
47239
47240 reader.beginUpdate();
47241 reader.add("north", new CP("north", "North"));
47242 reader.add("west", new CP("west", {title: "West"}));
47243 reader.add("east", new CP("east", {title: "East"}));
47244
47245 reader.regions.listView.add(new CP("listView", "List"));
47246 reader.regions.preview.add(new CP("preview", "Preview"));
47247 reader.endUpdate();
47248 </code></pre>
47249 * @constructor
47250 * Create a new ReaderLayout
47251 * @param {Object} config Configuration options
47252 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47253 * document.body if omitted)
47254 */
47255 Roo.ReaderLayout = function(config, renderTo){
47256     var c = config || {size:{}};
47257     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47258         north: c.north !== false ? Roo.apply({
47259             split:false,
47260             initialSize: 32,
47261             titlebar: false
47262         }, c.north) : false,
47263         west: c.west !== false ? Roo.apply({
47264             split:true,
47265             initialSize: 200,
47266             minSize: 175,
47267             maxSize: 400,
47268             titlebar: true,
47269             collapsible: true,
47270             animate: true,
47271             margins:{left:5,right:0,bottom:5,top:5},
47272             cmargins:{left:5,right:5,bottom:5,top:5}
47273         }, c.west) : false,
47274         east: c.east !== false ? Roo.apply({
47275             split:true,
47276             initialSize: 200,
47277             minSize: 175,
47278             maxSize: 400,
47279             titlebar: true,
47280             collapsible: true,
47281             animate: true,
47282             margins:{left:0,right:5,bottom:5,top:5},
47283             cmargins:{left:5,right:5,bottom:5,top:5}
47284         }, c.east) : false,
47285         center: Roo.apply({
47286             tabPosition: 'top',
47287             autoScroll:false,
47288             closeOnTab: true,
47289             titlebar:false,
47290             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47291         }, c.center)
47292     });
47293
47294     this.el.addClass('x-reader');
47295
47296     this.beginUpdate();
47297
47298     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47299         south: c.preview !== false ? Roo.apply({
47300             split:true,
47301             initialSize: 200,
47302             minSize: 100,
47303             autoScroll:true,
47304             collapsible:true,
47305             titlebar: true,
47306             cmargins:{top:5,left:0, right:0, bottom:0}
47307         }, c.preview) : false,
47308         center: Roo.apply({
47309             autoScroll:false,
47310             titlebar:false,
47311             minHeight:200
47312         }, c.listView)
47313     });
47314     this.add('center', new Roo.NestedLayoutPanel(inner,
47315             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47316
47317     this.endUpdate();
47318
47319     this.regions.preview = inner.getRegion('south');
47320     this.regions.listView = inner.getRegion('center');
47321 };
47322
47323 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47324  * Based on:
47325  * Ext JS Library 1.1.1
47326  * Copyright(c) 2006-2007, Ext JS, LLC.
47327  *
47328  * Originally Released Under LGPL - original licence link has changed is not relivant.
47329  *
47330  * Fork - LGPL
47331  * <script type="text/javascript">
47332  */
47333  
47334 /**
47335  * @class Roo.grid.Grid
47336  * @extends Roo.util.Observable
47337  * This class represents the primary interface of a component based grid control.
47338  * <br><br>Usage:<pre><code>
47339  var grid = new Roo.grid.Grid("my-container-id", {
47340      ds: myDataStore,
47341      cm: myColModel,
47342      selModel: mySelectionModel,
47343      autoSizeColumns: true,
47344      monitorWindowResize: false,
47345      trackMouseOver: true
47346  });
47347  // set any options
47348  grid.render();
47349  * </code></pre>
47350  * <b>Common Problems:</b><br/>
47351  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47352  * element will correct this<br/>
47353  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47354  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47355  * are unpredictable.<br/>
47356  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47357  * grid to calculate dimensions/offsets.<br/>
47358   * @constructor
47359  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47360  * The container MUST have some type of size defined for the grid to fill. The container will be
47361  * automatically set to position relative if it isn't already.
47362  * @param {Object} config A config object that sets properties on this grid.
47363  */
47364 Roo.grid.Grid = function(container, config){
47365         // initialize the container
47366         this.container = Roo.get(container);
47367         this.container.update("");
47368         this.container.setStyle("overflow", "hidden");
47369     this.container.addClass('x-grid-container');
47370
47371     this.id = this.container.id;
47372
47373     Roo.apply(this, config);
47374     // check and correct shorthanded configs
47375     if(this.ds){
47376         this.dataSource = this.ds;
47377         delete this.ds;
47378     }
47379     if(this.cm){
47380         this.colModel = this.cm;
47381         delete this.cm;
47382     }
47383     if(this.sm){
47384         this.selModel = this.sm;
47385         delete this.sm;
47386     }
47387
47388     if (this.selModel) {
47389         this.selModel = Roo.factory(this.selModel, Roo.grid);
47390         this.sm = this.selModel;
47391         this.sm.xmodule = this.xmodule || false;
47392     }
47393     if (typeof(this.colModel.config) == 'undefined') {
47394         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47395         this.cm = this.colModel;
47396         this.cm.xmodule = this.xmodule || false;
47397     }
47398     if (this.dataSource) {
47399         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47400         this.ds = this.dataSource;
47401         this.ds.xmodule = this.xmodule || false;
47402          
47403     }
47404     
47405     
47406     
47407     if(this.width){
47408         this.container.setWidth(this.width);
47409     }
47410
47411     if(this.height){
47412         this.container.setHeight(this.height);
47413     }
47414     /** @private */
47415         this.addEvents({
47416         // raw events
47417         /**
47418          * @event click
47419          * The raw click event for the entire grid.
47420          * @param {Roo.EventObject} e
47421          */
47422         "click" : true,
47423         /**
47424          * @event dblclick
47425          * The raw dblclick event for the entire grid.
47426          * @param {Roo.EventObject} e
47427          */
47428         "dblclick" : true,
47429         /**
47430          * @event contextmenu
47431          * The raw contextmenu event for the entire grid.
47432          * @param {Roo.EventObject} e
47433          */
47434         "contextmenu" : true,
47435         /**
47436          * @event mousedown
47437          * The raw mousedown event for the entire grid.
47438          * @param {Roo.EventObject} e
47439          */
47440         "mousedown" : true,
47441         /**
47442          * @event mouseup
47443          * The raw mouseup event for the entire grid.
47444          * @param {Roo.EventObject} e
47445          */
47446         "mouseup" : true,
47447         /**
47448          * @event mouseover
47449          * The raw mouseover event for the entire grid.
47450          * @param {Roo.EventObject} e
47451          */
47452         "mouseover" : true,
47453         /**
47454          * @event mouseout
47455          * The raw mouseout event for the entire grid.
47456          * @param {Roo.EventObject} e
47457          */
47458         "mouseout" : true,
47459         /**
47460          * @event keypress
47461          * The raw keypress event for the entire grid.
47462          * @param {Roo.EventObject} e
47463          */
47464         "keypress" : true,
47465         /**
47466          * @event keydown
47467          * The raw keydown event for the entire grid.
47468          * @param {Roo.EventObject} e
47469          */
47470         "keydown" : true,
47471
47472         // custom events
47473
47474         /**
47475          * @event cellclick
47476          * Fires when a cell is clicked
47477          * @param {Grid} this
47478          * @param {Number} rowIndex
47479          * @param {Number} columnIndex
47480          * @param {Roo.EventObject} e
47481          */
47482         "cellclick" : true,
47483         /**
47484          * @event celldblclick
47485          * Fires when a cell is double clicked
47486          * @param {Grid} this
47487          * @param {Number} rowIndex
47488          * @param {Number} columnIndex
47489          * @param {Roo.EventObject} e
47490          */
47491         "celldblclick" : true,
47492         /**
47493          * @event rowclick
47494          * Fires when a row is clicked
47495          * @param {Grid} this
47496          * @param {Number} rowIndex
47497          * @param {Roo.EventObject} e
47498          */
47499         "rowclick" : true,
47500         /**
47501          * @event rowdblclick
47502          * Fires when a row is double clicked
47503          * @param {Grid} this
47504          * @param {Number} rowIndex
47505          * @param {Roo.EventObject} e
47506          */
47507         "rowdblclick" : true,
47508         /**
47509          * @event headerclick
47510          * Fires when a header is clicked
47511          * @param {Grid} this
47512          * @param {Number} columnIndex
47513          * @param {Roo.EventObject} e
47514          */
47515         "headerclick" : true,
47516         /**
47517          * @event headerdblclick
47518          * Fires when a header cell is double clicked
47519          * @param {Grid} this
47520          * @param {Number} columnIndex
47521          * @param {Roo.EventObject} e
47522          */
47523         "headerdblclick" : true,
47524         /**
47525          * @event rowcontextmenu
47526          * Fires when a row is right clicked
47527          * @param {Grid} this
47528          * @param {Number} rowIndex
47529          * @param {Roo.EventObject} e
47530          */
47531         "rowcontextmenu" : true,
47532         /**
47533          * @event cellcontextmenu
47534          * Fires when a cell is right clicked
47535          * @param {Grid} this
47536          * @param {Number} rowIndex
47537          * @param {Number} cellIndex
47538          * @param {Roo.EventObject} e
47539          */
47540          "cellcontextmenu" : true,
47541         /**
47542          * @event headercontextmenu
47543          * Fires when a header is right clicked
47544          * @param {Grid} this
47545          * @param {Number} columnIndex
47546          * @param {Roo.EventObject} e
47547          */
47548         "headercontextmenu" : true,
47549         /**
47550          * @event bodyscroll
47551          * Fires when the body element is scrolled
47552          * @param {Number} scrollLeft
47553          * @param {Number} scrollTop
47554          */
47555         "bodyscroll" : true,
47556         /**
47557          * @event columnresize
47558          * Fires when the user resizes a column
47559          * @param {Number} columnIndex
47560          * @param {Number} newSize
47561          */
47562         "columnresize" : true,
47563         /**
47564          * @event columnmove
47565          * Fires when the user moves a column
47566          * @param {Number} oldIndex
47567          * @param {Number} newIndex
47568          */
47569         "columnmove" : true,
47570         /**
47571          * @event startdrag
47572          * Fires when row(s) start being dragged
47573          * @param {Grid} this
47574          * @param {Roo.GridDD} dd The drag drop object
47575          * @param {event} e The raw browser event
47576          */
47577         "startdrag" : true,
47578         /**
47579          * @event enddrag
47580          * Fires when a drag operation is complete
47581          * @param {Grid} this
47582          * @param {Roo.GridDD} dd The drag drop object
47583          * @param {event} e The raw browser event
47584          */
47585         "enddrag" : true,
47586         /**
47587          * @event dragdrop
47588          * Fires when dragged row(s) are dropped on a valid DD target
47589          * @param {Grid} this
47590          * @param {Roo.GridDD} dd The drag drop object
47591          * @param {String} targetId The target drag drop object
47592          * @param {event} e The raw browser event
47593          */
47594         "dragdrop" : true,
47595         /**
47596          * @event dragover
47597          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47598          * @param {Grid} this
47599          * @param {Roo.GridDD} dd The drag drop object
47600          * @param {String} targetId The target drag drop object
47601          * @param {event} e The raw browser event
47602          */
47603         "dragover" : true,
47604         /**
47605          * @event dragenter
47606          *  Fires when the dragged row(s) first cross another DD target while being dragged
47607          * @param {Grid} this
47608          * @param {Roo.GridDD} dd The drag drop object
47609          * @param {String} targetId The target drag drop object
47610          * @param {event} e The raw browser event
47611          */
47612         "dragenter" : true,
47613         /**
47614          * @event dragout
47615          * Fires when the dragged row(s) leave another DD target while being dragged
47616          * @param {Grid} this
47617          * @param {Roo.GridDD} dd The drag drop object
47618          * @param {String} targetId The target drag drop object
47619          * @param {event} e The raw browser event
47620          */
47621         "dragout" : true,
47622         /**
47623          * @event rowclass
47624          * Fires when a row is rendered, so you can change add a style to it.
47625          * @param {GridView} gridview   The grid view
47626          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47627          */
47628         'rowclass' : true,
47629
47630         /**
47631          * @event render
47632          * Fires when the grid is rendered
47633          * @param {Grid} grid
47634          */
47635         'render' : true
47636     });
47637
47638     Roo.grid.Grid.superclass.constructor.call(this);
47639 };
47640 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47641     
47642     /**
47643      * @cfg {String} ddGroup - drag drop group.
47644      */
47645
47646     /**
47647      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47648      */
47649     minColumnWidth : 25,
47650
47651     /**
47652      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47653      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47654      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47655      */
47656     autoSizeColumns : false,
47657
47658     /**
47659      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47660      */
47661     autoSizeHeaders : true,
47662
47663     /**
47664      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47665      */
47666     monitorWindowResize : true,
47667
47668     /**
47669      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47670      * rows measured to get a columns size. Default is 0 (all rows).
47671      */
47672     maxRowsToMeasure : 0,
47673
47674     /**
47675      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47676      */
47677     trackMouseOver : true,
47678
47679     /**
47680     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47681     */
47682     
47683     /**
47684     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47685     */
47686     enableDragDrop : false,
47687     
47688     /**
47689     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47690     */
47691     enableColumnMove : true,
47692     
47693     /**
47694     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47695     */
47696     enableColumnHide : true,
47697     
47698     /**
47699     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47700     */
47701     enableRowHeightSync : false,
47702     
47703     /**
47704     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47705     */
47706     stripeRows : true,
47707     
47708     /**
47709     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47710     */
47711     autoHeight : false,
47712
47713     /**
47714      * @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.
47715      */
47716     autoExpandColumn : false,
47717
47718     /**
47719     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47720     * Default is 50.
47721     */
47722     autoExpandMin : 50,
47723
47724     /**
47725     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47726     */
47727     autoExpandMax : 1000,
47728
47729     /**
47730     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47731     */
47732     view : null,
47733
47734     /**
47735     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47736     */
47737     loadMask : false,
47738     /**
47739     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47740     */
47741     dropTarget: false,
47742     
47743    
47744     
47745     // private
47746     rendered : false,
47747
47748     /**
47749     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47750     * of a fixed width. Default is false.
47751     */
47752     /**
47753     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47754     */
47755     /**
47756      * Called once after all setup has been completed and the grid is ready to be rendered.
47757      * @return {Roo.grid.Grid} this
47758      */
47759     render : function()
47760     {
47761         var c = this.container;
47762         // try to detect autoHeight/width mode
47763         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47764             this.autoHeight = true;
47765         }
47766         var view = this.getView();
47767         view.init(this);
47768
47769         c.on("click", this.onClick, this);
47770         c.on("dblclick", this.onDblClick, this);
47771         c.on("contextmenu", this.onContextMenu, this);
47772         c.on("keydown", this.onKeyDown, this);
47773
47774         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47775
47776         this.getSelectionModel().init(this);
47777
47778         view.render();
47779
47780         if(this.loadMask){
47781             this.loadMask = new Roo.LoadMask(this.container,
47782                     Roo.apply({store:this.dataSource}, this.loadMask));
47783         }
47784         
47785         
47786         if (this.toolbar && this.toolbar.xtype) {
47787             this.toolbar.container = this.getView().getHeaderPanel(true);
47788             this.toolbar = new Roo.Toolbar(this.toolbar);
47789         }
47790         if (this.footer && this.footer.xtype) {
47791             this.footer.dataSource = this.getDataSource();
47792             this.footer.container = this.getView().getFooterPanel(true);
47793             this.footer = Roo.factory(this.footer, Roo);
47794         }
47795         if (this.dropTarget && this.dropTarget.xtype) {
47796             delete this.dropTarget.xtype;
47797             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47798         }
47799         
47800         
47801         this.rendered = true;
47802         this.fireEvent('render', this);
47803         return this;
47804     },
47805
47806         /**
47807          * Reconfigures the grid to use a different Store and Column Model.
47808          * The View will be bound to the new objects and refreshed.
47809          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47810          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47811          */
47812     reconfigure : function(dataSource, colModel){
47813         if(this.loadMask){
47814             this.loadMask.destroy();
47815             this.loadMask = new Roo.LoadMask(this.container,
47816                     Roo.apply({store:dataSource}, this.loadMask));
47817         }
47818         this.view.bind(dataSource, colModel);
47819         this.dataSource = dataSource;
47820         this.colModel = colModel;
47821         this.view.refresh(true);
47822     },
47823
47824     // private
47825     onKeyDown : function(e){
47826         this.fireEvent("keydown", e);
47827     },
47828
47829     /**
47830      * Destroy this grid.
47831      * @param {Boolean} removeEl True to remove the element
47832      */
47833     destroy : function(removeEl, keepListeners){
47834         if(this.loadMask){
47835             this.loadMask.destroy();
47836         }
47837         var c = this.container;
47838         c.removeAllListeners();
47839         this.view.destroy();
47840         this.colModel.purgeListeners();
47841         if(!keepListeners){
47842             this.purgeListeners();
47843         }
47844         c.update("");
47845         if(removeEl === true){
47846             c.remove();
47847         }
47848     },
47849
47850     // private
47851     processEvent : function(name, e){
47852         this.fireEvent(name, e);
47853         var t = e.getTarget();
47854         var v = this.view;
47855         var header = v.findHeaderIndex(t);
47856         if(header !== false){
47857             this.fireEvent("header" + name, this, header, e);
47858         }else{
47859             var row = v.findRowIndex(t);
47860             var cell = v.findCellIndex(t);
47861             if(row !== false){
47862                 this.fireEvent("row" + name, this, row, e);
47863                 if(cell !== false){
47864                     this.fireEvent("cell" + name, this, row, cell, e);
47865                 }
47866             }
47867         }
47868     },
47869
47870     // private
47871     onClick : function(e){
47872         this.processEvent("click", e);
47873     },
47874
47875     // private
47876     onContextMenu : function(e, t){
47877         this.processEvent("contextmenu", e);
47878     },
47879
47880     // private
47881     onDblClick : function(e){
47882         this.processEvent("dblclick", e);
47883     },
47884
47885     // private
47886     walkCells : function(row, col, step, fn, scope){
47887         var cm = this.colModel, clen = cm.getColumnCount();
47888         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47889         if(step < 0){
47890             if(col < 0){
47891                 row--;
47892                 first = false;
47893             }
47894             while(row >= 0){
47895                 if(!first){
47896                     col = clen-1;
47897                 }
47898                 first = false;
47899                 while(col >= 0){
47900                     if(fn.call(scope || this, row, col, cm) === true){
47901                         return [row, col];
47902                     }
47903                     col--;
47904                 }
47905                 row--;
47906             }
47907         } else {
47908             if(col >= clen){
47909                 row++;
47910                 first = false;
47911             }
47912             while(row < rlen){
47913                 if(!first){
47914                     col = 0;
47915                 }
47916                 first = false;
47917                 while(col < clen){
47918                     if(fn.call(scope || this, row, col, cm) === true){
47919                         return [row, col];
47920                     }
47921                     col++;
47922                 }
47923                 row++;
47924             }
47925         }
47926         return null;
47927     },
47928
47929     // private
47930     getSelections : function(){
47931         return this.selModel.getSelections();
47932     },
47933
47934     /**
47935      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47936      * but if manual update is required this method will initiate it.
47937      */
47938     autoSize : function(){
47939         if(this.rendered){
47940             this.view.layout();
47941             if(this.view.adjustForScroll){
47942                 this.view.adjustForScroll();
47943             }
47944         }
47945     },
47946
47947     /**
47948      * Returns the grid's underlying element.
47949      * @return {Element} The element
47950      */
47951     getGridEl : function(){
47952         return this.container;
47953     },
47954
47955     // private for compatibility, overridden by editor grid
47956     stopEditing : function(){},
47957
47958     /**
47959      * Returns the grid's SelectionModel.
47960      * @return {SelectionModel}
47961      */
47962     getSelectionModel : function(){
47963         if(!this.selModel){
47964             this.selModel = new Roo.grid.RowSelectionModel();
47965         }
47966         return this.selModel;
47967     },
47968
47969     /**
47970      * Returns the grid's DataSource.
47971      * @return {DataSource}
47972      */
47973     getDataSource : function(){
47974         return this.dataSource;
47975     },
47976
47977     /**
47978      * Returns the grid's ColumnModel.
47979      * @return {ColumnModel}
47980      */
47981     getColumnModel : function(){
47982         return this.colModel;
47983     },
47984
47985     /**
47986      * Returns the grid's GridView object.
47987      * @return {GridView}
47988      */
47989     getView : function(){
47990         if(!this.view){
47991             this.view = new Roo.grid.GridView(this.viewConfig);
47992         }
47993         return this.view;
47994     },
47995     /**
47996      * Called to get grid's drag proxy text, by default returns this.ddText.
47997      * @return {String}
47998      */
47999     getDragDropText : function(){
48000         var count = this.selModel.getCount();
48001         return String.format(this.ddText, count, count == 1 ? '' : 's');
48002     }
48003 });
48004 /**
48005  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48006  * %0 is replaced with the number of selected rows.
48007  * @type String
48008  */
48009 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48010  * Based on:
48011  * Ext JS Library 1.1.1
48012  * Copyright(c) 2006-2007, Ext JS, LLC.
48013  *
48014  * Originally Released Under LGPL - original licence link has changed is not relivant.
48015  *
48016  * Fork - LGPL
48017  * <script type="text/javascript">
48018  */
48019  
48020 Roo.grid.AbstractGridView = function(){
48021         this.grid = null;
48022         
48023         this.events = {
48024             "beforerowremoved" : true,
48025             "beforerowsinserted" : true,
48026             "beforerefresh" : true,
48027             "rowremoved" : true,
48028             "rowsinserted" : true,
48029             "rowupdated" : true,
48030             "refresh" : true
48031         };
48032     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48033 };
48034
48035 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48036     rowClass : "x-grid-row",
48037     cellClass : "x-grid-cell",
48038     tdClass : "x-grid-td",
48039     hdClass : "x-grid-hd",
48040     splitClass : "x-grid-hd-split",
48041     
48042         init: function(grid){
48043         this.grid = grid;
48044                 var cid = this.grid.getGridEl().id;
48045         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48046         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48047         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48048         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48049         },
48050         
48051         getColumnRenderers : function(){
48052         var renderers = [];
48053         var cm = this.grid.colModel;
48054         var colCount = cm.getColumnCount();
48055         for(var i = 0; i < colCount; i++){
48056             renderers[i] = cm.getRenderer(i);
48057         }
48058         return renderers;
48059     },
48060     
48061     getColumnIds : function(){
48062         var ids = [];
48063         var cm = this.grid.colModel;
48064         var colCount = cm.getColumnCount();
48065         for(var i = 0; i < colCount; i++){
48066             ids[i] = cm.getColumnId(i);
48067         }
48068         return ids;
48069     },
48070     
48071     getDataIndexes : function(){
48072         if(!this.indexMap){
48073             this.indexMap = this.buildIndexMap();
48074         }
48075         return this.indexMap.colToData;
48076     },
48077     
48078     getColumnIndexByDataIndex : function(dataIndex){
48079         if(!this.indexMap){
48080             this.indexMap = this.buildIndexMap();
48081         }
48082         return this.indexMap.dataToCol[dataIndex];
48083     },
48084     
48085     /**
48086      * Set a css style for a column dynamically. 
48087      * @param {Number} colIndex The index of the column
48088      * @param {String} name The css property name
48089      * @param {String} value The css value
48090      */
48091     setCSSStyle : function(colIndex, name, value){
48092         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48093         Roo.util.CSS.updateRule(selector, name, value);
48094     },
48095     
48096     generateRules : function(cm){
48097         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48098         Roo.util.CSS.removeStyleSheet(rulesId);
48099         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48100             var cid = cm.getColumnId(i);
48101             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48102                          this.tdSelector, cid, " {\n}\n",
48103                          this.hdSelector, cid, " {\n}\n",
48104                          this.splitSelector, cid, " {\n}\n");
48105         }
48106         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48107     }
48108 });/*
48109  * Based on:
48110  * Ext JS Library 1.1.1
48111  * Copyright(c) 2006-2007, Ext JS, LLC.
48112  *
48113  * Originally Released Under LGPL - original licence link has changed is not relivant.
48114  *
48115  * Fork - LGPL
48116  * <script type="text/javascript">
48117  */
48118
48119 // private
48120 // This is a support class used internally by the Grid components
48121 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48122     this.grid = grid;
48123     this.view = grid.getView();
48124     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48125     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48126     if(hd2){
48127         this.setHandleElId(Roo.id(hd));
48128         this.setOuterHandleElId(Roo.id(hd2));
48129     }
48130     this.scroll = false;
48131 };
48132 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48133     maxDragWidth: 120,
48134     getDragData : function(e){
48135         var t = Roo.lib.Event.getTarget(e);
48136         var h = this.view.findHeaderCell(t);
48137         if(h){
48138             return {ddel: h.firstChild, header:h};
48139         }
48140         return false;
48141     },
48142
48143     onInitDrag : function(e){
48144         this.view.headersDisabled = true;
48145         var clone = this.dragData.ddel.cloneNode(true);
48146         clone.id = Roo.id();
48147         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48148         this.proxy.update(clone);
48149         return true;
48150     },
48151
48152     afterValidDrop : function(){
48153         var v = this.view;
48154         setTimeout(function(){
48155             v.headersDisabled = false;
48156         }, 50);
48157     },
48158
48159     afterInvalidDrop : function(){
48160         var v = this.view;
48161         setTimeout(function(){
48162             v.headersDisabled = false;
48163         }, 50);
48164     }
48165 });
48166 /*
48167  * Based on:
48168  * Ext JS Library 1.1.1
48169  * Copyright(c) 2006-2007, Ext JS, LLC.
48170  *
48171  * Originally Released Under LGPL - original licence link has changed is not relivant.
48172  *
48173  * Fork - LGPL
48174  * <script type="text/javascript">
48175  */
48176 // private
48177 // This is a support class used internally by the Grid components
48178 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48179     this.grid = grid;
48180     this.view = grid.getView();
48181     // split the proxies so they don't interfere with mouse events
48182     this.proxyTop = Roo.DomHelper.append(document.body, {
48183         cls:"col-move-top", html:"&#160;"
48184     }, true);
48185     this.proxyBottom = Roo.DomHelper.append(document.body, {
48186         cls:"col-move-bottom", html:"&#160;"
48187     }, true);
48188     this.proxyTop.hide = this.proxyBottom.hide = function(){
48189         this.setLeftTop(-100,-100);
48190         this.setStyle("visibility", "hidden");
48191     };
48192     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48193     // temporarily disabled
48194     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48195     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48196 };
48197 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48198     proxyOffsets : [-4, -9],
48199     fly: Roo.Element.fly,
48200
48201     getTargetFromEvent : function(e){
48202         var t = Roo.lib.Event.getTarget(e);
48203         var cindex = this.view.findCellIndex(t);
48204         if(cindex !== false){
48205             return this.view.getHeaderCell(cindex);
48206         }
48207         return null;
48208     },
48209
48210     nextVisible : function(h){
48211         var v = this.view, cm = this.grid.colModel;
48212         h = h.nextSibling;
48213         while(h){
48214             if(!cm.isHidden(v.getCellIndex(h))){
48215                 return h;
48216             }
48217             h = h.nextSibling;
48218         }
48219         return null;
48220     },
48221
48222     prevVisible : function(h){
48223         var v = this.view, cm = this.grid.colModel;
48224         h = h.prevSibling;
48225         while(h){
48226             if(!cm.isHidden(v.getCellIndex(h))){
48227                 return h;
48228             }
48229             h = h.prevSibling;
48230         }
48231         return null;
48232     },
48233
48234     positionIndicator : function(h, n, e){
48235         var x = Roo.lib.Event.getPageX(e);
48236         var r = Roo.lib.Dom.getRegion(n.firstChild);
48237         var px, pt, py = r.top + this.proxyOffsets[1];
48238         if((r.right - x) <= (r.right-r.left)/2){
48239             px = r.right+this.view.borderWidth;
48240             pt = "after";
48241         }else{
48242             px = r.left;
48243             pt = "before";
48244         }
48245         var oldIndex = this.view.getCellIndex(h);
48246         var newIndex = this.view.getCellIndex(n);
48247
48248         if(this.grid.colModel.isFixed(newIndex)){
48249             return false;
48250         }
48251
48252         var locked = this.grid.colModel.isLocked(newIndex);
48253
48254         if(pt == "after"){
48255             newIndex++;
48256         }
48257         if(oldIndex < newIndex){
48258             newIndex--;
48259         }
48260         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48261             return false;
48262         }
48263         px +=  this.proxyOffsets[0];
48264         this.proxyTop.setLeftTop(px, py);
48265         this.proxyTop.show();
48266         if(!this.bottomOffset){
48267             this.bottomOffset = this.view.mainHd.getHeight();
48268         }
48269         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48270         this.proxyBottom.show();
48271         return pt;
48272     },
48273
48274     onNodeEnter : function(n, dd, e, data){
48275         if(data.header != n){
48276             this.positionIndicator(data.header, n, e);
48277         }
48278     },
48279
48280     onNodeOver : function(n, dd, e, data){
48281         var result = false;
48282         if(data.header != n){
48283             result = this.positionIndicator(data.header, n, e);
48284         }
48285         if(!result){
48286             this.proxyTop.hide();
48287             this.proxyBottom.hide();
48288         }
48289         return result ? this.dropAllowed : this.dropNotAllowed;
48290     },
48291
48292     onNodeOut : function(n, dd, e, data){
48293         this.proxyTop.hide();
48294         this.proxyBottom.hide();
48295     },
48296
48297     onNodeDrop : function(n, dd, e, data){
48298         var h = data.header;
48299         if(h != n){
48300             var cm = this.grid.colModel;
48301             var x = Roo.lib.Event.getPageX(e);
48302             var r = Roo.lib.Dom.getRegion(n.firstChild);
48303             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48304             var oldIndex = this.view.getCellIndex(h);
48305             var newIndex = this.view.getCellIndex(n);
48306             var locked = cm.isLocked(newIndex);
48307             if(pt == "after"){
48308                 newIndex++;
48309             }
48310             if(oldIndex < newIndex){
48311                 newIndex--;
48312             }
48313             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48314                 return false;
48315             }
48316             cm.setLocked(oldIndex, locked, true);
48317             cm.moveColumn(oldIndex, newIndex);
48318             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48319             return true;
48320         }
48321         return false;
48322     }
48323 });
48324 /*
48325  * Based on:
48326  * Ext JS Library 1.1.1
48327  * Copyright(c) 2006-2007, Ext JS, LLC.
48328  *
48329  * Originally Released Under LGPL - original licence link has changed is not relivant.
48330  *
48331  * Fork - LGPL
48332  * <script type="text/javascript">
48333  */
48334   
48335 /**
48336  * @class Roo.grid.GridView
48337  * @extends Roo.util.Observable
48338  *
48339  * @constructor
48340  * @param {Object} config
48341  */
48342 Roo.grid.GridView = function(config){
48343     Roo.grid.GridView.superclass.constructor.call(this);
48344     this.el = null;
48345
48346     Roo.apply(this, config);
48347 };
48348
48349 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48350
48351     /**
48352      * Override this function to apply custom css classes to rows during rendering
48353      * @param {Record} record The record
48354      * @param {Number} index
48355      * @method getRowClass
48356      */
48357     rowClass : "x-grid-row",
48358
48359     cellClass : "x-grid-col",
48360
48361     tdClass : "x-grid-td",
48362
48363     hdClass : "x-grid-hd",
48364
48365     splitClass : "x-grid-split",
48366
48367     sortClasses : ["sort-asc", "sort-desc"],
48368
48369     enableMoveAnim : false,
48370
48371     hlColor: "C3DAF9",
48372
48373     dh : Roo.DomHelper,
48374
48375     fly : Roo.Element.fly,
48376
48377     css : Roo.util.CSS,
48378
48379     borderWidth: 1,
48380
48381     splitOffset: 3,
48382
48383     scrollIncrement : 22,
48384
48385     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48386
48387     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48388
48389     bind : function(ds, cm){
48390         if(this.ds){
48391             this.ds.un("load", this.onLoad, this);
48392             this.ds.un("datachanged", this.onDataChange, this);
48393             this.ds.un("add", this.onAdd, this);
48394             this.ds.un("remove", this.onRemove, this);
48395             this.ds.un("update", this.onUpdate, this);
48396             this.ds.un("clear", this.onClear, this);
48397         }
48398         if(ds){
48399             ds.on("load", this.onLoad, this);
48400             ds.on("datachanged", this.onDataChange, this);
48401             ds.on("add", this.onAdd, this);
48402             ds.on("remove", this.onRemove, this);
48403             ds.on("update", this.onUpdate, this);
48404             ds.on("clear", this.onClear, this);
48405         }
48406         this.ds = ds;
48407
48408         if(this.cm){
48409             this.cm.un("widthchange", this.onColWidthChange, this);
48410             this.cm.un("headerchange", this.onHeaderChange, this);
48411             this.cm.un("hiddenchange", this.onHiddenChange, this);
48412             this.cm.un("columnmoved", this.onColumnMove, this);
48413             this.cm.un("columnlockchange", this.onColumnLock, this);
48414         }
48415         if(cm){
48416             this.generateRules(cm);
48417             cm.on("widthchange", this.onColWidthChange, this);
48418             cm.on("headerchange", this.onHeaderChange, this);
48419             cm.on("hiddenchange", this.onHiddenChange, this);
48420             cm.on("columnmoved", this.onColumnMove, this);
48421             cm.on("columnlockchange", this.onColumnLock, this);
48422         }
48423         this.cm = cm;
48424     },
48425
48426     init: function(grid){
48427         Roo.grid.GridView.superclass.init.call(this, grid);
48428
48429         this.bind(grid.dataSource, grid.colModel);
48430
48431         grid.on("headerclick", this.handleHeaderClick, this);
48432
48433         if(grid.trackMouseOver){
48434             grid.on("mouseover", this.onRowOver, this);
48435             grid.on("mouseout", this.onRowOut, this);
48436         }
48437         grid.cancelTextSelection = function(){};
48438         this.gridId = grid.id;
48439
48440         var tpls = this.templates || {};
48441
48442         if(!tpls.master){
48443             tpls.master = new Roo.Template(
48444                '<div class="x-grid" hidefocus="true">',
48445                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48446                   '<div class="x-grid-topbar"></div>',
48447                   '<div class="x-grid-scroller"><div></div></div>',
48448                   '<div class="x-grid-locked">',
48449                       '<div class="x-grid-header">{lockedHeader}</div>',
48450                       '<div class="x-grid-body">{lockedBody}</div>',
48451                   "</div>",
48452                   '<div class="x-grid-viewport">',
48453                       '<div class="x-grid-header">{header}</div>',
48454                       '<div class="x-grid-body">{body}</div>',
48455                   "</div>",
48456                   '<div class="x-grid-bottombar"></div>',
48457                  
48458                   '<div class="x-grid-resize-proxy">&#160;</div>',
48459                "</div>"
48460             );
48461             tpls.master.disableformats = true;
48462         }
48463
48464         if(!tpls.header){
48465             tpls.header = new Roo.Template(
48466                '<table border="0" cellspacing="0" cellpadding="0">',
48467                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48468                "</table>{splits}"
48469             );
48470             tpls.header.disableformats = true;
48471         }
48472         tpls.header.compile();
48473
48474         if(!tpls.hcell){
48475             tpls.hcell = new Roo.Template(
48476                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48477                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48478                 "</div></td>"
48479              );
48480              tpls.hcell.disableFormats = true;
48481         }
48482         tpls.hcell.compile();
48483
48484         if(!tpls.hsplit){
48485             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48486             tpls.hsplit.disableFormats = true;
48487         }
48488         tpls.hsplit.compile();
48489
48490         if(!tpls.body){
48491             tpls.body = new Roo.Template(
48492                '<table border="0" cellspacing="0" cellpadding="0">',
48493                "<tbody>{rows}</tbody>",
48494                "</table>"
48495             );
48496             tpls.body.disableFormats = true;
48497         }
48498         tpls.body.compile();
48499
48500         if(!tpls.row){
48501             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48502             tpls.row.disableFormats = true;
48503         }
48504         tpls.row.compile();
48505
48506         if(!tpls.cell){
48507             tpls.cell = new Roo.Template(
48508                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48509                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48510                 "</td>"
48511             );
48512             tpls.cell.disableFormats = true;
48513         }
48514         tpls.cell.compile();
48515
48516         this.templates = tpls;
48517     },
48518
48519     // remap these for backwards compat
48520     onColWidthChange : function(){
48521         this.updateColumns.apply(this, arguments);
48522     },
48523     onHeaderChange : function(){
48524         this.updateHeaders.apply(this, arguments);
48525     }, 
48526     onHiddenChange : function(){
48527         this.handleHiddenChange.apply(this, arguments);
48528     },
48529     onColumnMove : function(){
48530         this.handleColumnMove.apply(this, arguments);
48531     },
48532     onColumnLock : function(){
48533         this.handleLockChange.apply(this, arguments);
48534     },
48535
48536     onDataChange : function(){
48537         this.refresh();
48538         this.updateHeaderSortState();
48539     },
48540
48541     onClear : function(){
48542         this.refresh();
48543     },
48544
48545     onUpdate : function(ds, record){
48546         this.refreshRow(record);
48547     },
48548
48549     refreshRow : function(record){
48550         var ds = this.ds, index;
48551         if(typeof record == 'number'){
48552             index = record;
48553             record = ds.getAt(index);
48554         }else{
48555             index = ds.indexOf(record);
48556         }
48557         this.insertRows(ds, index, index, true);
48558         this.onRemove(ds, record, index+1, true);
48559         this.syncRowHeights(index, index);
48560         this.layout();
48561         this.fireEvent("rowupdated", this, index, record);
48562     },
48563
48564     onAdd : function(ds, records, index){
48565         this.insertRows(ds, index, index + (records.length-1));
48566     },
48567
48568     onRemove : function(ds, record, index, isUpdate){
48569         if(isUpdate !== true){
48570             this.fireEvent("beforerowremoved", this, index, record);
48571         }
48572         var bt = this.getBodyTable(), lt = this.getLockedTable();
48573         if(bt.rows[index]){
48574             bt.firstChild.removeChild(bt.rows[index]);
48575         }
48576         if(lt.rows[index]){
48577             lt.firstChild.removeChild(lt.rows[index]);
48578         }
48579         if(isUpdate !== true){
48580             this.stripeRows(index);
48581             this.syncRowHeights(index, index);
48582             this.layout();
48583             this.fireEvent("rowremoved", this, index, record);
48584         }
48585     },
48586
48587     onLoad : function(){
48588         this.scrollToTop();
48589     },
48590
48591     /**
48592      * Scrolls the grid to the top
48593      */
48594     scrollToTop : function(){
48595         if(this.scroller){
48596             this.scroller.dom.scrollTop = 0;
48597             this.syncScroll();
48598         }
48599     },
48600
48601     /**
48602      * Gets a panel in the header of the grid that can be used for toolbars etc.
48603      * After modifying the contents of this panel a call to grid.autoSize() may be
48604      * required to register any changes in size.
48605      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48606      * @return Roo.Element
48607      */
48608     getHeaderPanel : function(doShow){
48609         if(doShow){
48610             this.headerPanel.show();
48611         }
48612         return this.headerPanel;
48613     },
48614
48615     /**
48616      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48617      * After modifying the contents of this panel a call to grid.autoSize() may be
48618      * required to register any changes in size.
48619      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48620      * @return Roo.Element
48621      */
48622     getFooterPanel : function(doShow){
48623         if(doShow){
48624             this.footerPanel.show();
48625         }
48626         return this.footerPanel;
48627     },
48628
48629     initElements : function(){
48630         var E = Roo.Element;
48631         var el = this.grid.getGridEl().dom.firstChild;
48632         var cs = el.childNodes;
48633
48634         this.el = new E(el);
48635         
48636          this.focusEl = new E(el.firstChild);
48637         this.focusEl.swallowEvent("click", true);
48638         
48639         this.headerPanel = new E(cs[1]);
48640         this.headerPanel.enableDisplayMode("block");
48641
48642         this.scroller = new E(cs[2]);
48643         this.scrollSizer = new E(this.scroller.dom.firstChild);
48644
48645         this.lockedWrap = new E(cs[3]);
48646         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48647         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48648
48649         this.mainWrap = new E(cs[4]);
48650         this.mainHd = new E(this.mainWrap.dom.firstChild);
48651         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48652
48653         this.footerPanel = new E(cs[5]);
48654         this.footerPanel.enableDisplayMode("block");
48655
48656         this.resizeProxy = new E(cs[6]);
48657
48658         this.headerSelector = String.format(
48659            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48660            this.lockedHd.id, this.mainHd.id
48661         );
48662
48663         this.splitterSelector = String.format(
48664            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48665            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48666         );
48667     },
48668     idToCssName : function(s)
48669     {
48670         return s.replace(/[^a-z0-9]+/ig, '-');
48671     },
48672
48673     getHeaderCell : function(index){
48674         return Roo.DomQuery.select(this.headerSelector)[index];
48675     },
48676
48677     getHeaderCellMeasure : function(index){
48678         return this.getHeaderCell(index).firstChild;
48679     },
48680
48681     getHeaderCellText : function(index){
48682         return this.getHeaderCell(index).firstChild.firstChild;
48683     },
48684
48685     getLockedTable : function(){
48686         return this.lockedBody.dom.firstChild;
48687     },
48688
48689     getBodyTable : function(){
48690         return this.mainBody.dom.firstChild;
48691     },
48692
48693     getLockedRow : function(index){
48694         return this.getLockedTable().rows[index];
48695     },
48696
48697     getRow : function(index){
48698         return this.getBodyTable().rows[index];
48699     },
48700
48701     getRowComposite : function(index){
48702         if(!this.rowEl){
48703             this.rowEl = new Roo.CompositeElementLite();
48704         }
48705         var els = [], lrow, mrow;
48706         if(lrow = this.getLockedRow(index)){
48707             els.push(lrow);
48708         }
48709         if(mrow = this.getRow(index)){
48710             els.push(mrow);
48711         }
48712         this.rowEl.elements = els;
48713         return this.rowEl;
48714     },
48715     /**
48716      * Gets the 'td' of the cell
48717      * 
48718      * @param {Integer} rowIndex row to select
48719      * @param {Integer} colIndex column to select
48720      * 
48721      * @return {Object} 
48722      */
48723     getCell : function(rowIndex, colIndex){
48724         var locked = this.cm.getLockedCount();
48725         var source;
48726         if(colIndex < locked){
48727             source = this.lockedBody.dom.firstChild;
48728         }else{
48729             source = this.mainBody.dom.firstChild;
48730             colIndex -= locked;
48731         }
48732         return source.rows[rowIndex].childNodes[colIndex];
48733     },
48734
48735     getCellText : function(rowIndex, colIndex){
48736         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48737     },
48738
48739     getCellBox : function(cell){
48740         var b = this.fly(cell).getBox();
48741         if(Roo.isOpera){ // opera fails to report the Y
48742             b.y = cell.offsetTop + this.mainBody.getY();
48743         }
48744         return b;
48745     },
48746
48747     getCellIndex : function(cell){
48748         var id = String(cell.className).match(this.cellRE);
48749         if(id){
48750             return parseInt(id[1], 10);
48751         }
48752         return 0;
48753     },
48754
48755     findHeaderIndex : function(n){
48756         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48757         return r ? this.getCellIndex(r) : false;
48758     },
48759
48760     findHeaderCell : function(n){
48761         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48762         return r ? r : false;
48763     },
48764
48765     findRowIndex : function(n){
48766         if(!n){
48767             return false;
48768         }
48769         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48770         return r ? r.rowIndex : false;
48771     },
48772
48773     findCellIndex : function(node){
48774         var stop = this.el.dom;
48775         while(node && node != stop){
48776             if(this.findRE.test(node.className)){
48777                 return this.getCellIndex(node);
48778             }
48779             node = node.parentNode;
48780         }
48781         return false;
48782     },
48783
48784     getColumnId : function(index){
48785         return this.cm.getColumnId(index);
48786     },
48787
48788     getSplitters : function()
48789     {
48790         if(this.splitterSelector){
48791            return Roo.DomQuery.select(this.splitterSelector);
48792         }else{
48793             return null;
48794       }
48795     },
48796
48797     getSplitter : function(index){
48798         return this.getSplitters()[index];
48799     },
48800
48801     onRowOver : function(e, t){
48802         var row;
48803         if((row = this.findRowIndex(t)) !== false){
48804             this.getRowComposite(row).addClass("x-grid-row-over");
48805         }
48806     },
48807
48808     onRowOut : function(e, t){
48809         var row;
48810         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48811             this.getRowComposite(row).removeClass("x-grid-row-over");
48812         }
48813     },
48814
48815     renderHeaders : function(){
48816         var cm = this.cm;
48817         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48818         var cb = [], lb = [], sb = [], lsb = [], p = {};
48819         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48820             p.cellId = "x-grid-hd-0-" + i;
48821             p.splitId = "x-grid-csplit-0-" + i;
48822             p.id = cm.getColumnId(i);
48823             p.title = cm.getColumnTooltip(i) || "";
48824             p.value = cm.getColumnHeader(i) || "";
48825             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48826             if(!cm.isLocked(i)){
48827                 cb[cb.length] = ct.apply(p);
48828                 sb[sb.length] = st.apply(p);
48829             }else{
48830                 lb[lb.length] = ct.apply(p);
48831                 lsb[lsb.length] = st.apply(p);
48832             }
48833         }
48834         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48835                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48836     },
48837
48838     updateHeaders : function(){
48839         var html = this.renderHeaders();
48840         this.lockedHd.update(html[0]);
48841         this.mainHd.update(html[1]);
48842     },
48843
48844     /**
48845      * Focuses the specified row.
48846      * @param {Number} row The row index
48847      */
48848     focusRow : function(row)
48849     {
48850         //Roo.log('GridView.focusRow');
48851         var x = this.scroller.dom.scrollLeft;
48852         this.focusCell(row, 0, false);
48853         this.scroller.dom.scrollLeft = x;
48854     },
48855
48856     /**
48857      * Focuses the specified cell.
48858      * @param {Number} row The row index
48859      * @param {Number} col The column index
48860      * @param {Boolean} hscroll false to disable horizontal scrolling
48861      */
48862     focusCell : function(row, col, hscroll)
48863     {
48864         //Roo.log('GridView.focusCell');
48865         var el = this.ensureVisible(row, col, hscroll);
48866         this.focusEl.alignTo(el, "tl-tl");
48867         if(Roo.isGecko){
48868             this.focusEl.focus();
48869         }else{
48870             this.focusEl.focus.defer(1, this.focusEl);
48871         }
48872     },
48873
48874     /**
48875      * Scrolls the specified cell into view
48876      * @param {Number} row The row index
48877      * @param {Number} col The column index
48878      * @param {Boolean} hscroll false to disable horizontal scrolling
48879      */
48880     ensureVisible : function(row, col, hscroll)
48881     {
48882         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48883         //return null; //disable for testing.
48884         if(typeof row != "number"){
48885             row = row.rowIndex;
48886         }
48887         if(row < 0 && row >= this.ds.getCount()){
48888             return  null;
48889         }
48890         col = (col !== undefined ? col : 0);
48891         var cm = this.grid.colModel;
48892         while(cm.isHidden(col)){
48893             col++;
48894         }
48895
48896         var el = this.getCell(row, col);
48897         if(!el){
48898             return null;
48899         }
48900         var c = this.scroller.dom;
48901
48902         var ctop = parseInt(el.offsetTop, 10);
48903         var cleft = parseInt(el.offsetLeft, 10);
48904         var cbot = ctop + el.offsetHeight;
48905         var cright = cleft + el.offsetWidth;
48906         
48907         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48908         var stop = parseInt(c.scrollTop, 10);
48909         var sleft = parseInt(c.scrollLeft, 10);
48910         var sbot = stop + ch;
48911         var sright = sleft + c.clientWidth;
48912         /*
48913         Roo.log('GridView.ensureVisible:' +
48914                 ' ctop:' + ctop +
48915                 ' c.clientHeight:' + c.clientHeight +
48916                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48917                 ' stop:' + stop +
48918                 ' cbot:' + cbot +
48919                 ' sbot:' + sbot +
48920                 ' ch:' + ch  
48921                 );
48922         */
48923         if(ctop < stop){
48924              c.scrollTop = ctop;
48925             //Roo.log("set scrolltop to ctop DISABLE?");
48926         }else if(cbot > sbot){
48927             //Roo.log("set scrolltop to cbot-ch");
48928             c.scrollTop = cbot-ch;
48929         }
48930         
48931         if(hscroll !== false){
48932             if(cleft < sleft){
48933                 c.scrollLeft = cleft;
48934             }else if(cright > sright){
48935                 c.scrollLeft = cright-c.clientWidth;
48936             }
48937         }
48938          
48939         return el;
48940     },
48941
48942     updateColumns : function(){
48943         this.grid.stopEditing();
48944         var cm = this.grid.colModel, colIds = this.getColumnIds();
48945         //var totalWidth = cm.getTotalWidth();
48946         var pos = 0;
48947         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48948             //if(cm.isHidden(i)) continue;
48949             var w = cm.getColumnWidth(i);
48950             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48951             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48952         }
48953         this.updateSplitters();
48954     },
48955
48956     generateRules : function(cm){
48957         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48958         Roo.util.CSS.removeStyleSheet(rulesId);
48959         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48960             var cid = cm.getColumnId(i);
48961             var align = '';
48962             if(cm.config[i].align){
48963                 align = 'text-align:'+cm.config[i].align+';';
48964             }
48965             var hidden = '';
48966             if(cm.isHidden(i)){
48967                 hidden = 'display:none;';
48968             }
48969             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48970             ruleBuf.push(
48971                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48972                     this.hdSelector, cid, " {\n", align, width, "}\n",
48973                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48974                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48975         }
48976         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48977     },
48978
48979     updateSplitters : function(){
48980         var cm = this.cm, s = this.getSplitters();
48981         if(s){ // splitters not created yet
48982             var pos = 0, locked = true;
48983             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48984                 if(cm.isHidden(i)) continue;
48985                 var w = cm.getColumnWidth(i); // make sure it's a number
48986                 if(!cm.isLocked(i) && locked){
48987                     pos = 0;
48988                     locked = false;
48989                 }
48990                 pos += w;
48991                 s[i].style.left = (pos-this.splitOffset) + "px";
48992             }
48993         }
48994     },
48995
48996     handleHiddenChange : function(colModel, colIndex, hidden){
48997         if(hidden){
48998             this.hideColumn(colIndex);
48999         }else{
49000             this.unhideColumn(colIndex);
49001         }
49002     },
49003
49004     hideColumn : function(colIndex){
49005         var cid = this.getColumnId(colIndex);
49006         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49007         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49008         if(Roo.isSafari){
49009             this.updateHeaders();
49010         }
49011         this.updateSplitters();
49012         this.layout();
49013     },
49014
49015     unhideColumn : function(colIndex){
49016         var cid = this.getColumnId(colIndex);
49017         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49018         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49019
49020         if(Roo.isSafari){
49021             this.updateHeaders();
49022         }
49023         this.updateSplitters();
49024         this.layout();
49025     },
49026
49027     insertRows : function(dm, firstRow, lastRow, isUpdate){
49028         if(firstRow == 0 && lastRow == dm.getCount()-1){
49029             this.refresh();
49030         }else{
49031             if(!isUpdate){
49032                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49033             }
49034             var s = this.getScrollState();
49035             var markup = this.renderRows(firstRow, lastRow);
49036             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49037             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49038             this.restoreScroll(s);
49039             if(!isUpdate){
49040                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49041                 this.syncRowHeights(firstRow, lastRow);
49042                 this.stripeRows(firstRow);
49043                 this.layout();
49044             }
49045         }
49046     },
49047
49048     bufferRows : function(markup, target, index){
49049         var before = null, trows = target.rows, tbody = target.tBodies[0];
49050         if(index < trows.length){
49051             before = trows[index];
49052         }
49053         var b = document.createElement("div");
49054         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49055         var rows = b.firstChild.rows;
49056         for(var i = 0, len = rows.length; i < len; i++){
49057             if(before){
49058                 tbody.insertBefore(rows[0], before);
49059             }else{
49060                 tbody.appendChild(rows[0]);
49061             }
49062         }
49063         b.innerHTML = "";
49064         b = null;
49065     },
49066
49067     deleteRows : function(dm, firstRow, lastRow){
49068         if(dm.getRowCount()<1){
49069             this.fireEvent("beforerefresh", this);
49070             this.mainBody.update("");
49071             this.lockedBody.update("");
49072             this.fireEvent("refresh", this);
49073         }else{
49074             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49075             var bt = this.getBodyTable();
49076             var tbody = bt.firstChild;
49077             var rows = bt.rows;
49078             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49079                 tbody.removeChild(rows[firstRow]);
49080             }
49081             this.stripeRows(firstRow);
49082             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49083         }
49084     },
49085
49086     updateRows : function(dataSource, firstRow, lastRow){
49087         var s = this.getScrollState();
49088         this.refresh();
49089         this.restoreScroll(s);
49090     },
49091
49092     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49093         if(!noRefresh){
49094            this.refresh();
49095         }
49096         this.updateHeaderSortState();
49097     },
49098
49099     getScrollState : function(){
49100         
49101         var sb = this.scroller.dom;
49102         return {left: sb.scrollLeft, top: sb.scrollTop};
49103     },
49104
49105     stripeRows : function(startRow){
49106         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49107             return;
49108         }
49109         startRow = startRow || 0;
49110         var rows = this.getBodyTable().rows;
49111         var lrows = this.getLockedTable().rows;
49112         var cls = ' x-grid-row-alt ';
49113         for(var i = startRow, len = rows.length; i < len; i++){
49114             var row = rows[i], lrow = lrows[i];
49115             var isAlt = ((i+1) % 2 == 0);
49116             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49117             if(isAlt == hasAlt){
49118                 continue;
49119             }
49120             if(isAlt){
49121                 row.className += " x-grid-row-alt";
49122             }else{
49123                 row.className = row.className.replace("x-grid-row-alt", "");
49124             }
49125             if(lrow){
49126                 lrow.className = row.className;
49127             }
49128         }
49129     },
49130
49131     restoreScroll : function(state){
49132         //Roo.log('GridView.restoreScroll');
49133         var sb = this.scroller.dom;
49134         sb.scrollLeft = state.left;
49135         sb.scrollTop = state.top;
49136         this.syncScroll();
49137     },
49138
49139     syncScroll : function(){
49140         //Roo.log('GridView.syncScroll');
49141         var sb = this.scroller.dom;
49142         var sh = this.mainHd.dom;
49143         var bs = this.mainBody.dom;
49144         var lv = this.lockedBody.dom;
49145         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49146         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49147     },
49148
49149     handleScroll : function(e){
49150         this.syncScroll();
49151         var sb = this.scroller.dom;
49152         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49153         e.stopEvent();
49154     },
49155
49156     handleWheel : function(e){
49157         var d = e.getWheelDelta();
49158         this.scroller.dom.scrollTop -= d*22;
49159         // set this here to prevent jumpy scrolling on large tables
49160         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49161         e.stopEvent();
49162     },
49163
49164     renderRows : function(startRow, endRow){
49165         // pull in all the crap needed to render rows
49166         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49167         var colCount = cm.getColumnCount();
49168
49169         if(ds.getCount() < 1){
49170             return ["", ""];
49171         }
49172
49173         // build a map for all the columns
49174         var cs = [];
49175         for(var i = 0; i < colCount; i++){
49176             var name = cm.getDataIndex(i);
49177             cs[i] = {
49178                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49179                 renderer : cm.getRenderer(i),
49180                 id : cm.getColumnId(i),
49181                 locked : cm.isLocked(i)
49182             };
49183         }
49184
49185         startRow = startRow || 0;
49186         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49187
49188         // records to render
49189         var rs = ds.getRange(startRow, endRow);
49190
49191         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49192     },
49193
49194     // As much as I hate to duplicate code, this was branched because FireFox really hates
49195     // [].join("") on strings. The performance difference was substantial enough to
49196     // branch this function
49197     doRender : Roo.isGecko ?
49198             function(cs, rs, ds, startRow, colCount, stripe){
49199                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49200                 // buffers
49201                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49202                 
49203                 var hasListener = this.grid.hasListener('rowclass');
49204                 var rowcfg = {};
49205                 for(var j = 0, len = rs.length; j < len; j++){
49206                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49207                     for(var i = 0; i < colCount; i++){
49208                         c = cs[i];
49209                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49210                         p.id = c.id;
49211                         p.css = p.attr = "";
49212                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49213                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49214                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49215                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49216                         }
49217                         var markup = ct.apply(p);
49218                         if(!c.locked){
49219                             cb+= markup;
49220                         }else{
49221                             lcb+= markup;
49222                         }
49223                     }
49224                     var alt = [];
49225                     if(stripe && ((rowIndex+1) % 2 == 0)){
49226                         alt.push("x-grid-row-alt")
49227                     }
49228                     if(r.dirty){
49229                         alt.push(  " x-grid-dirty-row");
49230                     }
49231                     rp.cells = lcb;
49232                     if(this.getRowClass){
49233                         alt.push(this.getRowClass(r, rowIndex));
49234                     }
49235                     if (hasListener) {
49236                         rowcfg = {
49237                              
49238                             record: r,
49239                             rowIndex : rowIndex,
49240                             rowClass : ''
49241                         }
49242                         this.grid.fireEvent('rowclass', this, rowcfg);
49243                         alt.push(rowcfg.rowClass);
49244                     }
49245                     rp.alt = alt.join(" ");
49246                     lbuf+= rt.apply(rp);
49247                     rp.cells = cb;
49248                     buf+=  rt.apply(rp);
49249                 }
49250                 return [lbuf, buf];
49251             } :
49252             function(cs, rs, ds, startRow, colCount, stripe){
49253                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49254                 // buffers
49255                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49256                 var hasListener = this.grid.hasListener('rowclass');
49257                 var rowcfg = {};
49258                 for(var j = 0, len = rs.length; j < len; j++){
49259                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49260                     for(var i = 0; i < colCount; i++){
49261                         c = cs[i];
49262                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49263                         p.id = c.id;
49264                         p.css = p.attr = "";
49265                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49266                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49267                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49268                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49269                         }
49270                         var markup = ct.apply(p);
49271                         if(!c.locked){
49272                             cb[cb.length] = markup;
49273                         }else{
49274                             lcb[lcb.length] = markup;
49275                         }
49276                     }
49277                     var alt = [];
49278                     if(stripe && ((rowIndex+1) % 2 == 0)){
49279                         alt.push( "x-grid-row-alt");
49280                     }
49281                     if(r.dirty){
49282                         alt.push(" x-grid-dirty-row");
49283                     }
49284                     rp.cells = lcb;
49285                     if(this.getRowClass){
49286                         alt.push( this.getRowClass(r, rowIndex));
49287                     }
49288                     if (hasListener) {
49289                         rowcfg = {
49290                              
49291                             record: r,
49292                             rowIndex : rowIndex,
49293                             rowClass : ''
49294                         }
49295                         this.grid.fireEvent('rowclass', this, rowcfg);
49296                         alt.push(rowcfg.rowClass);
49297                     }
49298                     rp.alt = alt.join(" ");
49299                     rp.cells = lcb.join("");
49300                     lbuf[lbuf.length] = rt.apply(rp);
49301                     rp.cells = cb.join("");
49302                     buf[buf.length] =  rt.apply(rp);
49303                 }
49304                 return [lbuf.join(""), buf.join("")];
49305             },
49306
49307     renderBody : function(){
49308         var markup = this.renderRows();
49309         var bt = this.templates.body;
49310         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49311     },
49312
49313     /**
49314      * Refreshes the grid
49315      * @param {Boolean} headersToo
49316      */
49317     refresh : function(headersToo){
49318         this.fireEvent("beforerefresh", this);
49319         this.grid.stopEditing();
49320         var result = this.renderBody();
49321         this.lockedBody.update(result[0]);
49322         this.mainBody.update(result[1]);
49323         if(headersToo === true){
49324             this.updateHeaders();
49325             this.updateColumns();
49326             this.updateSplitters();
49327             this.updateHeaderSortState();
49328         }
49329         this.syncRowHeights();
49330         this.layout();
49331         this.fireEvent("refresh", this);
49332     },
49333
49334     handleColumnMove : function(cm, oldIndex, newIndex){
49335         this.indexMap = null;
49336         var s = this.getScrollState();
49337         this.refresh(true);
49338         this.restoreScroll(s);
49339         this.afterMove(newIndex);
49340     },
49341
49342     afterMove : function(colIndex){
49343         if(this.enableMoveAnim && Roo.enableFx){
49344             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49345         }
49346         // if multisort - fix sortOrder, and reload..
49347         if (this.grid.dataSource.multiSort) {
49348             // the we can call sort again..
49349             var dm = this.grid.dataSource;
49350             var cm = this.grid.colModel;
49351             var so = [];
49352             for(var i = 0; i < cm.config.length; i++ ) {
49353                 
49354                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49355                     continue; // dont' bother, it's not in sort list or being set.
49356                 }
49357                 
49358                 so.push(cm.config[i].dataIndex);
49359             };
49360             dm.sortOrder = so;
49361             dm.load(dm.lastOptions);
49362             
49363             
49364         }
49365         
49366     },
49367
49368     updateCell : function(dm, rowIndex, dataIndex){
49369         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49370         if(typeof colIndex == "undefined"){ // not present in grid
49371             return;
49372         }
49373         var cm = this.grid.colModel;
49374         var cell = this.getCell(rowIndex, colIndex);
49375         var cellText = this.getCellText(rowIndex, colIndex);
49376
49377         var p = {
49378             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49379             id : cm.getColumnId(colIndex),
49380             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49381         };
49382         var renderer = cm.getRenderer(colIndex);
49383         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49384         if(typeof val == "undefined" || val === "") val = "&#160;";
49385         cellText.innerHTML = val;
49386         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49387         this.syncRowHeights(rowIndex, rowIndex);
49388     },
49389
49390     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49391         var maxWidth = 0;
49392         if(this.grid.autoSizeHeaders){
49393             var h = this.getHeaderCellMeasure(colIndex);
49394             maxWidth = Math.max(maxWidth, h.scrollWidth);
49395         }
49396         var tb, index;
49397         if(this.cm.isLocked(colIndex)){
49398             tb = this.getLockedTable();
49399             index = colIndex;
49400         }else{
49401             tb = this.getBodyTable();
49402             index = colIndex - this.cm.getLockedCount();
49403         }
49404         if(tb && tb.rows){
49405             var rows = tb.rows;
49406             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49407             for(var i = 0; i < stopIndex; i++){
49408                 var cell = rows[i].childNodes[index].firstChild;
49409                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49410             }
49411         }
49412         return maxWidth + /*margin for error in IE*/ 5;
49413     },
49414     /**
49415      * Autofit a column to its content.
49416      * @param {Number} colIndex
49417      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49418      */
49419      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49420          if(this.cm.isHidden(colIndex)){
49421              return; // can't calc a hidden column
49422          }
49423         if(forceMinSize){
49424             var cid = this.cm.getColumnId(colIndex);
49425             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49426            if(this.grid.autoSizeHeaders){
49427                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49428            }
49429         }
49430         var newWidth = this.calcColumnWidth(colIndex);
49431         this.cm.setColumnWidth(colIndex,
49432             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49433         if(!suppressEvent){
49434             this.grid.fireEvent("columnresize", colIndex, newWidth);
49435         }
49436     },
49437
49438     /**
49439      * Autofits all columns to their content and then expands to fit any extra space in the grid
49440      */
49441      autoSizeColumns : function(){
49442         var cm = this.grid.colModel;
49443         var colCount = cm.getColumnCount();
49444         for(var i = 0; i < colCount; i++){
49445             this.autoSizeColumn(i, true, true);
49446         }
49447         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49448             this.fitColumns();
49449         }else{
49450             this.updateColumns();
49451             this.layout();
49452         }
49453     },
49454
49455     /**
49456      * Autofits all columns to the grid's width proportionate with their current size
49457      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49458      */
49459     fitColumns : function(reserveScrollSpace){
49460         var cm = this.grid.colModel;
49461         var colCount = cm.getColumnCount();
49462         var cols = [];
49463         var width = 0;
49464         var i, w;
49465         for (i = 0; i < colCount; i++){
49466             if(!cm.isHidden(i) && !cm.isFixed(i)){
49467                 w = cm.getColumnWidth(i);
49468                 cols.push(i);
49469                 cols.push(w);
49470                 width += w;
49471             }
49472         }
49473         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49474         if(reserveScrollSpace){
49475             avail -= 17;
49476         }
49477         var frac = (avail - cm.getTotalWidth())/width;
49478         while (cols.length){
49479             w = cols.pop();
49480             i = cols.pop();
49481             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49482         }
49483         this.updateColumns();
49484         this.layout();
49485     },
49486
49487     onRowSelect : function(rowIndex){
49488         var row = this.getRowComposite(rowIndex);
49489         row.addClass("x-grid-row-selected");
49490     },
49491
49492     onRowDeselect : function(rowIndex){
49493         var row = this.getRowComposite(rowIndex);
49494         row.removeClass("x-grid-row-selected");
49495     },
49496
49497     onCellSelect : function(row, col){
49498         var cell = this.getCell(row, col);
49499         if(cell){
49500             Roo.fly(cell).addClass("x-grid-cell-selected");
49501         }
49502     },
49503
49504     onCellDeselect : function(row, col){
49505         var cell = this.getCell(row, col);
49506         if(cell){
49507             Roo.fly(cell).removeClass("x-grid-cell-selected");
49508         }
49509     },
49510
49511     updateHeaderSortState : function(){
49512         
49513         // sort state can be single { field: xxx, direction : yyy}
49514         // or   { xxx=>ASC , yyy : DESC ..... }
49515         
49516         var mstate = {};
49517         if (!this.ds.multiSort) { 
49518             var state = this.ds.getSortState();
49519             if(!state){
49520                 return;
49521             }
49522             mstate[state.field] = state.direction;
49523             // FIXME... - this is not used here.. but might be elsewhere..
49524             this.sortState = state;
49525             
49526         } else {
49527             mstate = this.ds.sortToggle;
49528         }
49529         //remove existing sort classes..
49530         
49531         var sc = this.sortClasses;
49532         var hds = this.el.select(this.headerSelector).removeClass(sc);
49533         
49534         for(var f in mstate) {
49535         
49536             var sortColumn = this.cm.findColumnIndex(f);
49537             
49538             if(sortColumn != -1){
49539                 var sortDir = mstate[f];        
49540                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49541             }
49542         }
49543         
49544          
49545         
49546     },
49547
49548
49549     handleHeaderClick : function(g, index){
49550         if(this.headersDisabled){
49551             return;
49552         }
49553         var dm = g.dataSource, cm = g.colModel;
49554         if(!cm.isSortable(index)){
49555             return;
49556         }
49557         g.stopEditing();
49558         
49559         if (dm.multiSort) {
49560             // update the sortOrder
49561             var so = [];
49562             for(var i = 0; i < cm.config.length; i++ ) {
49563                 
49564                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49565                     continue; // dont' bother, it's not in sort list or being set.
49566                 }
49567                 
49568                 so.push(cm.config[i].dataIndex);
49569             };
49570             dm.sortOrder = so;
49571         }
49572         
49573         
49574         dm.sort(cm.getDataIndex(index));
49575     },
49576
49577
49578     destroy : function(){
49579         if(this.colMenu){
49580             this.colMenu.removeAll();
49581             Roo.menu.MenuMgr.unregister(this.colMenu);
49582             this.colMenu.getEl().remove();
49583             delete this.colMenu;
49584         }
49585         if(this.hmenu){
49586             this.hmenu.removeAll();
49587             Roo.menu.MenuMgr.unregister(this.hmenu);
49588             this.hmenu.getEl().remove();
49589             delete this.hmenu;
49590         }
49591         if(this.grid.enableColumnMove){
49592             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49593             if(dds){
49594                 for(var dd in dds){
49595                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49596                         var elid = dds[dd].dragElId;
49597                         dds[dd].unreg();
49598                         Roo.get(elid).remove();
49599                     } else if(dds[dd].config.isTarget){
49600                         dds[dd].proxyTop.remove();
49601                         dds[dd].proxyBottom.remove();
49602                         dds[dd].unreg();
49603                     }
49604                     if(Roo.dd.DDM.locationCache[dd]){
49605                         delete Roo.dd.DDM.locationCache[dd];
49606                     }
49607                 }
49608                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49609             }
49610         }
49611         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49612         this.bind(null, null);
49613         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49614     },
49615
49616     handleLockChange : function(){
49617         this.refresh(true);
49618     },
49619
49620     onDenyColumnLock : function(){
49621
49622     },
49623
49624     onDenyColumnHide : function(){
49625
49626     },
49627
49628     handleHdMenuClick : function(item){
49629         var index = this.hdCtxIndex;
49630         var cm = this.cm, ds = this.ds;
49631         switch(item.id){
49632             case "asc":
49633                 ds.sort(cm.getDataIndex(index), "ASC");
49634                 break;
49635             case "desc":
49636                 ds.sort(cm.getDataIndex(index), "DESC");
49637                 break;
49638             case "lock":
49639                 var lc = cm.getLockedCount();
49640                 if(cm.getColumnCount(true) <= lc+1){
49641                     this.onDenyColumnLock();
49642                     return;
49643                 }
49644                 if(lc != index){
49645                     cm.setLocked(index, true, true);
49646                     cm.moveColumn(index, lc);
49647                     this.grid.fireEvent("columnmove", index, lc);
49648                 }else{
49649                     cm.setLocked(index, true);
49650                 }
49651             break;
49652             case "unlock":
49653                 var lc = cm.getLockedCount();
49654                 if((lc-1) != index){
49655                     cm.setLocked(index, false, true);
49656                     cm.moveColumn(index, lc-1);
49657                     this.grid.fireEvent("columnmove", index, lc-1);
49658                 }else{
49659                     cm.setLocked(index, false);
49660                 }
49661             break;
49662             default:
49663                 index = cm.getIndexById(item.id.substr(4));
49664                 if(index != -1){
49665                     if(item.checked && cm.getColumnCount(true) <= 1){
49666                         this.onDenyColumnHide();
49667                         return false;
49668                     }
49669                     cm.setHidden(index, item.checked);
49670                 }
49671         }
49672         return true;
49673     },
49674
49675     beforeColMenuShow : function(){
49676         var cm = this.cm,  colCount = cm.getColumnCount();
49677         this.colMenu.removeAll();
49678         for(var i = 0; i < colCount; i++){
49679             this.colMenu.add(new Roo.menu.CheckItem({
49680                 id: "col-"+cm.getColumnId(i),
49681                 text: cm.getColumnHeader(i),
49682                 checked: !cm.isHidden(i),
49683                 hideOnClick:false
49684             }));
49685         }
49686     },
49687
49688     handleHdCtx : function(g, index, e){
49689         e.stopEvent();
49690         var hd = this.getHeaderCell(index);
49691         this.hdCtxIndex = index;
49692         var ms = this.hmenu.items, cm = this.cm;
49693         ms.get("asc").setDisabled(!cm.isSortable(index));
49694         ms.get("desc").setDisabled(!cm.isSortable(index));
49695         if(this.grid.enableColLock !== false){
49696             ms.get("lock").setDisabled(cm.isLocked(index));
49697             ms.get("unlock").setDisabled(!cm.isLocked(index));
49698         }
49699         this.hmenu.show(hd, "tl-bl");
49700     },
49701
49702     handleHdOver : function(e){
49703         var hd = this.findHeaderCell(e.getTarget());
49704         if(hd && !this.headersDisabled){
49705             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49706                this.fly(hd).addClass("x-grid-hd-over");
49707             }
49708         }
49709     },
49710
49711     handleHdOut : function(e){
49712         var hd = this.findHeaderCell(e.getTarget());
49713         if(hd){
49714             this.fly(hd).removeClass("x-grid-hd-over");
49715         }
49716     },
49717
49718     handleSplitDblClick : function(e, t){
49719         var i = this.getCellIndex(t);
49720         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49721             this.autoSizeColumn(i, true);
49722             this.layout();
49723         }
49724     },
49725
49726     render : function(){
49727
49728         var cm = this.cm;
49729         var colCount = cm.getColumnCount();
49730
49731         if(this.grid.monitorWindowResize === true){
49732             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49733         }
49734         var header = this.renderHeaders();
49735         var body = this.templates.body.apply({rows:""});
49736         var html = this.templates.master.apply({
49737             lockedBody: body,
49738             body: body,
49739             lockedHeader: header[0],
49740             header: header[1]
49741         });
49742
49743         //this.updateColumns();
49744
49745         this.grid.getGridEl().dom.innerHTML = html;
49746
49747         this.initElements();
49748         
49749         // a kludge to fix the random scolling effect in webkit
49750         this.el.on("scroll", function() {
49751             this.el.dom.scrollTop=0; // hopefully not recursive..
49752         },this);
49753
49754         this.scroller.on("scroll", this.handleScroll, this);
49755         this.lockedBody.on("mousewheel", this.handleWheel, this);
49756         this.mainBody.on("mousewheel", this.handleWheel, this);
49757
49758         this.mainHd.on("mouseover", this.handleHdOver, this);
49759         this.mainHd.on("mouseout", this.handleHdOut, this);
49760         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49761                 {delegate: "."+this.splitClass});
49762
49763         this.lockedHd.on("mouseover", this.handleHdOver, this);
49764         this.lockedHd.on("mouseout", this.handleHdOut, this);
49765         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49766                 {delegate: "."+this.splitClass});
49767
49768         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49769             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49770         }
49771
49772         this.updateSplitters();
49773
49774         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49775             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49776             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49777         }
49778
49779         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49780             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49781             this.hmenu.add(
49782                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49783                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49784             );
49785             if(this.grid.enableColLock !== false){
49786                 this.hmenu.add('-',
49787                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49788                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49789                 );
49790             }
49791             if(this.grid.enableColumnHide !== false){
49792
49793                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49794                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49795                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49796
49797                 this.hmenu.add('-',
49798                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49799                 );
49800             }
49801             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49802
49803             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49804         }
49805
49806         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49807             this.dd = new Roo.grid.GridDragZone(this.grid, {
49808                 ddGroup : this.grid.ddGroup || 'GridDD'
49809             });
49810         }
49811
49812         /*
49813         for(var i = 0; i < colCount; i++){
49814             if(cm.isHidden(i)){
49815                 this.hideColumn(i);
49816             }
49817             if(cm.config[i].align){
49818                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49819                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49820             }
49821         }*/
49822         
49823         this.updateHeaderSortState();
49824
49825         this.beforeInitialResize();
49826         this.layout(true);
49827
49828         // two part rendering gives faster view to the user
49829         this.renderPhase2.defer(1, this);
49830     },
49831
49832     renderPhase2 : function(){
49833         // render the rows now
49834         this.refresh();
49835         if(this.grid.autoSizeColumns){
49836             this.autoSizeColumns();
49837         }
49838     },
49839
49840     beforeInitialResize : function(){
49841
49842     },
49843
49844     onColumnSplitterMoved : function(i, w){
49845         this.userResized = true;
49846         var cm = this.grid.colModel;
49847         cm.setColumnWidth(i, w, true);
49848         var cid = cm.getColumnId(i);
49849         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49850         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49851         this.updateSplitters();
49852         this.layout();
49853         this.grid.fireEvent("columnresize", i, w);
49854     },
49855
49856     syncRowHeights : function(startIndex, endIndex){
49857         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49858             startIndex = startIndex || 0;
49859             var mrows = this.getBodyTable().rows;
49860             var lrows = this.getLockedTable().rows;
49861             var len = mrows.length-1;
49862             endIndex = Math.min(endIndex || len, len);
49863             for(var i = startIndex; i <= endIndex; i++){
49864                 var m = mrows[i], l = lrows[i];
49865                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49866                 m.style.height = l.style.height = h + "px";
49867             }
49868         }
49869     },
49870
49871     layout : function(initialRender, is2ndPass){
49872         var g = this.grid;
49873         var auto = g.autoHeight;
49874         var scrollOffset = 16;
49875         var c = g.getGridEl(), cm = this.cm,
49876                 expandCol = g.autoExpandColumn,
49877                 gv = this;
49878         //c.beginMeasure();
49879
49880         if(!c.dom.offsetWidth){ // display:none?
49881             if(initialRender){
49882                 this.lockedWrap.show();
49883                 this.mainWrap.show();
49884             }
49885             return;
49886         }
49887
49888         var hasLock = this.cm.isLocked(0);
49889
49890         var tbh = this.headerPanel.getHeight();
49891         var bbh = this.footerPanel.getHeight();
49892
49893         if(auto){
49894             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49895             var newHeight = ch + c.getBorderWidth("tb");
49896             if(g.maxHeight){
49897                 newHeight = Math.min(g.maxHeight, newHeight);
49898             }
49899             c.setHeight(newHeight);
49900         }
49901
49902         if(g.autoWidth){
49903             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49904         }
49905
49906         var s = this.scroller;
49907
49908         var csize = c.getSize(true);
49909
49910         this.el.setSize(csize.width, csize.height);
49911
49912         this.headerPanel.setWidth(csize.width);
49913         this.footerPanel.setWidth(csize.width);
49914
49915         var hdHeight = this.mainHd.getHeight();
49916         var vw = csize.width;
49917         var vh = csize.height - (tbh + bbh);
49918
49919         s.setSize(vw, vh);
49920
49921         var bt = this.getBodyTable();
49922         var ltWidth = hasLock ?
49923                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49924
49925         var scrollHeight = bt.offsetHeight;
49926         var scrollWidth = ltWidth + bt.offsetWidth;
49927         var vscroll = false, hscroll = false;
49928
49929         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49930
49931         var lw = this.lockedWrap, mw = this.mainWrap;
49932         var lb = this.lockedBody, mb = this.mainBody;
49933
49934         setTimeout(function(){
49935             var t = s.dom.offsetTop;
49936             var w = s.dom.clientWidth,
49937                 h = s.dom.clientHeight;
49938
49939             lw.setTop(t);
49940             lw.setSize(ltWidth, h);
49941
49942             mw.setLeftTop(ltWidth, t);
49943             mw.setSize(w-ltWidth, h);
49944
49945             lb.setHeight(h-hdHeight);
49946             mb.setHeight(h-hdHeight);
49947
49948             if(is2ndPass !== true && !gv.userResized && expandCol){
49949                 // high speed resize without full column calculation
49950                 
49951                 var ci = cm.getIndexById(expandCol);
49952                 if (ci < 0) {
49953                     ci = cm.findColumnIndex(expandCol);
49954                 }
49955                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49956                 var expandId = cm.getColumnId(ci);
49957                 var  tw = cm.getTotalWidth(false);
49958                 var currentWidth = cm.getColumnWidth(ci);
49959                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49960                 if(currentWidth != cw){
49961                     cm.setColumnWidth(ci, cw, true);
49962                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49963                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49964                     gv.updateSplitters();
49965                     gv.layout(false, true);
49966                 }
49967             }
49968
49969             if(initialRender){
49970                 lw.show();
49971                 mw.show();
49972             }
49973             //c.endMeasure();
49974         }, 10);
49975     },
49976
49977     onWindowResize : function(){
49978         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49979             return;
49980         }
49981         this.layout();
49982     },
49983
49984     appendFooter : function(parentEl){
49985         return null;
49986     },
49987
49988     sortAscText : "Sort Ascending",
49989     sortDescText : "Sort Descending",
49990     lockText : "Lock Column",
49991     unlockText : "Unlock Column",
49992     columnsText : "Columns"
49993 });
49994
49995
49996 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49997     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49998     this.proxy.el.addClass('x-grid3-col-dd');
49999 };
50000
50001 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50002     handleMouseDown : function(e){
50003
50004     },
50005
50006     callHandleMouseDown : function(e){
50007         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50008     }
50009 });
50010 /*
50011  * Based on:
50012  * Ext JS Library 1.1.1
50013  * Copyright(c) 2006-2007, Ext JS, LLC.
50014  *
50015  * Originally Released Under LGPL - original licence link has changed is not relivant.
50016  *
50017  * Fork - LGPL
50018  * <script type="text/javascript">
50019  */
50020  
50021 // private
50022 // This is a support class used internally by the Grid components
50023 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50024     this.grid = grid;
50025     this.view = grid.getView();
50026     this.proxy = this.view.resizeProxy;
50027     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50028         "gridSplitters" + this.grid.getGridEl().id, {
50029         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50030     });
50031     this.setHandleElId(Roo.id(hd));
50032     this.setOuterHandleElId(Roo.id(hd2));
50033     this.scroll = false;
50034 };
50035 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50036     fly: Roo.Element.fly,
50037
50038     b4StartDrag : function(x, y){
50039         this.view.headersDisabled = true;
50040         this.proxy.setHeight(this.view.mainWrap.getHeight());
50041         var w = this.cm.getColumnWidth(this.cellIndex);
50042         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50043         this.resetConstraints();
50044         this.setXConstraint(minw, 1000);
50045         this.setYConstraint(0, 0);
50046         this.minX = x - minw;
50047         this.maxX = x + 1000;
50048         this.startPos = x;
50049         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50050     },
50051
50052
50053     handleMouseDown : function(e){
50054         ev = Roo.EventObject.setEvent(e);
50055         var t = this.fly(ev.getTarget());
50056         if(t.hasClass("x-grid-split")){
50057             this.cellIndex = this.view.getCellIndex(t.dom);
50058             this.split = t.dom;
50059             this.cm = this.grid.colModel;
50060             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50061                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50062             }
50063         }
50064     },
50065
50066     endDrag : function(e){
50067         this.view.headersDisabled = false;
50068         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50069         var diff = endX - this.startPos;
50070         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50071     },
50072
50073     autoOffset : function(){
50074         this.setDelta(0,0);
50075     }
50076 });/*
50077  * Based on:
50078  * Ext JS Library 1.1.1
50079  * Copyright(c) 2006-2007, Ext JS, LLC.
50080  *
50081  * Originally Released Under LGPL - original licence link has changed is not relivant.
50082  *
50083  * Fork - LGPL
50084  * <script type="text/javascript">
50085  */
50086  
50087 // private
50088 // This is a support class used internally by the Grid components
50089 Roo.grid.GridDragZone = function(grid, config){
50090     this.view = grid.getView();
50091     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50092     if(this.view.lockedBody){
50093         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50094         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50095     }
50096     this.scroll = false;
50097     this.grid = grid;
50098     this.ddel = document.createElement('div');
50099     this.ddel.className = 'x-grid-dd-wrap';
50100 };
50101
50102 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50103     ddGroup : "GridDD",
50104
50105     getDragData : function(e){
50106         var t = Roo.lib.Event.getTarget(e);
50107         var rowIndex = this.view.findRowIndex(t);
50108         if(rowIndex !== false){
50109             var sm = this.grid.selModel;
50110             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50111               //  sm.mouseDown(e, t);
50112             //}
50113             if (e.hasModifier()){
50114                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50115             }
50116             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50117         }
50118         return false;
50119     },
50120
50121     onInitDrag : function(e){
50122         var data = this.dragData;
50123         this.ddel.innerHTML = this.grid.getDragDropText();
50124         this.proxy.update(this.ddel);
50125         // fire start drag?
50126     },
50127
50128     afterRepair : function(){
50129         this.dragging = false;
50130     },
50131
50132     getRepairXY : function(e, data){
50133         return false;
50134     },
50135
50136     onEndDrag : function(data, e){
50137         // fire end drag?
50138     },
50139
50140     onValidDrop : function(dd, e, id){
50141         // fire drag drop?
50142         this.hideProxy();
50143     },
50144
50145     beforeInvalidDrop : function(e, id){
50146
50147     }
50148 });/*
50149  * Based on:
50150  * Ext JS Library 1.1.1
50151  * Copyright(c) 2006-2007, Ext JS, LLC.
50152  *
50153  * Originally Released Under LGPL - original licence link has changed is not relivant.
50154  *
50155  * Fork - LGPL
50156  * <script type="text/javascript">
50157  */
50158  
50159
50160 /**
50161  * @class Roo.grid.ColumnModel
50162  * @extends Roo.util.Observable
50163  * This is the default implementation of a ColumnModel used by the Grid. It defines
50164  * the columns in the grid.
50165  * <br>Usage:<br>
50166  <pre><code>
50167  var colModel = new Roo.grid.ColumnModel([
50168         {header: "Ticker", width: 60, sortable: true, locked: true},
50169         {header: "Company Name", width: 150, sortable: true},
50170         {header: "Market Cap.", width: 100, sortable: true},
50171         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50172         {header: "Employees", width: 100, sortable: true, resizable: false}
50173  ]);
50174  </code></pre>
50175  * <p>
50176  
50177  * The config options listed for this class are options which may appear in each
50178  * individual column definition.
50179  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50180  * @constructor
50181  * @param {Object} config An Array of column config objects. See this class's
50182  * config objects for details.
50183 */
50184 Roo.grid.ColumnModel = function(config){
50185         /**
50186      * The config passed into the constructor
50187      */
50188     this.config = config;
50189     this.lookup = {};
50190
50191     // if no id, create one
50192     // if the column does not have a dataIndex mapping,
50193     // map it to the order it is in the config
50194     for(var i = 0, len = config.length; i < len; i++){
50195         var c = config[i];
50196         if(typeof c.dataIndex == "undefined"){
50197             c.dataIndex = i;
50198         }
50199         if(typeof c.renderer == "string"){
50200             c.renderer = Roo.util.Format[c.renderer];
50201         }
50202         if(typeof c.id == "undefined"){
50203             c.id = Roo.id();
50204         }
50205         if(c.editor && c.editor.xtype){
50206             c.editor  = Roo.factory(c.editor, Roo.grid);
50207         }
50208         if(c.editor && c.editor.isFormField){
50209             c.editor = new Roo.grid.GridEditor(c.editor);
50210         }
50211         this.lookup[c.id] = c;
50212     }
50213
50214     /**
50215      * The width of columns which have no width specified (defaults to 100)
50216      * @type Number
50217      */
50218     this.defaultWidth = 100;
50219
50220     /**
50221      * Default sortable of columns which have no sortable specified (defaults to false)
50222      * @type Boolean
50223      */
50224     this.defaultSortable = false;
50225
50226     this.addEvents({
50227         /**
50228              * @event widthchange
50229              * Fires when the width of a column changes.
50230              * @param {ColumnModel} this
50231              * @param {Number} columnIndex The column index
50232              * @param {Number} newWidth The new width
50233              */
50234             "widthchange": true,
50235         /**
50236              * @event headerchange
50237              * Fires when the text of a header changes.
50238              * @param {ColumnModel} this
50239              * @param {Number} columnIndex The column index
50240              * @param {Number} newText The new header text
50241              */
50242             "headerchange": true,
50243         /**
50244              * @event hiddenchange
50245              * Fires when a column is hidden or "unhidden".
50246              * @param {ColumnModel} this
50247              * @param {Number} columnIndex The column index
50248              * @param {Boolean} hidden true if hidden, false otherwise
50249              */
50250             "hiddenchange": true,
50251             /**
50252          * @event columnmoved
50253          * Fires when a column is moved.
50254          * @param {ColumnModel} this
50255          * @param {Number} oldIndex
50256          * @param {Number} newIndex
50257          */
50258         "columnmoved" : true,
50259         /**
50260          * @event columlockchange
50261          * Fires when a column's locked state is changed
50262          * @param {ColumnModel} this
50263          * @param {Number} colIndex
50264          * @param {Boolean} locked true if locked
50265          */
50266         "columnlockchange" : true
50267     });
50268     Roo.grid.ColumnModel.superclass.constructor.call(this);
50269 };
50270 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50271     /**
50272      * @cfg {String} header The header text to display in the Grid view.
50273      */
50274     /**
50275      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50276      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50277      * specified, the column's index is used as an index into the Record's data Array.
50278      */
50279     /**
50280      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50281      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50282      */
50283     /**
50284      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50285      * Defaults to the value of the {@link #defaultSortable} property.
50286      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50287      */
50288     /**
50289      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50290      */
50291     /**
50292      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50293      */
50294     /**
50295      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50296      */
50297     /**
50298      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50299      */
50300     /**
50301      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50302      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50303      * default renderer uses the raw data value.
50304      */
50305        /**
50306      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50307      */
50308     /**
50309      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50310      */
50311
50312     /**
50313      * Returns the id of the column at the specified index.
50314      * @param {Number} index The column index
50315      * @return {String} the id
50316      */
50317     getColumnId : function(index){
50318         return this.config[index].id;
50319     },
50320
50321     /**
50322      * Returns the column for a specified id.
50323      * @param {String} id The column id
50324      * @return {Object} the column
50325      */
50326     getColumnById : function(id){
50327         return this.lookup[id];
50328     },
50329
50330     
50331     /**
50332      * Returns the column for a specified dataIndex.
50333      * @param {String} dataIndex The column dataIndex
50334      * @return {Object|Boolean} the column or false if not found
50335      */
50336     getColumnByDataIndex: function(dataIndex){
50337         var index = this.findColumnIndex(dataIndex);
50338         return index > -1 ? this.config[index] : false;
50339     },
50340     
50341     /**
50342      * Returns the index for a specified column id.
50343      * @param {String} id The column id
50344      * @return {Number} the index, or -1 if not found
50345      */
50346     getIndexById : function(id){
50347         for(var i = 0, len = this.config.length; i < len; i++){
50348             if(this.config[i].id == id){
50349                 return i;
50350             }
50351         }
50352         return -1;
50353     },
50354     
50355     /**
50356      * Returns the index for a specified column dataIndex.
50357      * @param {String} dataIndex The column dataIndex
50358      * @return {Number} the index, or -1 if not found
50359      */
50360     
50361     findColumnIndex : function(dataIndex){
50362         for(var i = 0, len = this.config.length; i < len; i++){
50363             if(this.config[i].dataIndex == dataIndex){
50364                 return i;
50365             }
50366         }
50367         return -1;
50368     },
50369     
50370     
50371     moveColumn : function(oldIndex, newIndex){
50372         var c = this.config[oldIndex];
50373         this.config.splice(oldIndex, 1);
50374         this.config.splice(newIndex, 0, c);
50375         this.dataMap = null;
50376         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50377     },
50378
50379     isLocked : function(colIndex){
50380         return this.config[colIndex].locked === true;
50381     },
50382
50383     setLocked : function(colIndex, value, suppressEvent){
50384         if(this.isLocked(colIndex) == value){
50385             return;
50386         }
50387         this.config[colIndex].locked = value;
50388         if(!suppressEvent){
50389             this.fireEvent("columnlockchange", this, colIndex, value);
50390         }
50391     },
50392
50393     getTotalLockedWidth : function(){
50394         var totalWidth = 0;
50395         for(var i = 0; i < this.config.length; i++){
50396             if(this.isLocked(i) && !this.isHidden(i)){
50397                 this.totalWidth += this.getColumnWidth(i);
50398             }
50399         }
50400         return totalWidth;
50401     },
50402
50403     getLockedCount : function(){
50404         for(var i = 0, len = this.config.length; i < len; i++){
50405             if(!this.isLocked(i)){
50406                 return i;
50407             }
50408         }
50409     },
50410
50411     /**
50412      * Returns the number of columns.
50413      * @return {Number}
50414      */
50415     getColumnCount : function(visibleOnly){
50416         if(visibleOnly === true){
50417             var c = 0;
50418             for(var i = 0, len = this.config.length; i < len; i++){
50419                 if(!this.isHidden(i)){
50420                     c++;
50421                 }
50422             }
50423             return c;
50424         }
50425         return this.config.length;
50426     },
50427
50428     /**
50429      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50430      * @param {Function} fn
50431      * @param {Object} scope (optional)
50432      * @return {Array} result
50433      */
50434     getColumnsBy : function(fn, scope){
50435         var r = [];
50436         for(var i = 0, len = this.config.length; i < len; i++){
50437             var c = this.config[i];
50438             if(fn.call(scope||this, c, i) === true){
50439                 r[r.length] = c;
50440             }
50441         }
50442         return r;
50443     },
50444
50445     /**
50446      * Returns true if the specified column is sortable.
50447      * @param {Number} col The column index
50448      * @return {Boolean}
50449      */
50450     isSortable : function(col){
50451         if(typeof this.config[col].sortable == "undefined"){
50452             return this.defaultSortable;
50453         }
50454         return this.config[col].sortable;
50455     },
50456
50457     /**
50458      * Returns the rendering (formatting) function defined for the column.
50459      * @param {Number} col The column index.
50460      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50461      */
50462     getRenderer : function(col){
50463         if(!this.config[col].renderer){
50464             return Roo.grid.ColumnModel.defaultRenderer;
50465         }
50466         return this.config[col].renderer;
50467     },
50468
50469     /**
50470      * Sets the rendering (formatting) function for a column.
50471      * @param {Number} col The column index
50472      * @param {Function} fn The function to use to process the cell's raw data
50473      * to return HTML markup for the grid view. The render function is called with
50474      * the following parameters:<ul>
50475      * <li>Data value.</li>
50476      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50477      * <li>css A CSS style string to apply to the table cell.</li>
50478      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50479      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50480      * <li>Row index</li>
50481      * <li>Column index</li>
50482      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50483      */
50484     setRenderer : function(col, fn){
50485         this.config[col].renderer = fn;
50486     },
50487
50488     /**
50489      * Returns the width for the specified column.
50490      * @param {Number} col The column index
50491      * @return {Number}
50492      */
50493     getColumnWidth : function(col){
50494         return this.config[col].width * 1 || this.defaultWidth;
50495     },
50496
50497     /**
50498      * Sets the width for a column.
50499      * @param {Number} col The column index
50500      * @param {Number} width The new width
50501      */
50502     setColumnWidth : function(col, width, suppressEvent){
50503         this.config[col].width = width;
50504         this.totalWidth = null;
50505         if(!suppressEvent){
50506              this.fireEvent("widthchange", this, col, width);
50507         }
50508     },
50509
50510     /**
50511      * Returns the total width of all columns.
50512      * @param {Boolean} includeHidden True to include hidden column widths
50513      * @return {Number}
50514      */
50515     getTotalWidth : function(includeHidden){
50516         if(!this.totalWidth){
50517             this.totalWidth = 0;
50518             for(var i = 0, len = this.config.length; i < len; i++){
50519                 if(includeHidden || !this.isHidden(i)){
50520                     this.totalWidth += this.getColumnWidth(i);
50521                 }
50522             }
50523         }
50524         return this.totalWidth;
50525     },
50526
50527     /**
50528      * Returns the header for the specified column.
50529      * @param {Number} col The column index
50530      * @return {String}
50531      */
50532     getColumnHeader : function(col){
50533         return this.config[col].header;
50534     },
50535
50536     /**
50537      * Sets the header for a column.
50538      * @param {Number} col The column index
50539      * @param {String} header The new header
50540      */
50541     setColumnHeader : function(col, header){
50542         this.config[col].header = header;
50543         this.fireEvent("headerchange", this, col, header);
50544     },
50545
50546     /**
50547      * Returns the tooltip for the specified column.
50548      * @param {Number} col The column index
50549      * @return {String}
50550      */
50551     getColumnTooltip : function(col){
50552             return this.config[col].tooltip;
50553     },
50554     /**
50555      * Sets the tooltip for a column.
50556      * @param {Number} col The column index
50557      * @param {String} tooltip The new tooltip
50558      */
50559     setColumnTooltip : function(col, tooltip){
50560             this.config[col].tooltip = tooltip;
50561     },
50562
50563     /**
50564      * Returns the dataIndex for the specified column.
50565      * @param {Number} col The column index
50566      * @return {Number}
50567      */
50568     getDataIndex : function(col){
50569         return this.config[col].dataIndex;
50570     },
50571
50572     /**
50573      * Sets the dataIndex for a column.
50574      * @param {Number} col The column index
50575      * @param {Number} dataIndex The new dataIndex
50576      */
50577     setDataIndex : function(col, dataIndex){
50578         this.config[col].dataIndex = dataIndex;
50579     },
50580
50581     
50582     
50583     /**
50584      * Returns true if the cell is editable.
50585      * @param {Number} colIndex The column index
50586      * @param {Number} rowIndex The row index
50587      * @return {Boolean}
50588      */
50589     isCellEditable : function(colIndex, rowIndex){
50590         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50591     },
50592
50593     /**
50594      * Returns the editor defined for the cell/column.
50595      * return false or null to disable editing.
50596      * @param {Number} colIndex The column index
50597      * @param {Number} rowIndex The row index
50598      * @return {Object}
50599      */
50600     getCellEditor : function(colIndex, rowIndex){
50601         return this.config[colIndex].editor;
50602     },
50603
50604     /**
50605      * Sets if a column is editable.
50606      * @param {Number} col The column index
50607      * @param {Boolean} editable True if the column is editable
50608      */
50609     setEditable : function(col, editable){
50610         this.config[col].editable = editable;
50611     },
50612
50613
50614     /**
50615      * Returns true if the column is hidden.
50616      * @param {Number} colIndex The column index
50617      * @return {Boolean}
50618      */
50619     isHidden : function(colIndex){
50620         return this.config[colIndex].hidden;
50621     },
50622
50623
50624     /**
50625      * Returns true if the column width cannot be changed
50626      */
50627     isFixed : function(colIndex){
50628         return this.config[colIndex].fixed;
50629     },
50630
50631     /**
50632      * Returns true if the column can be resized
50633      * @return {Boolean}
50634      */
50635     isResizable : function(colIndex){
50636         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50637     },
50638     /**
50639      * Sets if a column is hidden.
50640      * @param {Number} colIndex The column index
50641      * @param {Boolean} hidden True if the column is hidden
50642      */
50643     setHidden : function(colIndex, hidden){
50644         this.config[colIndex].hidden = hidden;
50645         this.totalWidth = null;
50646         this.fireEvent("hiddenchange", this, colIndex, hidden);
50647     },
50648
50649     /**
50650      * Sets the editor for a column.
50651      * @param {Number} col The column index
50652      * @param {Object} editor The editor object
50653      */
50654     setEditor : function(col, editor){
50655         this.config[col].editor = editor;
50656     }
50657 });
50658
50659 Roo.grid.ColumnModel.defaultRenderer = function(value){
50660         if(typeof value == "string" && value.length < 1){
50661             return "&#160;";
50662         }
50663         return value;
50664 };
50665
50666 // Alias for backwards compatibility
50667 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50668 /*
50669  * Based on:
50670  * Ext JS Library 1.1.1
50671  * Copyright(c) 2006-2007, Ext JS, LLC.
50672  *
50673  * Originally Released Under LGPL - original licence link has changed is not relivant.
50674  *
50675  * Fork - LGPL
50676  * <script type="text/javascript">
50677  */
50678
50679 /**
50680  * @class Roo.grid.AbstractSelectionModel
50681  * @extends Roo.util.Observable
50682  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50683  * implemented by descendant classes.  This class should not be directly instantiated.
50684  * @constructor
50685  */
50686 Roo.grid.AbstractSelectionModel = function(){
50687     this.locked = false;
50688     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50689 };
50690
50691 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50692     /** @ignore Called by the grid automatically. Do not call directly. */
50693     init : function(grid){
50694         this.grid = grid;
50695         this.initEvents();
50696     },
50697
50698     /**
50699      * Locks the selections.
50700      */
50701     lock : function(){
50702         this.locked = true;
50703     },
50704
50705     /**
50706      * Unlocks the selections.
50707      */
50708     unlock : function(){
50709         this.locked = false;
50710     },
50711
50712     /**
50713      * Returns true if the selections are locked.
50714      * @return {Boolean}
50715      */
50716     isLocked : function(){
50717         return this.locked;
50718     }
50719 });/*
50720  * Based on:
50721  * Ext JS Library 1.1.1
50722  * Copyright(c) 2006-2007, Ext JS, LLC.
50723  *
50724  * Originally Released Under LGPL - original licence link has changed is not relivant.
50725  *
50726  * Fork - LGPL
50727  * <script type="text/javascript">
50728  */
50729 /**
50730  * @extends Roo.grid.AbstractSelectionModel
50731  * @class Roo.grid.RowSelectionModel
50732  * The default SelectionModel used by {@link Roo.grid.Grid}.
50733  * It supports multiple selections and keyboard selection/navigation. 
50734  * @constructor
50735  * @param {Object} config
50736  */
50737 Roo.grid.RowSelectionModel = function(config){
50738     Roo.apply(this, config);
50739     this.selections = new Roo.util.MixedCollection(false, function(o){
50740         return o.id;
50741     });
50742
50743     this.last = false;
50744     this.lastActive = false;
50745
50746     this.addEvents({
50747         /**
50748              * @event selectionchange
50749              * Fires when the selection changes
50750              * @param {SelectionModel} this
50751              */
50752             "selectionchange" : true,
50753         /**
50754              * @event afterselectionchange
50755              * Fires after the selection changes (eg. by key press or clicking)
50756              * @param {SelectionModel} this
50757              */
50758             "afterselectionchange" : true,
50759         /**
50760              * @event beforerowselect
50761              * Fires when a row is selected being selected, return false to cancel.
50762              * @param {SelectionModel} this
50763              * @param {Number} rowIndex The selected index
50764              * @param {Boolean} keepExisting False if other selections will be cleared
50765              */
50766             "beforerowselect" : true,
50767         /**
50768              * @event rowselect
50769              * Fires when a row is selected.
50770              * @param {SelectionModel} this
50771              * @param {Number} rowIndex The selected index
50772              * @param {Roo.data.Record} r The record
50773              */
50774             "rowselect" : true,
50775         /**
50776              * @event rowdeselect
50777              * Fires when a row is deselected.
50778              * @param {SelectionModel} this
50779              * @param {Number} rowIndex The selected index
50780              */
50781         "rowdeselect" : true
50782     });
50783     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50784     this.locked = false;
50785 };
50786
50787 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50788     /**
50789      * @cfg {Boolean} singleSelect
50790      * True to allow selection of only one row at a time (defaults to false)
50791      */
50792     singleSelect : false,
50793
50794     // private
50795     initEvents : function(){
50796
50797         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50798             this.grid.on("mousedown", this.handleMouseDown, this);
50799         }else{ // allow click to work like normal
50800             this.grid.on("rowclick", this.handleDragableRowClick, this);
50801         }
50802
50803         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50804             "up" : function(e){
50805                 if(!e.shiftKey){
50806                     this.selectPrevious(e.shiftKey);
50807                 }else if(this.last !== false && this.lastActive !== false){
50808                     var last = this.last;
50809                     this.selectRange(this.last,  this.lastActive-1);
50810                     this.grid.getView().focusRow(this.lastActive);
50811                     if(last !== false){
50812                         this.last = last;
50813                     }
50814                 }else{
50815                     this.selectFirstRow();
50816                 }
50817                 this.fireEvent("afterselectionchange", this);
50818             },
50819             "down" : function(e){
50820                 if(!e.shiftKey){
50821                     this.selectNext(e.shiftKey);
50822                 }else if(this.last !== false && this.lastActive !== false){
50823                     var last = this.last;
50824                     this.selectRange(this.last,  this.lastActive+1);
50825                     this.grid.getView().focusRow(this.lastActive);
50826                     if(last !== false){
50827                         this.last = last;
50828                     }
50829                 }else{
50830                     this.selectFirstRow();
50831                 }
50832                 this.fireEvent("afterselectionchange", this);
50833             },
50834             scope: this
50835         });
50836
50837         var view = this.grid.view;
50838         view.on("refresh", this.onRefresh, this);
50839         view.on("rowupdated", this.onRowUpdated, this);
50840         view.on("rowremoved", this.onRemove, this);
50841     },
50842
50843     // private
50844     onRefresh : function(){
50845         var ds = this.grid.dataSource, i, v = this.grid.view;
50846         var s = this.selections;
50847         s.each(function(r){
50848             if((i = ds.indexOfId(r.id)) != -1){
50849                 v.onRowSelect(i);
50850             }else{
50851                 s.remove(r);
50852             }
50853         });
50854     },
50855
50856     // private
50857     onRemove : function(v, index, r){
50858         this.selections.remove(r);
50859     },
50860
50861     // private
50862     onRowUpdated : function(v, index, r){
50863         if(this.isSelected(r)){
50864             v.onRowSelect(index);
50865         }
50866     },
50867
50868     /**
50869      * Select records.
50870      * @param {Array} records The records to select
50871      * @param {Boolean} keepExisting (optional) True to keep existing selections
50872      */
50873     selectRecords : function(records, keepExisting){
50874         if(!keepExisting){
50875             this.clearSelections();
50876         }
50877         var ds = this.grid.dataSource;
50878         for(var i = 0, len = records.length; i < len; i++){
50879             this.selectRow(ds.indexOf(records[i]), true);
50880         }
50881     },
50882
50883     /**
50884      * Gets the number of selected rows.
50885      * @return {Number}
50886      */
50887     getCount : function(){
50888         return this.selections.length;
50889     },
50890
50891     /**
50892      * Selects the first row in the grid.
50893      */
50894     selectFirstRow : function(){
50895         this.selectRow(0);
50896     },
50897
50898     /**
50899      * Select the last row.
50900      * @param {Boolean} keepExisting (optional) True to keep existing selections
50901      */
50902     selectLastRow : function(keepExisting){
50903         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50904     },
50905
50906     /**
50907      * Selects the row immediately following the last selected row.
50908      * @param {Boolean} keepExisting (optional) True to keep existing selections
50909      */
50910     selectNext : function(keepExisting){
50911         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50912             this.selectRow(this.last+1, keepExisting);
50913             this.grid.getView().focusRow(this.last);
50914         }
50915     },
50916
50917     /**
50918      * Selects the row that precedes the last selected row.
50919      * @param {Boolean} keepExisting (optional) True to keep existing selections
50920      */
50921     selectPrevious : function(keepExisting){
50922         if(this.last){
50923             this.selectRow(this.last-1, keepExisting);
50924             this.grid.getView().focusRow(this.last);
50925         }
50926     },
50927
50928     /**
50929      * Returns the selected records
50930      * @return {Array} Array of selected records
50931      */
50932     getSelections : function(){
50933         return [].concat(this.selections.items);
50934     },
50935
50936     /**
50937      * Returns the first selected record.
50938      * @return {Record}
50939      */
50940     getSelected : function(){
50941         return this.selections.itemAt(0);
50942     },
50943
50944
50945     /**
50946      * Clears all selections.
50947      */
50948     clearSelections : function(fast){
50949         if(this.locked) return;
50950         if(fast !== true){
50951             var ds = this.grid.dataSource;
50952             var s = this.selections;
50953             s.each(function(r){
50954                 this.deselectRow(ds.indexOfId(r.id));
50955             }, this);
50956             s.clear();
50957         }else{
50958             this.selections.clear();
50959         }
50960         this.last = false;
50961     },
50962
50963
50964     /**
50965      * Selects all rows.
50966      */
50967     selectAll : function(){
50968         if(this.locked) return;
50969         this.selections.clear();
50970         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50971             this.selectRow(i, true);
50972         }
50973     },
50974
50975     /**
50976      * Returns True if there is a selection.
50977      * @return {Boolean}
50978      */
50979     hasSelection : function(){
50980         return this.selections.length > 0;
50981     },
50982
50983     /**
50984      * Returns True if the specified row is selected.
50985      * @param {Number/Record} record The record or index of the record to check
50986      * @return {Boolean}
50987      */
50988     isSelected : function(index){
50989         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50990         return (r && this.selections.key(r.id) ? true : false);
50991     },
50992
50993     /**
50994      * Returns True if the specified record id is selected.
50995      * @param {String} id The id of record to check
50996      * @return {Boolean}
50997      */
50998     isIdSelected : function(id){
50999         return (this.selections.key(id) ? true : false);
51000     },
51001
51002     // private
51003     handleMouseDown : function(e, t){
51004         var view = this.grid.getView(), rowIndex;
51005         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51006             return;
51007         };
51008         if(e.shiftKey && this.last !== false){
51009             var last = this.last;
51010             this.selectRange(last, rowIndex, e.ctrlKey);
51011             this.last = last; // reset the last
51012             view.focusRow(rowIndex);
51013         }else{
51014             var isSelected = this.isSelected(rowIndex);
51015             if(e.button !== 0 && isSelected){
51016                 view.focusRow(rowIndex);
51017             }else if(e.ctrlKey && isSelected){
51018                 this.deselectRow(rowIndex);
51019             }else if(!isSelected){
51020                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51021                 view.focusRow(rowIndex);
51022             }
51023         }
51024         this.fireEvent("afterselectionchange", this);
51025     },
51026     // private
51027     handleDragableRowClick :  function(grid, rowIndex, e) 
51028     {
51029         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51030             this.selectRow(rowIndex, false);
51031             grid.view.focusRow(rowIndex);
51032              this.fireEvent("afterselectionchange", this);
51033         }
51034     },
51035     
51036     /**
51037      * Selects multiple rows.
51038      * @param {Array} rows Array of the indexes of the row to select
51039      * @param {Boolean} keepExisting (optional) True to keep existing selections
51040      */
51041     selectRows : function(rows, keepExisting){
51042         if(!keepExisting){
51043             this.clearSelections();
51044         }
51045         for(var i = 0, len = rows.length; i < len; i++){
51046             this.selectRow(rows[i], true);
51047         }
51048     },
51049
51050     /**
51051      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51052      * @param {Number} startRow The index of the first row in the range
51053      * @param {Number} endRow The index of the last row in the range
51054      * @param {Boolean} keepExisting (optional) True to retain existing selections
51055      */
51056     selectRange : function(startRow, endRow, keepExisting){
51057         if(this.locked) return;
51058         if(!keepExisting){
51059             this.clearSelections();
51060         }
51061         if(startRow <= endRow){
51062             for(var i = startRow; i <= endRow; i++){
51063                 this.selectRow(i, true);
51064             }
51065         }else{
51066             for(var i = startRow; i >= endRow; i--){
51067                 this.selectRow(i, true);
51068             }
51069         }
51070     },
51071
51072     /**
51073      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51074      * @param {Number} startRow The index of the first row in the range
51075      * @param {Number} endRow The index of the last row in the range
51076      */
51077     deselectRange : function(startRow, endRow, preventViewNotify){
51078         if(this.locked) return;
51079         for(var i = startRow; i <= endRow; i++){
51080             this.deselectRow(i, preventViewNotify);
51081         }
51082     },
51083
51084     /**
51085      * Selects a row.
51086      * @param {Number} row The index of the row to select
51087      * @param {Boolean} keepExisting (optional) True to keep existing selections
51088      */
51089     selectRow : function(index, keepExisting, preventViewNotify){
51090         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51091         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51092             if(!keepExisting || this.singleSelect){
51093                 this.clearSelections();
51094             }
51095             var r = this.grid.dataSource.getAt(index);
51096             this.selections.add(r);
51097             this.last = this.lastActive = index;
51098             if(!preventViewNotify){
51099                 this.grid.getView().onRowSelect(index);
51100             }
51101             this.fireEvent("rowselect", this, index, r);
51102             this.fireEvent("selectionchange", this);
51103         }
51104     },
51105
51106     /**
51107      * Deselects a row.
51108      * @param {Number} row The index of the row to deselect
51109      */
51110     deselectRow : function(index, preventViewNotify){
51111         if(this.locked) return;
51112         if(this.last == index){
51113             this.last = false;
51114         }
51115         if(this.lastActive == index){
51116             this.lastActive = false;
51117         }
51118         var r = this.grid.dataSource.getAt(index);
51119         this.selections.remove(r);
51120         if(!preventViewNotify){
51121             this.grid.getView().onRowDeselect(index);
51122         }
51123         this.fireEvent("rowdeselect", this, index);
51124         this.fireEvent("selectionchange", this);
51125     },
51126
51127     // private
51128     restoreLast : function(){
51129         if(this._last){
51130             this.last = this._last;
51131         }
51132     },
51133
51134     // private
51135     acceptsNav : function(row, col, cm){
51136         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51137     },
51138
51139     // private
51140     onEditorKey : function(field, e){
51141         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51142         if(k == e.TAB){
51143             e.stopEvent();
51144             ed.completeEdit();
51145             if(e.shiftKey){
51146                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51147             }else{
51148                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51149             }
51150         }else if(k == e.ENTER && !e.ctrlKey){
51151             e.stopEvent();
51152             ed.completeEdit();
51153             if(e.shiftKey){
51154                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51155             }else{
51156                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51157             }
51158         }else if(k == e.ESC){
51159             ed.cancelEdit();
51160         }
51161         if(newCell){
51162             g.startEditing(newCell[0], newCell[1]);
51163         }
51164     }
51165 });/*
51166  * Based on:
51167  * Ext JS Library 1.1.1
51168  * Copyright(c) 2006-2007, Ext JS, LLC.
51169  *
51170  * Originally Released Under LGPL - original licence link has changed is not relivant.
51171  *
51172  * Fork - LGPL
51173  * <script type="text/javascript">
51174  */
51175 /**
51176  * @class Roo.grid.CellSelectionModel
51177  * @extends Roo.grid.AbstractSelectionModel
51178  * This class provides the basic implementation for cell selection in a grid.
51179  * @constructor
51180  * @param {Object} config The object containing the configuration of this model.
51181  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51182  */
51183 Roo.grid.CellSelectionModel = function(config){
51184     Roo.apply(this, config);
51185
51186     this.selection = null;
51187
51188     this.addEvents({
51189         /**
51190              * @event beforerowselect
51191              * Fires before a cell is selected.
51192              * @param {SelectionModel} this
51193              * @param {Number} rowIndex The selected row index
51194              * @param {Number} colIndex The selected cell index
51195              */
51196             "beforecellselect" : true,
51197         /**
51198              * @event cellselect
51199              * Fires when a cell is selected.
51200              * @param {SelectionModel} this
51201              * @param {Number} rowIndex The selected row index
51202              * @param {Number} colIndex The selected cell index
51203              */
51204             "cellselect" : true,
51205         /**
51206              * @event selectionchange
51207              * Fires when the active selection changes.
51208              * @param {SelectionModel} this
51209              * @param {Object} selection null for no selection or an object (o) with two properties
51210                 <ul>
51211                 <li>o.record: the record object for the row the selection is in</li>
51212                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51213                 </ul>
51214              */
51215             "selectionchange" : true,
51216         /**
51217              * @event tabend
51218              * Fires when the tab (or enter) was pressed on the last editable cell
51219              * You can use this to trigger add new row.
51220              * @param {SelectionModel} this
51221              */
51222             "tabend" : true
51223     });
51224     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51225 };
51226
51227 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51228     
51229     enter_is_tab: false,
51230
51231     /** @ignore */
51232     initEvents : function(){
51233         this.grid.on("mousedown", this.handleMouseDown, this);
51234         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51235         var view = this.grid.view;
51236         view.on("refresh", this.onViewChange, this);
51237         view.on("rowupdated", this.onRowUpdated, this);
51238         view.on("beforerowremoved", this.clearSelections, this);
51239         view.on("beforerowsinserted", this.clearSelections, this);
51240         if(this.grid.isEditor){
51241             this.grid.on("beforeedit", this.beforeEdit,  this);
51242         }
51243     },
51244
51245         //private
51246     beforeEdit : function(e){
51247         this.select(e.row, e.column, false, true, e.record);
51248     },
51249
51250         //private
51251     onRowUpdated : function(v, index, r){
51252         if(this.selection && this.selection.record == r){
51253             v.onCellSelect(index, this.selection.cell[1]);
51254         }
51255     },
51256
51257         //private
51258     onViewChange : function(){
51259         this.clearSelections(true);
51260     },
51261
51262         /**
51263          * Returns the currently selected cell,.
51264          * @return {Array} The selected cell (row, column) or null if none selected.
51265          */
51266     getSelectedCell : function(){
51267         return this.selection ? this.selection.cell : null;
51268     },
51269
51270     /**
51271      * Clears all selections.
51272      * @param {Boolean} true to prevent the gridview from being notified about the change.
51273      */
51274     clearSelections : function(preventNotify){
51275         var s = this.selection;
51276         if(s){
51277             if(preventNotify !== true){
51278                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51279             }
51280             this.selection = null;
51281             this.fireEvent("selectionchange", this, null);
51282         }
51283     },
51284
51285     /**
51286      * Returns true if there is a selection.
51287      * @return {Boolean}
51288      */
51289     hasSelection : function(){
51290         return this.selection ? true : false;
51291     },
51292
51293     /** @ignore */
51294     handleMouseDown : function(e, t){
51295         var v = this.grid.getView();
51296         if(this.isLocked()){
51297             return;
51298         };
51299         var row = v.findRowIndex(t);
51300         var cell = v.findCellIndex(t);
51301         if(row !== false && cell !== false){
51302             this.select(row, cell);
51303         }
51304     },
51305
51306     /**
51307      * Selects a cell.
51308      * @param {Number} rowIndex
51309      * @param {Number} collIndex
51310      */
51311     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51312         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51313             this.clearSelections();
51314             r = r || this.grid.dataSource.getAt(rowIndex);
51315             this.selection = {
51316                 record : r,
51317                 cell : [rowIndex, colIndex]
51318             };
51319             if(!preventViewNotify){
51320                 var v = this.grid.getView();
51321                 v.onCellSelect(rowIndex, colIndex);
51322                 if(preventFocus !== true){
51323                     v.focusCell(rowIndex, colIndex);
51324                 }
51325             }
51326             this.fireEvent("cellselect", this, rowIndex, colIndex);
51327             this.fireEvent("selectionchange", this, this.selection);
51328         }
51329     },
51330
51331         //private
51332     isSelectable : function(rowIndex, colIndex, cm){
51333         return !cm.isHidden(colIndex);
51334     },
51335
51336     /** @ignore */
51337     handleKeyDown : function(e){
51338         //Roo.log('Cell Sel Model handleKeyDown');
51339         if(!e.isNavKeyPress()){
51340             return;
51341         }
51342         var g = this.grid, s = this.selection;
51343         if(!s){
51344             e.stopEvent();
51345             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51346             if(cell){
51347                 this.select(cell[0], cell[1]);
51348             }
51349             return;
51350         }
51351         var sm = this;
51352         var walk = function(row, col, step){
51353             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51354         };
51355         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51356         var newCell;
51357
51358       
51359
51360         switch(k){
51361             case e.TAB:
51362                 // handled by onEditorKey
51363                 if (g.isEditor && g.editing) {
51364                     return;
51365                 }
51366                 if(e.shiftKey) {
51367                     newCell = walk(r, c-1, -1);
51368                 } else {
51369                     newCell = walk(r, c+1, 1);
51370                 }
51371                 break;
51372             
51373             case e.DOWN:
51374                newCell = walk(r+1, c, 1);
51375                 break;
51376             
51377             case e.UP:
51378                 newCell = walk(r-1, c, -1);
51379                 break;
51380             
51381             case e.RIGHT:
51382                 newCell = walk(r, c+1, 1);
51383                 break;
51384             
51385             case e.LEFT:
51386                 newCell = walk(r, c-1, -1);
51387                 break;
51388             
51389             case e.ENTER:
51390                 
51391                 if(g.isEditor && !g.editing){
51392                    g.startEditing(r, c);
51393                    e.stopEvent();
51394                    return;
51395                 }
51396                 
51397                 
51398              break;
51399         };
51400         if(newCell){
51401             this.select(newCell[0], newCell[1]);
51402             e.stopEvent();
51403             
51404         }
51405     },
51406
51407     acceptsNav : function(row, col, cm){
51408         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51409     },
51410     /**
51411      * Selects a cell.
51412      * @param {Number} field (not used) - as it's normally used as a listener
51413      * @param {Number} e - event - fake it by using
51414      *
51415      * var e = Roo.EventObjectImpl.prototype;
51416      * e.keyCode = e.TAB
51417      *
51418      * 
51419      */
51420     onEditorKey : function(field, e){
51421         
51422         var k = e.getKey(),
51423             newCell,
51424             g = this.grid,
51425             ed = g.activeEditor,
51426             forward = false;
51427         ///Roo.log('onEditorKey' + k);
51428         
51429         
51430         if (this.enter_is_tab && k == e.ENTER) {
51431             k = e.TAB;
51432         }
51433         
51434         if(k == e.TAB){
51435             if(e.shiftKey){
51436                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51437             }else{
51438                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51439                 forward = true;
51440             }
51441             
51442             e.stopEvent();
51443             
51444         }else if(k == e.ENTER &&  !e.ctrlKey){
51445             ed.completeEdit();
51446             e.stopEvent();
51447             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51448         }else if(k == e.ESC){
51449             ed.cancelEdit();
51450         }
51451         
51452         
51453         if(newCell){
51454             //Roo.log('next cell after edit');
51455             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51456         } else if (forward) {
51457             // tabbed past last
51458             this.fireEvent.defer(100, this, ['tabend',this]);
51459         }
51460     }
51461 });/*
51462  * Based on:
51463  * Ext JS Library 1.1.1
51464  * Copyright(c) 2006-2007, Ext JS, LLC.
51465  *
51466  * Originally Released Under LGPL - original licence link has changed is not relivant.
51467  *
51468  * Fork - LGPL
51469  * <script type="text/javascript">
51470  */
51471  
51472 /**
51473  * @class Roo.grid.EditorGrid
51474  * @extends Roo.grid.Grid
51475  * Class for creating and editable grid.
51476  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51477  * The container MUST have some type of size defined for the grid to fill. The container will be 
51478  * automatically set to position relative if it isn't already.
51479  * @param {Object} dataSource The data model to bind to
51480  * @param {Object} colModel The column model with info about this grid's columns
51481  */
51482 Roo.grid.EditorGrid = function(container, config){
51483     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51484     this.getGridEl().addClass("xedit-grid");
51485
51486     if(!this.selModel){
51487         this.selModel = new Roo.grid.CellSelectionModel();
51488     }
51489
51490     this.activeEditor = null;
51491
51492         this.addEvents({
51493             /**
51494              * @event beforeedit
51495              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51496              * <ul style="padding:5px;padding-left:16px;">
51497              * <li>grid - This grid</li>
51498              * <li>record - The record being edited</li>
51499              * <li>field - The field name being edited</li>
51500              * <li>value - The value for the field being edited.</li>
51501              * <li>row - The grid row index</li>
51502              * <li>column - The grid column index</li>
51503              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51504              * </ul>
51505              * @param {Object} e An edit event (see above for description)
51506              */
51507             "beforeedit" : true,
51508             /**
51509              * @event afteredit
51510              * Fires after a cell is edited. <br />
51511              * <ul style="padding:5px;padding-left:16px;">
51512              * <li>grid - This grid</li>
51513              * <li>record - The record being edited</li>
51514              * <li>field - The field name being edited</li>
51515              * <li>value - The value being set</li>
51516              * <li>originalValue - The original value for the field, before the edit.</li>
51517              * <li>row - The grid row index</li>
51518              * <li>column - The grid column index</li>
51519              * </ul>
51520              * @param {Object} e An edit event (see above for description)
51521              */
51522             "afteredit" : true,
51523             /**
51524              * @event validateedit
51525              * Fires after a cell is edited, but before the value is set in the record. 
51526          * You can use this to modify the value being set in the field, Return false
51527              * to cancel the change. The edit event object has the following properties <br />
51528              * <ul style="padding:5px;padding-left:16px;">
51529          * <li>editor - This editor</li>
51530              * <li>grid - This grid</li>
51531              * <li>record - The record being edited</li>
51532              * <li>field - The field name being edited</li>
51533              * <li>value - The value being set</li>
51534              * <li>originalValue - The original value for the field, before the edit.</li>
51535              * <li>row - The grid row index</li>
51536              * <li>column - The grid column index</li>
51537              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51538              * </ul>
51539              * @param {Object} e An edit event (see above for description)
51540              */
51541             "validateedit" : true
51542         });
51543     this.on("bodyscroll", this.stopEditing,  this);
51544     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51545 };
51546
51547 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51548     /**
51549      * @cfg {Number} clicksToEdit
51550      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51551      */
51552     clicksToEdit: 2,
51553
51554     // private
51555     isEditor : true,
51556     // private
51557     trackMouseOver: false, // causes very odd FF errors
51558
51559     onCellDblClick : function(g, row, col){
51560         this.startEditing(row, col);
51561     },
51562
51563     onEditComplete : function(ed, value, startValue){
51564         this.editing = false;
51565         this.activeEditor = null;
51566         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51567         var r = ed.record;
51568         var field = this.colModel.getDataIndex(ed.col);
51569         var e = {
51570             grid: this,
51571             record: r,
51572             field: field,
51573             originalValue: startValue,
51574             value: value,
51575             row: ed.row,
51576             column: ed.col,
51577             cancel:false,
51578             editor: ed
51579         };
51580         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51581         cell.show();
51582           
51583         if(String(value) !== String(startValue)){
51584             
51585             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51586                 r.set(field, e.value);
51587                 // if we are dealing with a combo box..
51588                 // then we also set the 'name' colum to be the displayField
51589                 if (ed.field.displayField && ed.field.name) {
51590                     r.set(ed.field.name, ed.field.el.dom.value);
51591                 }
51592                 
51593                 delete e.cancel; //?? why!!!
51594                 this.fireEvent("afteredit", e);
51595             }
51596         } else {
51597             this.fireEvent("afteredit", e); // always fire it!
51598         }
51599         this.view.focusCell(ed.row, ed.col);
51600     },
51601
51602     /**
51603      * Starts editing the specified for the specified row/column
51604      * @param {Number} rowIndex
51605      * @param {Number} colIndex
51606      */
51607     startEditing : function(row, col){
51608         this.stopEditing();
51609         if(this.colModel.isCellEditable(col, row)){
51610             this.view.ensureVisible(row, col, true);
51611           
51612             var r = this.dataSource.getAt(row);
51613             var field = this.colModel.getDataIndex(col);
51614             var cell = Roo.get(this.view.getCell(row,col));
51615             var e = {
51616                 grid: this,
51617                 record: r,
51618                 field: field,
51619                 value: r.data[field],
51620                 row: row,
51621                 column: col,
51622                 cancel:false 
51623             };
51624             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51625                 this.editing = true;
51626                 var ed = this.colModel.getCellEditor(col, row);
51627                 
51628                 if (!ed) {
51629                     return;
51630                 }
51631                 if(!ed.rendered){
51632                     ed.render(ed.parentEl || document.body);
51633                 }
51634                 ed.field.reset();
51635                
51636                 cell.hide();
51637                 
51638                 (function(){ // complex but required for focus issues in safari, ie and opera
51639                     ed.row = row;
51640                     ed.col = col;
51641                     ed.record = r;
51642                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51643                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51644                     this.activeEditor = ed;
51645                     var v = r.data[field];
51646                     ed.startEdit(this.view.getCell(row, col), v);
51647                     // combo's with 'displayField and name set
51648                     if (ed.field.displayField && ed.field.name) {
51649                         ed.field.el.dom.value = r.data[ed.field.name];
51650                     }
51651                     
51652                     
51653                 }).defer(50, this);
51654             }
51655         }
51656     },
51657         
51658     /**
51659      * Stops any active editing
51660      */
51661     stopEditing : function(){
51662         if(this.activeEditor){
51663             this.activeEditor.completeEdit();
51664         }
51665         this.activeEditor = null;
51666     }
51667 });/*
51668  * Based on:
51669  * Ext JS Library 1.1.1
51670  * Copyright(c) 2006-2007, Ext JS, LLC.
51671  *
51672  * Originally Released Under LGPL - original licence link has changed is not relivant.
51673  *
51674  * Fork - LGPL
51675  * <script type="text/javascript">
51676  */
51677
51678 // private - not really -- you end up using it !
51679 // This is a support class used internally by the Grid components
51680
51681 /**
51682  * @class Roo.grid.GridEditor
51683  * @extends Roo.Editor
51684  * Class for creating and editable grid elements.
51685  * @param {Object} config any settings (must include field)
51686  */
51687 Roo.grid.GridEditor = function(field, config){
51688     if (!config && field.field) {
51689         config = field;
51690         field = Roo.factory(config.field, Roo.form);
51691     }
51692     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51693     field.monitorTab = false;
51694 };
51695
51696 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51697     
51698     /**
51699      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51700      */
51701     
51702     alignment: "tl-tl",
51703     autoSize: "width",
51704     hideEl : false,
51705     cls: "x-small-editor x-grid-editor",
51706     shim:false,
51707     shadow:"frame"
51708 });/*
51709  * Based on:
51710  * Ext JS Library 1.1.1
51711  * Copyright(c) 2006-2007, Ext JS, LLC.
51712  *
51713  * Originally Released Under LGPL - original licence link has changed is not relivant.
51714  *
51715  * Fork - LGPL
51716  * <script type="text/javascript">
51717  */
51718   
51719
51720   
51721 Roo.grid.PropertyRecord = Roo.data.Record.create([
51722     {name:'name',type:'string'},  'value'
51723 ]);
51724
51725
51726 Roo.grid.PropertyStore = function(grid, source){
51727     this.grid = grid;
51728     this.store = new Roo.data.Store({
51729         recordType : Roo.grid.PropertyRecord
51730     });
51731     this.store.on('update', this.onUpdate,  this);
51732     if(source){
51733         this.setSource(source);
51734     }
51735     Roo.grid.PropertyStore.superclass.constructor.call(this);
51736 };
51737
51738
51739
51740 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51741     setSource : function(o){
51742         this.source = o;
51743         this.store.removeAll();
51744         var data = [];
51745         for(var k in o){
51746             if(this.isEditableValue(o[k])){
51747                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51748             }
51749         }
51750         this.store.loadRecords({records: data}, {}, true);
51751     },
51752
51753     onUpdate : function(ds, record, type){
51754         if(type == Roo.data.Record.EDIT){
51755             var v = record.data['value'];
51756             var oldValue = record.modified['value'];
51757             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51758                 this.source[record.id] = v;
51759                 record.commit();
51760                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51761             }else{
51762                 record.reject();
51763             }
51764         }
51765     },
51766
51767     getProperty : function(row){
51768        return this.store.getAt(row);
51769     },
51770
51771     isEditableValue: function(val){
51772         if(val && val instanceof Date){
51773             return true;
51774         }else if(typeof val == 'object' || typeof val == 'function'){
51775             return false;
51776         }
51777         return true;
51778     },
51779
51780     setValue : function(prop, value){
51781         this.source[prop] = value;
51782         this.store.getById(prop).set('value', value);
51783     },
51784
51785     getSource : function(){
51786         return this.source;
51787     }
51788 });
51789
51790 Roo.grid.PropertyColumnModel = function(grid, store){
51791     this.grid = grid;
51792     var g = Roo.grid;
51793     g.PropertyColumnModel.superclass.constructor.call(this, [
51794         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51795         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51796     ]);
51797     this.store = store;
51798     this.bselect = Roo.DomHelper.append(document.body, {
51799         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51800             {tag: 'option', value: 'true', html: 'true'},
51801             {tag: 'option', value: 'false', html: 'false'}
51802         ]
51803     });
51804     Roo.id(this.bselect);
51805     var f = Roo.form;
51806     this.editors = {
51807         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51808         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51809         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51810         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51811         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51812     };
51813     this.renderCellDelegate = this.renderCell.createDelegate(this);
51814     this.renderPropDelegate = this.renderProp.createDelegate(this);
51815 };
51816
51817 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51818     
51819     
51820     nameText : 'Name',
51821     valueText : 'Value',
51822     
51823     dateFormat : 'm/j/Y',
51824     
51825     
51826     renderDate : function(dateVal){
51827         return dateVal.dateFormat(this.dateFormat);
51828     },
51829
51830     renderBool : function(bVal){
51831         return bVal ? 'true' : 'false';
51832     },
51833
51834     isCellEditable : function(colIndex, rowIndex){
51835         return colIndex == 1;
51836     },
51837
51838     getRenderer : function(col){
51839         return col == 1 ?
51840             this.renderCellDelegate : this.renderPropDelegate;
51841     },
51842
51843     renderProp : function(v){
51844         return this.getPropertyName(v);
51845     },
51846
51847     renderCell : function(val){
51848         var rv = val;
51849         if(val instanceof Date){
51850             rv = this.renderDate(val);
51851         }else if(typeof val == 'boolean'){
51852             rv = this.renderBool(val);
51853         }
51854         return Roo.util.Format.htmlEncode(rv);
51855     },
51856
51857     getPropertyName : function(name){
51858         var pn = this.grid.propertyNames;
51859         return pn && pn[name] ? pn[name] : name;
51860     },
51861
51862     getCellEditor : function(colIndex, rowIndex){
51863         var p = this.store.getProperty(rowIndex);
51864         var n = p.data['name'], val = p.data['value'];
51865         
51866         if(typeof(this.grid.customEditors[n]) == 'string'){
51867             return this.editors[this.grid.customEditors[n]];
51868         }
51869         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51870             return this.grid.customEditors[n];
51871         }
51872         if(val instanceof Date){
51873             return this.editors['date'];
51874         }else if(typeof val == 'number'){
51875             return this.editors['number'];
51876         }else if(typeof val == 'boolean'){
51877             return this.editors['boolean'];
51878         }else{
51879             return this.editors['string'];
51880         }
51881     }
51882 });
51883
51884 /**
51885  * @class Roo.grid.PropertyGrid
51886  * @extends Roo.grid.EditorGrid
51887  * This class represents the  interface of a component based property grid control.
51888  * <br><br>Usage:<pre><code>
51889  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51890       
51891  });
51892  // set any options
51893  grid.render();
51894  * </code></pre>
51895   
51896  * @constructor
51897  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51898  * The container MUST have some type of size defined for the grid to fill. The container will be
51899  * automatically set to position relative if it isn't already.
51900  * @param {Object} config A config object that sets properties on this grid.
51901  */
51902 Roo.grid.PropertyGrid = function(container, config){
51903     config = config || {};
51904     var store = new Roo.grid.PropertyStore(this);
51905     this.store = store;
51906     var cm = new Roo.grid.PropertyColumnModel(this, store);
51907     store.store.sort('name', 'ASC');
51908     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51909         ds: store.store,
51910         cm: cm,
51911         enableColLock:false,
51912         enableColumnMove:false,
51913         stripeRows:false,
51914         trackMouseOver: false,
51915         clicksToEdit:1
51916     }, config));
51917     this.getGridEl().addClass('x-props-grid');
51918     this.lastEditRow = null;
51919     this.on('columnresize', this.onColumnResize, this);
51920     this.addEvents({
51921          /**
51922              * @event beforepropertychange
51923              * Fires before a property changes (return false to stop?)
51924              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51925              * @param {String} id Record Id
51926              * @param {String} newval New Value
51927          * @param {String} oldval Old Value
51928              */
51929         "beforepropertychange": true,
51930         /**
51931              * @event propertychange
51932              * Fires after a property changes
51933              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51934              * @param {String} id Record Id
51935              * @param {String} newval New Value
51936          * @param {String} oldval Old Value
51937              */
51938         "propertychange": true
51939     });
51940     this.customEditors = this.customEditors || {};
51941 };
51942 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51943     
51944      /**
51945      * @cfg {Object} customEditors map of colnames=> custom editors.
51946      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51947      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51948      * false disables editing of the field.
51949          */
51950     
51951       /**
51952      * @cfg {Object} propertyNames map of property Names to their displayed value
51953          */
51954     
51955     render : function(){
51956         Roo.grid.PropertyGrid.superclass.render.call(this);
51957         this.autoSize.defer(100, this);
51958     },
51959
51960     autoSize : function(){
51961         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51962         if(this.view){
51963             this.view.fitColumns();
51964         }
51965     },
51966
51967     onColumnResize : function(){
51968         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51969         this.autoSize();
51970     },
51971     /**
51972      * Sets the data for the Grid
51973      * accepts a Key => Value object of all the elements avaiable.
51974      * @param {Object} data  to appear in grid.
51975      */
51976     setSource : function(source){
51977         this.store.setSource(source);
51978         //this.autoSize();
51979     },
51980     /**
51981      * Gets all the data from the grid.
51982      * @return {Object} data  data stored in grid
51983      */
51984     getSource : function(){
51985         return this.store.getSource();
51986     }
51987 });/*
51988  * Based on:
51989  * Ext JS Library 1.1.1
51990  * Copyright(c) 2006-2007, Ext JS, LLC.
51991  *
51992  * Originally Released Under LGPL - original licence link has changed is not relivant.
51993  *
51994  * Fork - LGPL
51995  * <script type="text/javascript">
51996  */
51997  
51998 /**
51999  * @class Roo.LoadMask
52000  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52001  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52002  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52003  * element's UpdateManager load indicator and will be destroyed after the initial load.
52004  * @constructor
52005  * Create a new LoadMask
52006  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52007  * @param {Object} config The config object
52008  */
52009 Roo.LoadMask = function(el, config){
52010     this.el = Roo.get(el);
52011     Roo.apply(this, config);
52012     if(this.store){
52013         this.store.on('beforeload', this.onBeforeLoad, this);
52014         this.store.on('load', this.onLoad, this);
52015         this.store.on('loadexception', this.onLoadException, this);
52016         this.removeMask = false;
52017     }else{
52018         var um = this.el.getUpdateManager();
52019         um.showLoadIndicator = false; // disable the default indicator
52020         um.on('beforeupdate', this.onBeforeLoad, this);
52021         um.on('update', this.onLoad, this);
52022         um.on('failure', this.onLoad, this);
52023         this.removeMask = true;
52024     }
52025 };
52026
52027 Roo.LoadMask.prototype = {
52028     /**
52029      * @cfg {Boolean} removeMask
52030      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52031      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52032      */
52033     /**
52034      * @cfg {String} msg
52035      * The text to display in a centered loading message box (defaults to 'Loading...')
52036      */
52037     msg : 'Loading...',
52038     /**
52039      * @cfg {String} msgCls
52040      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52041      */
52042     msgCls : 'x-mask-loading',
52043
52044     /**
52045      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52046      * @type Boolean
52047      */
52048     disabled: false,
52049
52050     /**
52051      * Disables the mask to prevent it from being displayed
52052      */
52053     disable : function(){
52054        this.disabled = true;
52055     },
52056
52057     /**
52058      * Enables the mask so that it can be displayed
52059      */
52060     enable : function(){
52061         this.disabled = false;
52062     },
52063     
52064     onLoadException : function()
52065     {
52066         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52067             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52068         }
52069         this.el.unmask(this.removeMask);
52070     },
52071     // private
52072     onLoad : function()
52073     {
52074         this.el.unmask(this.removeMask);
52075     },
52076
52077     // private
52078     onBeforeLoad : function(){
52079         if(!this.disabled){
52080             this.el.mask(this.msg, this.msgCls);
52081         }
52082     },
52083
52084     // private
52085     destroy : function(){
52086         if(this.store){
52087             this.store.un('beforeload', this.onBeforeLoad, this);
52088             this.store.un('load', this.onLoad, this);
52089             this.store.un('loadexception', this.onLoadException, this);
52090         }else{
52091             var um = this.el.getUpdateManager();
52092             um.un('beforeupdate', this.onBeforeLoad, this);
52093             um.un('update', this.onLoad, this);
52094             um.un('failure', this.onLoad, this);
52095         }
52096     }
52097 };/*
52098  * Based on:
52099  * Ext JS Library 1.1.1
52100  * Copyright(c) 2006-2007, Ext JS, LLC.
52101  *
52102  * Originally Released Under LGPL - original licence link has changed is not relivant.
52103  *
52104  * Fork - LGPL
52105  * <script type="text/javascript">
52106  */
52107 Roo.XTemplate = function(){
52108     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52109     var s = this.html;
52110
52111     s = ['<tpl>', s, '</tpl>'].join('');
52112
52113     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52114
52115     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52116     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52117     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52118     var m, id = 0;
52119     var tpls = [];
52120
52121     while(m = s.match(re)){
52122        var m2 = m[0].match(nameRe);
52123        var m3 = m[0].match(ifRe);
52124        var m4 = m[0].match(execRe);
52125        var exp = null, fn = null, exec = null;
52126        var name = m2 && m2[1] ? m2[1] : '';
52127        if(m3){
52128            exp = m3 && m3[1] ? m3[1] : null;
52129            if(exp){
52130                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52131            }
52132        }
52133        if(m4){
52134            exp = m4 && m4[1] ? m4[1] : null;
52135            if(exp){
52136                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52137            }
52138        }
52139        if(name){
52140            switch(name){
52141                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52142                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52143                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52144            }
52145        }
52146        tpls.push({
52147             id: id,
52148             target: name,
52149             exec: exec,
52150             test: fn,
52151             body: m[1]||''
52152         });
52153        s = s.replace(m[0], '{xtpl'+ id + '}');
52154        ++id;
52155     }
52156     for(var i = tpls.length-1; i >= 0; --i){
52157         this.compileTpl(tpls[i]);
52158     }
52159     this.master = tpls[tpls.length-1];
52160     this.tpls = tpls;
52161 };
52162 Roo.extend(Roo.XTemplate, Roo.Template, {
52163
52164     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52165
52166     applySubTemplate : function(id, values, parent){
52167         var t = this.tpls[id];
52168         if(t.test && !t.test.call(this, values, parent)){
52169             return '';
52170         }
52171         if(t.exec && t.exec.call(this, values, parent)){
52172             return '';
52173         }
52174         var vs = t.target ? t.target.call(this, values, parent) : values;
52175         parent = t.target ? values : parent;
52176         if(t.target && vs instanceof Array){
52177             var buf = [];
52178             for(var i = 0, len = vs.length; i < len; i++){
52179                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52180             }
52181             return buf.join('');
52182         }
52183         return t.compiled.call(this, vs, parent);
52184     },
52185
52186     compileTpl : function(tpl){
52187         var fm = Roo.util.Format;
52188         var useF = this.disableFormats !== true;
52189         var sep = Roo.isGecko ? "+" : ",";
52190         var fn = function(m, name, format, args){
52191             if(name.substr(0, 4) == 'xtpl'){
52192                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52193             }
52194             var v;
52195             if(name.indexOf('.') != -1){
52196                 v = name;
52197             }else{
52198                 v = "values['" + name + "']";
52199             }
52200             if(format && useF){
52201                 args = args ? ',' + args : "";
52202                 if(format.substr(0, 5) != "this."){
52203                     format = "fm." + format + '(';
52204                 }else{
52205                     format = 'this.call("'+ format.substr(5) + '", ';
52206                     args = ", values";
52207                 }
52208             }else{
52209                 args= ''; format = "("+v+" === undefined ? '' : ";
52210             }
52211             return "'"+ sep + format + v + args + ")"+sep+"'";
52212         };
52213         var body;
52214         // branched to use + in gecko and [].join() in others
52215         if(Roo.isGecko){
52216             body = "tpl.compiled = function(values, parent){ return '" +
52217                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52218                     "';};";
52219         }else{
52220             body = ["tpl.compiled = function(values, parent){ return ['"];
52221             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52222             body.push("'].join('');};");
52223             body = body.join('');
52224         }
52225         /** eval:var:zzzzzzz */
52226         eval(body);
52227         return this;
52228     },
52229
52230     applyTemplate : function(values){
52231         return this.master.compiled.call(this, values, {});
52232         var s = this.subs;
52233     },
52234
52235     apply : function(){
52236         return this.applyTemplate.apply(this, arguments);
52237     },
52238
52239     compile : function(){return this;}
52240 });
52241
52242 Roo.XTemplate.from = function(el){
52243     el = Roo.getDom(el);
52244     return new Roo.XTemplate(el.value || el.innerHTML);
52245 };/*
52246  * Original code for Roojs - LGPL
52247  * <script type="text/javascript">
52248  */
52249  
52250 /**
52251  * @class Roo.XComponent
52252  * A delayed Element creator...
52253  * Or a way to group chunks of interface together.
52254  * 
52255  * Mypart.xyx = new Roo.XComponent({
52256
52257     parent : 'Mypart.xyz', // empty == document.element.!!
52258     order : '001',
52259     name : 'xxxx'
52260     region : 'xxxx'
52261     disabled : function() {} 
52262      
52263     tree : function() { // return an tree of xtype declared components
52264         var MODULE = this;
52265         return 
52266         {
52267             xtype : 'NestedLayoutPanel',
52268             // technicall
52269         }
52270      ]
52271  *})
52272  *
52273  *
52274  * It can be used to build a big heiracy, with parent etc.
52275  * or you can just use this to render a single compoent to a dom element
52276  * MYPART.render(Roo.Element | String(id) | dom_element )
52277  * 
52278  * @extends Roo.util.Observable
52279  * @constructor
52280  * @param cfg {Object} configuration of component
52281  * 
52282  */
52283 Roo.XComponent = function(cfg) {
52284     Roo.apply(this, cfg);
52285     this.addEvents({ 
52286         /**
52287              * @event built
52288              * Fires when this the componnt is built
52289              * @param {Roo.XComponent} c the component
52290              */
52291         'built' : true,
52292         /**
52293              * @event buildcomplete
52294              * Fires on the top level element when all elements have been built
52295              * @param {Roo.XComponent} c the top level component.
52296          */
52297         'buildcomplete' : true
52298         
52299     });
52300     this.region = this.region || 'center'; // default..
52301     Roo.XComponent.register(this);
52302     this.modules = false;
52303     this.el = false; // where the layout goes..
52304     
52305     
52306 }
52307 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52308     /**
52309      * @property el
52310      * The created element (with Roo.factory())
52311      * @type {Roo.Layout}
52312      */
52313     el  : false,
52314     
52315     /**
52316      * @property el
52317      * for BC  - use el in new code
52318      * @type {Roo.Layout}
52319      */
52320     panel : false,
52321     
52322     /**
52323      * @property layout
52324      * for BC  - use el in new code
52325      * @type {Roo.Layout}
52326      */
52327     layout : false,
52328     
52329      /**
52330      * @cfg {Function|boolean} disabled
52331      * If this module is disabled by some rule, return true from the funtion
52332      */
52333     disabled : false,
52334     
52335     /**
52336      * @cfg {String} parent 
52337      * Name of parent element which it get xtype added to..
52338      */
52339     parent: false,
52340     
52341     /**
52342      * @cfg {String} order
52343      * Used to set the order in which elements are created (usefull for multiple tabs)
52344      */
52345     
52346     order : false,
52347     /**
52348      * @cfg {String} name
52349      * String to display while loading.
52350      */
52351     name : false,
52352     /**
52353      * @cfg {String} region
52354      * Region to render component to (defaults to center)
52355      */
52356     region : 'center',
52357     
52358     /**
52359      * @cfg {Array} items
52360      * A single item array - the first element is the root of the tree..
52361      * It's done this way to stay compatible with the Xtype system...
52362      */
52363     items : false,
52364     
52365     
52366      /**
52367      * render
52368      * render element to dom or tree
52369      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52370      */
52371     
52372     render : function(el)
52373     {
52374         
52375         el = el || false;
52376         var hp = this.parent ? 1 : 0;
52377         
52378         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52379             // if parent is a '#.....' string, then let's use that..
52380             var ename = this.parent.substr(1)
52381             this.parent = false;
52382             el = Roo.get(ename);
52383             if (!el) {
52384                 Roo.log("Warning - element can not be found :#" + ename );
52385                 return;
52386             }
52387         }
52388         
52389         
52390         if (!this.parent) {
52391             
52392             el = el ? Roo.get(el) : false;
52393             
52394             // it's a top level one..
52395             this.parent =  {
52396                 el : new Roo.BorderLayout(el || document.body, {
52397                 
52398                      center: {
52399                          titlebar: false,
52400                          autoScroll:false,
52401                          closeOnTab: true,
52402                          tabPosition: 'top',
52403                           //resizeTabs: true,
52404                          alwaysShowTabs: el && hp? false :  true,
52405                          hideTabs: el || !hp ? true :  false,
52406                          minTabWidth: 140
52407                      }
52408                  })
52409             }
52410         }
52411         
52412         
52413             
52414         var tree = this.tree();
52415         tree.region = tree.region || this.region;
52416         this.el = this.parent.el.addxtype(tree);
52417         this.fireEvent('built', this);
52418         
52419         this.panel = this.el;
52420         this.layout = this.panel.layout;    
52421          
52422     }
52423     
52424 });
52425
52426 Roo.apply(Roo.XComponent, {
52427     
52428     /**
52429      * @property  buildCompleted
52430      * True when the builder has completed building the interface.
52431      * @type Boolean
52432      */
52433     buildCompleted : false,
52434      
52435     /**
52436      * @property  topModule
52437      * the upper most module - uses document.element as it's constructor.
52438      * @type Object
52439      */
52440      
52441     topModule  : false,
52442       
52443     /**
52444      * @property  modules
52445      * array of modules to be created by registration system.
52446      * @type {Array} of Roo.XComponent
52447      */
52448     
52449     modules : [],
52450     /**
52451      * @property  elmodules
52452      * array of modules to be created by which use #ID 
52453      * @type {Array} of Roo.XComponent
52454      */
52455      
52456     elmodules : [],
52457
52458     
52459     /**
52460      * Register components to be built later.
52461      *
52462      * This solves the following issues
52463      * - Building is not done on page load, but after an authentication process has occured.
52464      * - Interface elements are registered on page load
52465      * - Parent Interface elements may not be loaded before child, so this handles that..
52466      * 
52467      *
52468      * example:
52469      * 
52470      * MyApp.register({
52471           order : '000001',
52472           module : 'Pman.Tab.projectMgr',
52473           region : 'center',
52474           parent : 'Pman.layout',
52475           disabled : false,  // or use a function..
52476         })
52477      
52478      * * @param {Object} details about module
52479      */
52480     register : function(obj) {
52481         this.modules.push(obj);
52482          
52483     },
52484     /**
52485      * convert a string to an object..
52486      * eg. 'AAA.BBB' -> finds AAA.BBB
52487
52488      */
52489     
52490     toObject : function(str)
52491     {
52492         if (!str || typeof(str) == 'object') {
52493             return str;
52494         }
52495         if (str.substring(0,1) == '#') {
52496             return str;
52497         }
52498
52499         var ar = str.split('.');
52500         var rt, o;
52501         rt = ar.shift();
52502             /** eval:var:o */
52503         try {
52504             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52505         } catch (e) {
52506             throw "Module not found : " + str;
52507         }
52508         
52509         if (o === false) {
52510             throw "Module not found : " + str;
52511         }
52512         Roo.each(ar, function(e) {
52513             if (typeof(o[e]) == 'undefined') {
52514                 throw "Module not found : " + str;
52515             }
52516             o = o[e];
52517         });
52518         
52519         return o;
52520         
52521     },
52522     
52523     
52524     /**
52525      * move modules into their correct place in the tree..
52526      * 
52527      */
52528     preBuild : function ()
52529     {
52530         var _t = this;
52531         Roo.each(this.modules , function (obj)
52532         {
52533             var opar = obj.parent;
52534             try { 
52535                 obj.parent = this.toObject(opar);
52536             } catch(e) {
52537                 Roo.log(e.toString());
52538                 return;
52539             }
52540             
52541             if (!obj.parent) {
52542                 this.topModule = obj;
52543                 return;
52544             }
52545             if (typeof(obj.parent) == 'string') {
52546                 this.elmodules.push(obj);
52547                 return;
52548             }
52549             if (obj.parent.constructor != Roo.XComponent) {
52550                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52551             }
52552             if (!obj.parent.modules) {
52553                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52554                     function(o) { return o.order + '' }
52555                 );
52556             }
52557             
52558             obj.parent.modules.add(obj);
52559         }, this);
52560     },
52561     
52562      /**
52563      * make a list of modules to build.
52564      * @return {Array} list of modules. 
52565      */ 
52566     
52567     buildOrder : function()
52568     {
52569         var _this = this;
52570         var cmp = function(a,b) {   
52571             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52572         };
52573         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52574             throw "No top level modules to build";
52575         }
52576         
52577         // make a flat list in order of modules to build.
52578         var mods = this.topModule ? [ this.topModule ] : [];
52579         Roo.each(this.elmodules,function(e) { mods.push(e) });
52580
52581         
52582         // add modules to their parents..
52583         var addMod = function(m) {
52584            // Roo.debug && Roo.log(m.modKey);
52585             
52586             mods.push(m);
52587             if (m.modules) {
52588                 m.modules.keySort('ASC',  cmp );
52589                 m.modules.each(addMod);
52590             }
52591             // not sure if this is used any more..
52592             if (m.finalize) {
52593                 m.finalize.name = m.name + " (clean up) ";
52594                 mods.push(m.finalize);
52595             }
52596             
52597         }
52598         if (this.topModule) { 
52599             this.topModule.modules.keySort('ASC',  cmp );
52600             this.topModule.modules.each(addMod);
52601         }
52602         return mods;
52603     },
52604     
52605      /**
52606      * Build the registered modules.
52607      * @param {Object} parent element.
52608      * @param {Function} optional method to call after module has been added.
52609      * 
52610      */ 
52611    
52612     build : function() 
52613     {
52614         
52615         this.preBuild();
52616         var mods = this.buildOrder();
52617       
52618         //this.allmods = mods;
52619         //Roo.debug && Roo.log(mods);
52620         //return;
52621         if (!mods.length) { // should not happen
52622             throw "NO modules!!!";
52623         }
52624         
52625         
52626         
52627         // flash it up as modal - so we store the mask!?
52628         Roo.MessageBox.show({ title: 'loading' });
52629         Roo.MessageBox.show({
52630            title: "Please wait...",
52631            msg: "Building Interface...",
52632            width:450,
52633            progress:true,
52634            closable:false,
52635            modal: false
52636           
52637         });
52638         var total = mods.length;
52639         
52640         var _this = this;
52641         var progressRun = function() {
52642             if (!mods.length) {
52643                 Roo.debug && Roo.log('hide?');
52644                 Roo.MessageBox.hide();
52645                 if (_this.topModule) { 
52646                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52647                 }
52648                 // THE END...
52649                 return false;   
52650             }
52651             
52652             var m = mods.shift();
52653             
52654             
52655             Roo.debug && Roo.log(m);
52656             // not sure if this is supported any more.. - modules that are are just function
52657             if (typeof(m) == 'function') { 
52658                 m.call(this);
52659                 return progressRun.defer(10, _this);
52660             } 
52661             
52662             
52663             
52664             Roo.MessageBox.updateProgress(
52665                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52666                     " of " + total + 
52667                     (m.name ? (' - ' + m.name) : '')
52668                     );
52669             
52670          
52671             // is the module disabled?
52672             var disabled = (typeof(m.disabled) == 'function') ?
52673                 m.disabled.call(m.module.disabled) : m.disabled;    
52674             
52675             
52676             if (disabled) {
52677                 return progressRun(); // we do not update the display!
52678             }
52679             
52680             // now build 
52681             
52682             m.render();
52683             // it's 10 on top level, and 1 on others??? why...
52684             return progressRun.defer(10, _this);
52685              
52686         }
52687         progressRun.defer(1, _this);
52688      
52689         
52690         
52691     }
52692     
52693      
52694    
52695     
52696     
52697 });
52698  //<script type="text/javascript">
52699
52700
52701 /**
52702  * @class Roo.Login
52703  * @extends Roo.LayoutDialog
52704  * A generic Login Dialog..... - only one needed in theory!?!?
52705  *
52706  * Fires XComponent builder on success...
52707  * 
52708  * Sends 
52709  *    username,password, lang = for login actions.
52710  *    check = 1 for periodic checking that sesion is valid.
52711  *    passwordRequest = email request password
52712  *    logout = 1 = to logout
52713  * 
52714  * Affects: (this id="????" elements)
52715  *   loading  (removed) (used to indicate application is loading)
52716  *   loading-mask (hides) (used to hide application when it's building loading)
52717  *   
52718  * 
52719  * Usage: 
52720  *    
52721  * 
52722  * Myapp.login = Roo.Login({
52723      url: xxxx,
52724    
52725      realm : 'Myapp', 
52726      
52727      
52728      method : 'POST',
52729      
52730      
52731      * 
52732  })
52733  * 
52734  * 
52735  * 
52736  **/
52737  
52738 Roo.Login = function(cfg)
52739 {
52740     this.addEvents({
52741         'refreshed' : true
52742     });
52743     
52744     Roo.apply(this,cfg);
52745     
52746     Roo.onReady(function() {
52747         this.onLoad();
52748     }, this);
52749     // call parent..
52750     
52751    
52752     Roo.Login.superclass.constructor.call(this, this);
52753     //this.addxtype(this.items[0]);
52754     
52755     
52756 }
52757
52758
52759 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52760     
52761     /**
52762      * @cfg {String} method
52763      * Method used to query for login details.
52764      */
52765     
52766     method : 'POST',
52767     /**
52768      * @cfg {String} url
52769      * URL to query login data. - eg. baseURL + '/Login.php'
52770      */
52771     url : '',
52772     
52773     /**
52774      * @property user
52775      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52776      * @type {Object} 
52777      */
52778     user : false,
52779     /**
52780      * @property checkFails
52781      * Number of times we have attempted to get authentication check, and failed.
52782      * @type {Number} 
52783      */
52784     checkFails : 0,
52785       /**
52786      * @property intervalID
52787      * The window interval that does the constant login checking.
52788      * @type {Number} 
52789      */
52790     intervalID : 0,
52791     
52792     
52793     onLoad : function() // called on page load...
52794     {
52795         // load 
52796          
52797         if (Roo.get('loading')) { // clear any loading indicator..
52798             Roo.get('loading').remove();
52799         }
52800         
52801         //this.switchLang('en'); // set the language to english..
52802        
52803         this.check({
52804             success:  function(response, opts)  {  // check successfull...
52805             
52806                 var res = this.processResponse(response);
52807                 this.checkFails =0;
52808                 if (!res.success) { // error!
52809                     this.checkFails = 5;
52810                     //console.log('call failure');
52811                     return this.failure(response,opts);
52812                 }
52813                 
52814                 if (!res.data.id) { // id=0 == login failure.
52815                     return this.show();
52816                 }
52817                 
52818                               
52819                         //console.log(success);
52820                 this.fillAuth(res.data);   
52821                 this.checkFails =0;
52822                 Roo.XComponent.build();
52823             },
52824             failure : this.show
52825         });
52826         
52827     }, 
52828     
52829     
52830     check: function(cfg) // called every so often to refresh cookie etc..
52831     {
52832         if (cfg.again) { // could be undefined..
52833             this.checkFails++;
52834         } else {
52835             this.checkFails = 0;
52836         }
52837         var _this = this;
52838         if (this.sending) {
52839             if ( this.checkFails > 4) {
52840                 Roo.MessageBox.alert("Error",  
52841                     "Error getting authentication status. - try reloading, or wait a while", function() {
52842                         _this.sending = false;
52843                     }); 
52844                 return;
52845             }
52846             cfg.again = true;
52847             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52848             return;
52849         }
52850         this.sending = true;
52851         
52852         Roo.Ajax.request({  
52853             url: this.url,
52854             params: {
52855                 getAuthUser: true
52856             },  
52857             method: this.method,
52858             success:  cfg.success || this.success,
52859             failure : cfg.failure || this.failure,
52860             scope : this,
52861             callCfg : cfg
52862               
52863         });  
52864     }, 
52865     
52866     
52867     logout: function()
52868     {
52869         window.onbeforeunload = function() { }; // false does not work for IE..
52870         this.user = false;
52871         var _this = this;
52872         
52873         Roo.Ajax.request({  
52874             url: this.url,
52875             params: {
52876                 logout: 1
52877             },  
52878             method: 'GET',
52879             failure : function() {
52880                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52881                     document.location = document.location.toString() + '?ts=' + Math.random();
52882                 });
52883                 
52884             },
52885             success : function() {
52886                 _this.user = false;
52887                 this.checkFails =0;
52888                 // fixme..
52889                 document.location = document.location.toString() + '?ts=' + Math.random();
52890             }
52891               
52892               
52893         }); 
52894     },
52895     
52896     processResponse : function (response)
52897     {
52898         var res = '';
52899         try {
52900             res = Roo.decode(response.responseText);
52901             // oops...
52902             if (typeof(res) != 'object') {
52903                 res = { success : false, errorMsg : res, errors : true };
52904             }
52905             if (typeof(res.success) == 'undefined') {
52906                 res.success = false;
52907             }
52908             
52909         } catch(e) {
52910             res = { success : false,  errorMsg : response.responseText, errors : true };
52911         }
52912         return res;
52913     },
52914     
52915     success : function(response, opts)  // check successfull...
52916     {  
52917         this.sending = false;
52918         var res = this.processResponse(response);
52919         if (!res.success) {
52920             return this.failure(response, opts);
52921         }
52922         if (!res.data || !res.data.id) {
52923             return this.failure(response,opts);
52924         }
52925         //console.log(res);
52926         this.fillAuth(res.data);
52927         
52928         this.checkFails =0;
52929         
52930     },
52931     
52932     
52933     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52934     {
52935         this.authUser = -1;
52936         this.sending = false;
52937         var res = this.processResponse(response);
52938         //console.log(res);
52939         if ( this.checkFails > 2) {
52940         
52941             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52942                 "Error getting authentication status. - try reloading"); 
52943             return;
52944         }
52945         opts.callCfg.again = true;
52946         this.check.defer(1000, this, [ opts.callCfg ]);
52947         return;  
52948     },
52949     
52950     
52951     
52952     fillAuth: function(au) {
52953         this.startAuthCheck();
52954         this.authUserId = au.id;
52955         this.authUser = au;
52956         this.lastChecked = new Date();
52957         this.fireEvent('refreshed', au);
52958         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52959         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52960         au.lang = au.lang || 'en';
52961         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52962         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52963         this.switchLang(au.lang );
52964         
52965      
52966         // open system... - -on setyp..
52967         if (this.authUserId  < 0) {
52968             Roo.MessageBox.alert("Warning", 
52969                 "This is an open system - please set up a admin user with a password.");  
52970         }
52971          
52972         //Pman.onload(); // which should do nothing if it's a re-auth result...
52973         
52974              
52975     },
52976     
52977     startAuthCheck : function() // starter for timeout checking..
52978     {
52979         if (this.intervalID) { // timer already in place...
52980             return false;
52981         }
52982         var _this = this;
52983         this.intervalID =  window.setInterval(function() {
52984               _this.check(false);
52985             }, 120000); // every 120 secs = 2mins..
52986         
52987         
52988     },
52989          
52990     
52991     switchLang : function (lang) 
52992     {
52993         _T = typeof(_T) == 'undefined' ? false : _T;
52994           if (!_T || !lang.length) {
52995             return;
52996         }
52997         
52998         if (!_T && lang != 'en') {
52999             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53000             return;
53001         }
53002         
53003         if (typeof(_T.en) == 'undefined') {
53004             _T.en = {};
53005             Roo.apply(_T.en, _T);
53006         }
53007         
53008         if (typeof(_T[lang]) == 'undefined') {
53009             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53010             return;
53011         }
53012         
53013         
53014         Roo.apply(_T, _T[lang]);
53015         // just need to set the text values for everything...
53016         var _this = this;
53017         /* this will not work ...
53018         if (this.form) { 
53019             
53020                
53021             function formLabel(name, val) {
53022                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53023             }
53024             
53025             formLabel('password', "Password"+':');
53026             formLabel('username', "Email Address"+':');
53027             formLabel('lang', "Language"+':');
53028             this.dialog.setTitle("Login");
53029             this.dialog.buttons[0].setText("Forgot Password");
53030             this.dialog.buttons[1].setText("Login");
53031         }
53032         */
53033         
53034         
53035     },
53036     
53037     
53038     title: "Login",
53039     modal: true,
53040     width:  350,
53041     //height: 230,
53042     height: 180,
53043     shadow: true,
53044     minWidth:200,
53045     minHeight:180,
53046     //proxyDrag: true,
53047     closable: false,
53048     draggable: false,
53049     collapsible: false,
53050     resizable: false,
53051     center: {  // needed??
53052         autoScroll:false,
53053         titlebar: false,
53054        // tabPosition: 'top',
53055         hideTabs: true,
53056         closeOnTab: true,
53057         alwaysShowTabs: false
53058     } ,
53059     listeners : {
53060         
53061         show  : function(dlg)
53062         {
53063             //console.log(this);
53064             this.form = this.layout.getRegion('center').activePanel.form;
53065             this.form.dialog = dlg;
53066             this.buttons[0].form = this.form;
53067             this.buttons[0].dialog = dlg;
53068             this.buttons[1].form = this.form;
53069             this.buttons[1].dialog = dlg;
53070            
53071            //this.resizeToLogo.defer(1000,this);
53072             // this is all related to resizing for logos..
53073             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53074            //// if (!sz) {
53075              //   this.resizeToLogo.defer(1000,this);
53076              //   return;
53077            // }
53078             //var w = Ext.lib.Dom.getViewWidth() - 100;
53079             //var h = Ext.lib.Dom.getViewHeight() - 100;
53080             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53081             //this.center();
53082             if (this.disabled) {
53083                 this.hide();
53084                 return;
53085             }
53086             
53087             if (this.user.id < 0) { // used for inital setup situations.
53088                 return;
53089             }
53090             
53091             if (this.intervalID) {
53092                 // remove the timer
53093                 window.clearInterval(this.intervalID);
53094                 this.intervalID = false;
53095             }
53096             
53097             
53098             if (Roo.get('loading')) {
53099                 Roo.get('loading').remove();
53100             }
53101             if (Roo.get('loading-mask')) {
53102                 Roo.get('loading-mask').hide();
53103             }
53104             
53105             //incomming._node = tnode;
53106             this.form.reset();
53107             //this.dialog.modal = !modal;
53108             //this.dialog.show();
53109             this.el.unmask(); 
53110             
53111             
53112             this.form.setValues({
53113                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53114                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53115             });
53116             
53117             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53118             if (this.form.findField('username').getValue().length > 0 ){
53119                 this.form.findField('password').focus();
53120             } else {
53121                this.form.findField('username').focus();
53122             }
53123     
53124         }
53125     },
53126     items : [
53127          {
53128        
53129             xtype : 'ContentPanel',
53130             xns : Roo,
53131             region: 'center',
53132             fitToFrame : true,
53133             
53134             items : [
53135     
53136                 {
53137                
53138                     xtype : 'Form',
53139                     xns : Roo.form,
53140                     labelWidth: 100,
53141                     style : 'margin: 10px;',
53142                     
53143                     listeners : {
53144                         actionfailed : function(f, act) {
53145                             // form can return { errors: .... }
53146                                 
53147                             //act.result.errors // invalid form element list...
53148                             //act.result.errorMsg// invalid form element list...
53149                             
53150                             this.dialog.el.unmask();
53151                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53152                                         "Login failed - communication error - try again.");
53153                                       
53154                         },
53155                         actioncomplete: function(re, act) {
53156                              
53157                             Roo.state.Manager.set(
53158                                 this.dialog.realm + '.username',  
53159                                     this.findField('username').getValue()
53160                             );
53161                             Roo.state.Manager.set(
53162                                 this.dialog.realm + '.lang',  
53163                                 this.findField('lang').getValue() 
53164                             );
53165                             
53166                             this.dialog.fillAuth(act.result.data);
53167                               
53168                             this.dialog.hide();
53169                             
53170                             if (Roo.get('loading-mask')) {
53171                                 Roo.get('loading-mask').show();
53172                             }
53173                             Roo.XComponent.build();
53174                             
53175                              
53176                             
53177                         }
53178                     },
53179                     items : [
53180                         {
53181                             xtype : 'TextField',
53182                             xns : Roo.form,
53183                             fieldLabel: "Email Address",
53184                             name: 'username',
53185                             width:200,
53186                             autoCreate : {tag: "input", type: "text", size: "20"}
53187                         },
53188                         {
53189                             xtype : 'TextField',
53190                             xns : Roo.form,
53191                             fieldLabel: "Password",
53192                             inputType: 'password',
53193                             name: 'password',
53194                             width:200,
53195                             autoCreate : {tag: "input", type: "text", size: "20"},
53196                             listeners : {
53197                                 specialkey : function(e,ev) {
53198                                     if (ev.keyCode == 13) {
53199                                         this.form.dialog.el.mask("Logging in");
53200                                         this.form.doAction('submit', {
53201                                             url: this.form.dialog.url,
53202                                             method: this.form.dialog.method
53203                                         });
53204                                     }
53205                                 }
53206                             }  
53207                         },
53208                         {
53209                             xtype : 'ComboBox',
53210                             xns : Roo.form,
53211                             fieldLabel: "Language",
53212                             name : 'langdisp',
53213                             store: {
53214                                 xtype : 'SimpleStore',
53215                                 fields: ['lang', 'ldisp'],
53216                                 data : [
53217                                     [ 'en', 'English' ],
53218                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53219                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53220                                 ]
53221                             },
53222                             
53223                             valueField : 'lang',
53224                             hiddenName:  'lang',
53225                             width: 200,
53226                             displayField:'ldisp',
53227                             typeAhead: false,
53228                             editable: false,
53229                             mode: 'local',
53230                             triggerAction: 'all',
53231                             emptyText:'Select a Language...',
53232                             selectOnFocus:true,
53233                             listeners : {
53234                                 select :  function(cb, rec, ix) {
53235                                     this.form.switchLang(rec.data.lang);
53236                                 }
53237                             }
53238                         
53239                         }
53240                     ]
53241                 }
53242                   
53243                 
53244             ]
53245         }
53246     ],
53247     buttons : [
53248         {
53249             xtype : 'Button',
53250             xns : 'Roo',
53251             text : "Forgot Password",
53252             listeners : {
53253                 click : function() {
53254                     //console.log(this);
53255                     var n = this.form.findField('username').getValue();
53256                     if (!n.length) {
53257                         Roo.MessageBox.alert("Error", "Fill in your email address");
53258                         return;
53259                     }
53260                     Roo.Ajax.request({
53261                         url: this.dialog.url,
53262                         params: {
53263                             passwordRequest: n
53264                         },
53265                         method: this.dialog.method,
53266                         success:  function(response, opts)  {  // check successfull...
53267                         
53268                             var res = this.dialog.processResponse(response);
53269                             if (!res.success) { // error!
53270                                Roo.MessageBox.alert("Error" ,
53271                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53272                                return;
53273                             }
53274                             Roo.MessageBox.alert("Notice" ,
53275                                 "Please check you email for the Password Reset message");
53276                         },
53277                         failure : function() {
53278                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53279                         }
53280                         
53281                     });
53282                 }
53283             }
53284         },
53285         {
53286             xtype : 'Button',
53287             xns : 'Roo',
53288             text : "Login",
53289             listeners : {
53290                 
53291                 click : function () {
53292                         
53293                     this.dialog.el.mask("Logging in");
53294                     this.form.doAction('submit', {
53295                             url: this.dialog.url,
53296                             method: this.dialog.method
53297                     });
53298                 }
53299             }
53300         }
53301     ]
53302   
53303   
53304 })
53305  
53306
53307
53308