roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             // show a message if no listener is registered.
19557             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19558                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19559             }
19560             // loadmask wil be hooked into this..
19561             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19562             return;
19563         }
19564         var r = o.records, t = o.totalRecords || r.length;
19565         if(!options || options.add !== true){
19566             if(this.pruneModifiedRecords){
19567                 this.modified = [];
19568             }
19569             for(var i = 0, len = r.length; i < len; i++){
19570                 r[i].join(this);
19571             }
19572             if(this.snapshot){
19573                 this.data = this.snapshot;
19574                 delete this.snapshot;
19575             }
19576             this.data.clear();
19577             this.data.addAll(r);
19578             this.totalLength = t;
19579             this.applySort();
19580             this.fireEvent("datachanged", this);
19581         }else{
19582             this.totalLength = Math.max(t, this.data.length+r.length);
19583             this.add(r);
19584         }
19585         this.fireEvent("load", this, r, options);
19586         if(options.callback){
19587             options.callback.call(options.scope || this, r, options, true);
19588         }
19589     },
19590
19591
19592     /**
19593      * Loads data from a passed data block. A Reader which understands the format of the data
19594      * must have been configured in the constructor.
19595      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19596      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19597      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19598      */
19599     loadData : function(o, append){
19600         var r = this.reader.readRecords(o);
19601         this.loadRecords(r, {add: append}, true);
19602     },
19603
19604     /**
19605      * Gets the number of cached records.
19606      * <p>
19607      * <em>If using paging, this may not be the total size of the dataset. If the data object
19608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19609      * the data set size</em>
19610      */
19611     getCount : function(){
19612         return this.data.length || 0;
19613     },
19614
19615     /**
19616      * Gets the total number of records in the dataset as returned by the server.
19617      * <p>
19618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19619      * the dataset size</em>
19620      */
19621     getTotalCount : function(){
19622         return this.totalLength || 0;
19623     },
19624
19625     /**
19626      * Returns the sort state of the Store as an object with two properties:
19627      * <pre><code>
19628  field {String} The name of the field by which the Records are sorted
19629  direction {String} The sort order, "ASC" or "DESC"
19630      * </code></pre>
19631      */
19632     getSortState : function(){
19633         return this.sortInfo;
19634     },
19635
19636     // private
19637     applySort : function(){
19638         if(this.sortInfo && !this.remoteSort){
19639             var s = this.sortInfo, f = s.field;
19640             var st = this.fields.get(f).sortType;
19641             var fn = function(r1, r2){
19642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19644             };
19645             this.data.sort(s.direction, fn);
19646             if(this.snapshot && this.snapshot != this.data){
19647                 this.snapshot.sort(s.direction, fn);
19648             }
19649         }
19650     },
19651
19652     /**
19653      * Sets the default sort column and order to be used by the next load operation.
19654      * @param {String} fieldName The name of the field to sort by.
19655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19656      */
19657     setDefaultSort : function(field, dir){
19658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19659     },
19660
19661     /**
19662      * Sort the Records.
19663      * If remote sorting is used, the sort is performed on the server, and the cache is
19664      * reloaded. If local sorting is used, the cache is sorted internally.
19665      * @param {String} fieldName The name of the field to sort by.
19666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19667      */
19668     sort : function(fieldName, dir){
19669         var f = this.fields.get(fieldName);
19670         if(!dir){
19671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19672             
19673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19675             }else{
19676                 dir = f.sortDir;
19677             }
19678         }
19679         this.sortToggle[f.name] = dir;
19680         this.sortInfo = {field: f.name, direction: dir};
19681         if(!this.remoteSort){
19682             this.applySort();
19683             this.fireEvent("datachanged", this);
19684         }else{
19685             this.load(this.lastOptions);
19686         }
19687     },
19688
19689     /**
19690      * Calls the specified function for each of the Records in the cache.
19691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19692      * Returning <em>false</em> aborts and exits the iteration.
19693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19694      */
19695     each : function(fn, scope){
19696         this.data.each(fn, scope);
19697     },
19698
19699     /**
19700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19701      * (e.g., during paging).
19702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19703      */
19704     getModifiedRecords : function(){
19705         return this.modified;
19706     },
19707
19708     // private
19709     createFilterFn : function(property, value, anyMatch){
19710         if(!value.exec){ // not a regex
19711             value = String(value);
19712             if(value.length == 0){
19713                 return false;
19714             }
19715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19716         }
19717         return function(r){
19718             return value.test(r.data[property]);
19719         };
19720     },
19721
19722     /**
19723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19724      * @param {String} property A field on your records
19725      * @param {Number} start The record index to start at (defaults to 0)
19726      * @param {Number} end The last record index to include (defaults to length - 1)
19727      * @return {Number} The sum
19728      */
19729     sum : function(property, start, end){
19730         var rs = this.data.items, v = 0;
19731         start = start || 0;
19732         end = (end || end === 0) ? end : rs.length-1;
19733
19734         for(var i = start; i <= end; i++){
19735             v += (rs[i].data[property] || 0);
19736         }
19737         return v;
19738     },
19739
19740     /**
19741      * Filter the records by a specified property.
19742      * @param {String} field A field on your records
19743      * @param {String/RegExp} value Either a string that the field
19744      * should start with or a RegExp to test against the field
19745      * @param {Boolean} anyMatch True to match any part not just the beginning
19746      */
19747     filter : function(property, value, anyMatch){
19748         var fn = this.createFilterFn(property, value, anyMatch);
19749         return fn ? this.filterBy(fn) : this.clearFilter();
19750     },
19751
19752     /**
19753      * Filter by a function. The specified function will be called with each
19754      * record in this data source. If the function returns true the record is included,
19755      * otherwise it is filtered.
19756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19757      * @param {Object} scope (optional) The scope of the function (defaults to this)
19758      */
19759     filterBy : function(fn, scope){
19760         this.snapshot = this.snapshot || this.data;
19761         this.data = this.queryBy(fn, scope||this);
19762         this.fireEvent("datachanged", this);
19763     },
19764
19765     /**
19766      * Query the records by a specified property.
19767      * @param {String} field A field on your records
19768      * @param {String/RegExp} value Either a string that the field
19769      * should start with or a RegExp to test against the field
19770      * @param {Boolean} anyMatch True to match any part not just the beginning
19771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19772      */
19773     query : function(property, value, anyMatch){
19774         var fn = this.createFilterFn(property, value, anyMatch);
19775         return fn ? this.queryBy(fn) : this.data.clone();
19776     },
19777
19778     /**
19779      * Query by a function. The specified function will be called with each
19780      * record in this data source. If the function returns true the record is included
19781      * in the results.
19782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19783      * @param {Object} scope (optional) The scope of the function (defaults to this)
19784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19785      **/
19786     queryBy : function(fn, scope){
19787         var data = this.snapshot || this.data;
19788         return data.filterBy(fn, scope||this);
19789     },
19790
19791     /**
19792      * Collects unique values for a particular dataIndex from this store.
19793      * @param {String} dataIndex The property to collect
19794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19796      * @return {Array} An array of the unique values
19797      **/
19798     collect : function(dataIndex, allowNull, bypassFilter){
19799         var d = (bypassFilter === true && this.snapshot) ?
19800                 this.snapshot.items : this.data.items;
19801         var v, sv, r = [], l = {};
19802         for(var i = 0, len = d.length; i < len; i++){
19803             v = d[i].data[dataIndex];
19804             sv = String(v);
19805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19806                 l[sv] = true;
19807                 r[r.length] = v;
19808             }
19809         }
19810         return r;
19811     },
19812
19813     /**
19814      * Revert to a view of the Record cache with no filtering applied.
19815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19816      */
19817     clearFilter : function(suppressEvent){
19818         if(this.snapshot && this.snapshot != this.data){
19819             this.data = this.snapshot;
19820             delete this.snapshot;
19821             if(suppressEvent !== true){
19822                 this.fireEvent("datachanged", this);
19823             }
19824         }
19825     },
19826
19827     // private
19828     afterEdit : function(record){
19829         if(this.modified.indexOf(record) == -1){
19830             this.modified.push(record);
19831         }
19832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19833     },
19834     
19835     // private
19836     afterReject : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19839     },
19840
19841     // private
19842     afterCommit : function(record){
19843         this.modified.remove(record);
19844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19845     },
19846
19847     /**
19848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19850      */
19851     commitChanges : function(){
19852         var m = this.modified.slice(0);
19853         this.modified = [];
19854         for(var i = 0, len = m.length; i < len; i++){
19855             m[i].commit();
19856         }
19857     },
19858
19859     /**
19860      * Cancel outstanding changes on all changed records.
19861      */
19862     rejectChanges : function(){
19863         var m = this.modified.slice(0);
19864         this.modified = [];
19865         for(var i = 0, len = m.length; i < len; i++){
19866             m[i].reject();
19867         }
19868     },
19869
19870     onMetaChange : function(meta, rtype, o){
19871         this.recordType = rtype;
19872         this.fields = rtype.prototype.fields;
19873         delete this.snapshot;
19874         this.sortInfo = meta.sortInfo || this.sortInfo;
19875         this.modified = [];
19876         this.fireEvent('metachange', this, this.reader.meta);
19877     }
19878 });/*
19879  * Based on:
19880  * Ext JS Library 1.1.1
19881  * Copyright(c) 2006-2007, Ext JS, LLC.
19882  *
19883  * Originally Released Under LGPL - original licence link has changed is not relivant.
19884  *
19885  * Fork - LGPL
19886  * <script type="text/javascript">
19887  */
19888
19889 /**
19890  * @class Roo.data.SimpleStore
19891  * @extends Roo.data.Store
19892  * Small helper class to make creating Stores from Array data easier.
19893  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19894  * @cfg {Array} fields An array of field definition objects, or field name strings.
19895  * @cfg {Array} data The multi-dimensional array of data
19896  * @constructor
19897  * @param {Object} config
19898  */
19899 Roo.data.SimpleStore = function(config){
19900     Roo.data.SimpleStore.superclass.constructor.call(this, {
19901         isLocal : true,
19902         reader: new Roo.data.ArrayReader({
19903                 id: config.id
19904             },
19905             Roo.data.Record.create(config.fields)
19906         ),
19907         proxy : new Roo.data.MemoryProxy(config.data)
19908     });
19909     this.load();
19910 };
19911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19912  * Based on:
19913  * Ext JS Library 1.1.1
19914  * Copyright(c) 2006-2007, Ext JS, LLC.
19915  *
19916  * Originally Released Under LGPL - original licence link has changed is not relivant.
19917  *
19918  * Fork - LGPL
19919  * <script type="text/javascript">
19920  */
19921
19922 /**
19923 /**
19924  * @extends Roo.data.Store
19925  * @class Roo.data.JsonStore
19926  * Small helper class to make creating Stores for JSON data easier. <br/>
19927 <pre><code>
19928 var store = new Roo.data.JsonStore({
19929     url: 'get-images.php',
19930     root: 'images',
19931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19932 });
19933 </code></pre>
19934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19935  * JsonReader and HttpProxy (unless inline data is provided).</b>
19936  * @cfg {Array} fields An array of field definition objects, or field name strings.
19937  * @constructor
19938  * @param {Object} config
19939  */
19940 Roo.data.JsonStore = function(c){
19941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19943         reader: new Roo.data.JsonReader(c, c.fields)
19944     }));
19945 };
19946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19947  * Based on:
19948  * Ext JS Library 1.1.1
19949  * Copyright(c) 2006-2007, Ext JS, LLC.
19950  *
19951  * Originally Released Under LGPL - original licence link has changed is not relivant.
19952  *
19953  * Fork - LGPL
19954  * <script type="text/javascript">
19955  */
19956
19957  
19958 Roo.data.Field = function(config){
19959     if(typeof config == "string"){
19960         config = {name: config};
19961     }
19962     Roo.apply(this, config);
19963     
19964     if(!this.type){
19965         this.type = "auto";
19966     }
19967     
19968     var st = Roo.data.SortTypes;
19969     // named sortTypes are supported, here we look them up
19970     if(typeof this.sortType == "string"){
19971         this.sortType = st[this.sortType];
19972     }
19973     
19974     // set default sortType for strings and dates
19975     if(!this.sortType){
19976         switch(this.type){
19977             case "string":
19978                 this.sortType = st.asUCString;
19979                 break;
19980             case "date":
19981                 this.sortType = st.asDate;
19982                 break;
19983             default:
19984                 this.sortType = st.none;
19985         }
19986     }
19987
19988     // define once
19989     var stripRe = /[\$,%]/g;
19990
19991     // prebuilt conversion function for this field, instead of
19992     // switching every time we're reading a value
19993     if(!this.convert){
19994         var cv, dateFormat = this.dateFormat;
19995         switch(this.type){
19996             case "":
19997             case "auto":
19998             case undefined:
19999                 cv = function(v){ return v; };
20000                 break;
20001             case "string":
20002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20003                 break;
20004             case "int":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20008                     };
20009                 break;
20010             case "float":
20011                 cv = function(v){
20012                     return v !== undefined && v !== null && v !== '' ?
20013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20014                     };
20015                 break;
20016             case "bool":
20017             case "boolean":
20018                 cv = function(v){ return v === true || v === "true" || v == 1; };
20019                 break;
20020             case "date":
20021                 cv = function(v){
20022                     if(!v){
20023                         return '';
20024                     }
20025                     if(v instanceof Date){
20026                         return v;
20027                     }
20028                     if(dateFormat){
20029                         if(dateFormat == "timestamp"){
20030                             return new Date(v*1000);
20031                         }
20032                         return Date.parseDate(v, dateFormat);
20033                     }
20034                     var parsed = Date.parse(v);
20035                     return parsed ? new Date(parsed) : null;
20036                 };
20037              break;
20038             
20039         }
20040         this.convert = cv;
20041     }
20042 };
20043
20044 Roo.data.Field.prototype = {
20045     dateFormat: null,
20046     defaultValue: "",
20047     mapping: null,
20048     sortType : null,
20049     sortDir : "ASC"
20050 };/*
20051  * Based on:
20052  * Ext JS Library 1.1.1
20053  * Copyright(c) 2006-2007, Ext JS, LLC.
20054  *
20055  * Originally Released Under LGPL - original licence link has changed is not relivant.
20056  *
20057  * Fork - LGPL
20058  * <script type="text/javascript">
20059  */
20060  
20061 // Base class for reading structured data from a data source.  This class is intended to be
20062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20063
20064 /**
20065  * @class Roo.data.DataReader
20066  * Base class for reading structured data from a data source.  This class is intended to be
20067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20068  */
20069
20070 Roo.data.DataReader = function(meta, recordType){
20071     
20072     this.meta = meta;
20073     
20074     this.recordType = recordType instanceof Array ? 
20075         Roo.data.Record.create(recordType) : recordType;
20076 };
20077
20078 Roo.data.DataReader.prototype = {
20079      /**
20080      * Create an empty record
20081      * @param {Object} data (optional) - overlay some values
20082      * @return {Roo.data.Record} record created.
20083      */
20084     newRow :  function(d) {
20085         var da =  {};
20086         this.recordType.prototype.fields.each(function(c) {
20087             switch( c.type) {
20088                 case 'int' : da[c.name] = 0; break;
20089                 case 'date' : da[c.name] = new Date(); break;
20090                 case 'float' : da[c.name] = 0.0; break;
20091                 case 'boolean' : da[c.name] = false; break;
20092                 default : da[c.name] = ""; break;
20093             }
20094             
20095         });
20096         return new this.recordType(Roo.apply(da, d));
20097     }
20098     
20099 };/*
20100  * Based on:
20101  * Ext JS Library 1.1.1
20102  * Copyright(c) 2006-2007, Ext JS, LLC.
20103  *
20104  * Originally Released Under LGPL - original licence link has changed is not relivant.
20105  *
20106  * Fork - LGPL
20107  * <script type="text/javascript">
20108  */
20109
20110 /**
20111  * @class Roo.data.DataProxy
20112  * @extends Roo.data.Observable
20113  * This class is an abstract base class for implementations which provide retrieval of
20114  * unformatted data objects.<br>
20115  * <p>
20116  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20117  * (of the appropriate type which knows how to parse the data object) to provide a block of
20118  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20119  * <p>
20120  * Custom implementations must implement the load method as described in
20121  * {@link Roo.data.HttpProxy#load}.
20122  */
20123 Roo.data.DataProxy = function(){
20124     this.addEvents({
20125         /**
20126          * @event beforeload
20127          * Fires before a network request is made to retrieve a data object.
20128          * @param {Object} This DataProxy object.
20129          * @param {Object} params The params parameter to the load function.
20130          */
20131         beforeload : true,
20132         /**
20133          * @event load
20134          * Fires before the load method's callback is called.
20135          * @param {Object} This DataProxy object.
20136          * @param {Object} o The data object.
20137          * @param {Object} arg The callback argument object passed to the load function.
20138          */
20139         load : true,
20140         /**
20141          * @event loadexception
20142          * Fires if an Exception occurs during data retrieval.
20143          * @param {Object} This DataProxy object.
20144          * @param {Object} o The data object.
20145          * @param {Object} arg The callback argument object passed to the load function.
20146          * @param {Object} e The Exception.
20147          */
20148         loadexception : true
20149     });
20150     Roo.data.DataProxy.superclass.constructor.call(this);
20151 };
20152
20153 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20154
20155     /**
20156      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20157      */
20158 /*
20159  * Based on:
20160  * Ext JS Library 1.1.1
20161  * Copyright(c) 2006-2007, Ext JS, LLC.
20162  *
20163  * Originally Released Under LGPL - original licence link has changed is not relivant.
20164  *
20165  * Fork - LGPL
20166  * <script type="text/javascript">
20167  */
20168 /**
20169  * @class Roo.data.MemoryProxy
20170  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20171  * to the Reader when its load method is called.
20172  * @constructor
20173  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20174  */
20175 Roo.data.MemoryProxy = function(data){
20176     if (data.data) {
20177         data = data.data;
20178     }
20179     Roo.data.MemoryProxy.superclass.constructor.call(this);
20180     this.data = data;
20181 };
20182
20183 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20184     /**
20185      * Load data from the requested source (in this case an in-memory
20186      * data object passed to the constructor), read the data object into
20187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20188      * process that block using the passed callback.
20189      * @param {Object} params This parameter is not used by the MemoryProxy class.
20190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20191      * object into a block of Roo.data.Records.
20192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20193      * The function must be passed <ul>
20194      * <li>The Record block object</li>
20195      * <li>The "arg" argument from the load function</li>
20196      * <li>A boolean success indicator</li>
20197      * </ul>
20198      * @param {Object} scope The scope in which to call the callback
20199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20200      */
20201     load : function(params, reader, callback, scope, arg){
20202         params = params || {};
20203         var result;
20204         try {
20205             result = reader.readRecords(this.data);
20206         }catch(e){
20207             this.fireEvent("loadexception", this, arg, null, e);
20208             callback.call(scope, null, arg, false);
20209             return;
20210         }
20211         callback.call(scope, result, arg, true);
20212     },
20213     
20214     // private
20215     update : function(params, records){
20216         
20217     }
20218 });/*
20219  * Based on:
20220  * Ext JS Library 1.1.1
20221  * Copyright(c) 2006-2007, Ext JS, LLC.
20222  *
20223  * Originally Released Under LGPL - original licence link has changed is not relivant.
20224  *
20225  * Fork - LGPL
20226  * <script type="text/javascript">
20227  */
20228 /**
20229  * @class Roo.data.HttpProxy
20230  * @extends Roo.data.DataProxy
20231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20232  * configured to reference a certain URL.<br><br>
20233  * <p>
20234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20235  * from which the running page was served.<br><br>
20236  * <p>
20237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20238  * <p>
20239  * Be aware that to enable the browser to parse an XML document, the server must set
20240  * the Content-Type header in the HTTP response to "text/xml".
20241  * @constructor
20242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20244  * will be used to make the request.
20245  */
20246 Roo.data.HttpProxy = function(conn){
20247     Roo.data.HttpProxy.superclass.constructor.call(this);
20248     // is conn a conn config or a real conn?
20249     this.conn = conn;
20250     this.useAjax = !conn || !conn.events;
20251   
20252 };
20253
20254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20255     // thse are take from connection...
20256     
20257     /**
20258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20259      */
20260     /**
20261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20262      * extra parameters to each request made by this object. (defaults to undefined)
20263      */
20264     /**
20265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20266      *  to each request made by this object. (defaults to undefined)
20267      */
20268     /**
20269      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20270      */
20271     /**
20272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20273      */
20274      /**
20275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20276      * @type Boolean
20277      */
20278   
20279
20280     /**
20281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20282      * @type Boolean
20283      */
20284     /**
20285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20287      * a finer-grained basis than the DataProxy events.
20288      */
20289     getConnection : function(){
20290         return this.useAjax ? Roo.Ajax : this.conn;
20291     },
20292
20293     /**
20294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20296      * process that block using the passed callback.
20297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20298      * for the request to the remote server.
20299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20300      * object into a block of Roo.data.Records.
20301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20302      * The function must be passed <ul>
20303      * <li>The Record block object</li>
20304      * <li>The "arg" argument from the load function</li>
20305      * <li>A boolean success indicator</li>
20306      * </ul>
20307      * @param {Object} scope The scope in which to call the callback
20308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20309      */
20310     load : function(params, reader, callback, scope, arg){
20311         if(this.fireEvent("beforeload", this, params) !== false){
20312             var  o = {
20313                 params : params || {},
20314                 request: {
20315                     callback : callback,
20316                     scope : scope,
20317                     arg : arg
20318                 },
20319                 reader: reader,
20320                 callback : this.loadResponse,
20321                 scope: this
20322             };
20323             if(this.useAjax){
20324                 Roo.applyIf(o, this.conn);
20325                 if(this.activeRequest){
20326                     Roo.Ajax.abort(this.activeRequest);
20327                 }
20328                 this.activeRequest = Roo.Ajax.request(o);
20329             }else{
20330                 this.conn.request(o);
20331             }
20332         }else{
20333             callback.call(scope||this, null, arg, false);
20334         }
20335     },
20336
20337     // private
20338     loadResponse : function(o, success, response){
20339         delete this.activeRequest;
20340         if(!success){
20341             this.fireEvent("loadexception", this, o, response);
20342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20343             return;
20344         }
20345         var result;
20346         try {
20347             result = o.reader.read(response);
20348         }catch(e){
20349             this.fireEvent("loadexception", this, o, response, e);
20350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20351             return;
20352         }
20353         
20354         this.fireEvent("load", this, o, o.request.arg);
20355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20356     },
20357
20358     // private
20359     update : function(dataSet){
20360
20361     },
20362
20363     // private
20364     updateResponse : function(dataSet){
20365
20366     }
20367 });/*
20368  * Based on:
20369  * Ext JS Library 1.1.1
20370  * Copyright(c) 2006-2007, Ext JS, LLC.
20371  *
20372  * Originally Released Under LGPL - original licence link has changed is not relivant.
20373  *
20374  * Fork - LGPL
20375  * <script type="text/javascript">
20376  */
20377
20378 /**
20379  * @class Roo.data.ScriptTagProxy
20380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20381  * other than the originating domain of the running page.<br><br>
20382  * <p>
20383  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20385  * <p>
20386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20387  * source code that is used as the source inside a &lt;script> tag.<br><br>
20388  * <p>
20389  * In order for the browser to process the returned data, the server must wrap the data object
20390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20392  * depending on whether the callback name was passed:
20393  * <p>
20394  * <pre><code>
20395 boolean scriptTag = false;
20396 String cb = request.getParameter("callback");
20397 if (cb != null) {
20398     scriptTag = true;
20399     response.setContentType("text/javascript");
20400 } else {
20401     response.setContentType("application/x-json");
20402 }
20403 Writer out = response.getWriter();
20404 if (scriptTag) {
20405     out.write(cb + "(");
20406 }
20407 out.print(dataBlock.toJsonString());
20408 if (scriptTag) {
20409     out.write(");");
20410 }
20411 </pre></code>
20412  *
20413  * @constructor
20414  * @param {Object} config A configuration object.
20415  */
20416 Roo.data.ScriptTagProxy = function(config){
20417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20418     Roo.apply(this, config);
20419     this.head = document.getElementsByTagName("head")[0];
20420 };
20421
20422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20423
20424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20425     /**
20426      * @cfg {String} url The URL from which to request the data object.
20427      */
20428     /**
20429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20430      */
20431     timeout : 30000,
20432     /**
20433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20434      * the server the name of the callback function set up by the load call to process the returned data object.
20435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20436      * javascript output which calls this named function passing the data object as its only parameter.
20437      */
20438     callbackParam : "callback",
20439     /**
20440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20441      * name to the request.
20442      */
20443     nocache : true,
20444
20445     /**
20446      * Load data from the configured URL, read the data object into
20447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20448      * process that block using the passed callback.
20449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20450      * for the request to the remote server.
20451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20452      * object into a block of Roo.data.Records.
20453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20454      * The function must be passed <ul>
20455      * <li>The Record block object</li>
20456      * <li>The "arg" argument from the load function</li>
20457      * <li>A boolean success indicator</li>
20458      * </ul>
20459      * @param {Object} scope The scope in which to call the callback
20460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20461      */
20462     load : function(params, reader, callback, scope, arg){
20463         if(this.fireEvent("beforeload", this, params) !== false){
20464
20465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20466
20467             var url = this.url;
20468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20469             if(this.nocache){
20470                 url += "&_dc=" + (new Date().getTime());
20471             }
20472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20473             var trans = {
20474                 id : transId,
20475                 cb : "stcCallback"+transId,
20476                 scriptId : "stcScript"+transId,
20477                 params : params,
20478                 arg : arg,
20479                 url : url,
20480                 callback : callback,
20481                 scope : scope,
20482                 reader : reader
20483             };
20484             var conn = this;
20485
20486             window[trans.cb] = function(o){
20487                 conn.handleResponse(o, trans);
20488             };
20489
20490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20491
20492             if(this.autoAbort !== false){
20493                 this.abort();
20494             }
20495
20496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20497
20498             var script = document.createElement("script");
20499             script.setAttribute("src", url);
20500             script.setAttribute("type", "text/javascript");
20501             script.setAttribute("id", trans.scriptId);
20502             this.head.appendChild(script);
20503
20504             this.trans = trans;
20505         }else{
20506             callback.call(scope||this, null, arg, false);
20507         }
20508     },
20509
20510     // private
20511     isLoading : function(){
20512         return this.trans ? true : false;
20513     },
20514
20515     /**
20516      * Abort the current server request.
20517      */
20518     abort : function(){
20519         if(this.isLoading()){
20520             this.destroyTrans(this.trans);
20521         }
20522     },
20523
20524     // private
20525     destroyTrans : function(trans, isLoaded){
20526         this.head.removeChild(document.getElementById(trans.scriptId));
20527         clearTimeout(trans.timeoutId);
20528         if(isLoaded){
20529             window[trans.cb] = undefined;
20530             try{
20531                 delete window[trans.cb];
20532             }catch(e){}
20533         }else{
20534             // if hasn't been loaded, wait for load to remove it to prevent script error
20535             window[trans.cb] = function(){
20536                 window[trans.cb] = undefined;
20537                 try{
20538                     delete window[trans.cb];
20539                 }catch(e){}
20540             };
20541         }
20542     },
20543
20544     // private
20545     handleResponse : function(o, trans){
20546         this.trans = false;
20547         this.destroyTrans(trans, true);
20548         var result;
20549         try {
20550             result = trans.reader.readRecords(o);
20551         }catch(e){
20552             this.fireEvent("loadexception", this, o, trans.arg, e);
20553             trans.callback.call(trans.scope||window, null, trans.arg, false);
20554             return;
20555         }
20556         this.fireEvent("load", this, o, trans.arg);
20557         trans.callback.call(trans.scope||window, result, trans.arg, true);
20558     },
20559
20560     // private
20561     handleFailure : function(trans){
20562         this.trans = false;
20563         this.destroyTrans(trans, false);
20564         this.fireEvent("loadexception", this, null, trans.arg);
20565         trans.callback.call(trans.scope||window, null, trans.arg, false);
20566     }
20567 });/*
20568  * Based on:
20569  * Ext JS Library 1.1.1
20570  * Copyright(c) 2006-2007, Ext JS, LLC.
20571  *
20572  * Originally Released Under LGPL - original licence link has changed is not relivant.
20573  *
20574  * Fork - LGPL
20575  * <script type="text/javascript">
20576  */
20577
20578 /**
20579  * @class Roo.data.JsonReader
20580  * @extends Roo.data.DataReader
20581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20582  * based on mappings in a provided Roo.data.Record constructor.
20583  * 
20584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20585  * in the reply previously. 
20586  * 
20587  * <p>
20588  * Example code:
20589  * <pre><code>
20590 var RecordDef = Roo.data.Record.create([
20591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20593 ]);
20594 var myReader = new Roo.data.JsonReader({
20595     totalProperty: "results",    // The property which contains the total dataset size (optional)
20596     root: "rows",                // The property which contains an Array of row objects
20597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20598 }, RecordDef);
20599 </code></pre>
20600  * <p>
20601  * This would consume a JSON file like this:
20602  * <pre><code>
20603 { 'results': 2, 'rows': [
20604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20606 }
20607 </code></pre>
20608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20610  * paged from the remote server.
20611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20612  * @cfg {String} root name of the property which contains the Array of row objects.
20613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20614  * @constructor
20615  * Create a new JsonReader
20616  * @param {Object} meta Metadata configuration options
20617  * @param {Object} recordType Either an Array of field definition objects,
20618  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20619  */
20620 Roo.data.JsonReader = function(meta, recordType){
20621     
20622     meta = meta || {};
20623     // set some defaults:
20624     Roo.applyIf(meta, {
20625         totalProperty: 'total',
20626         successProperty : 'success',
20627         root : 'data',
20628         id : 'id'
20629     });
20630     
20631     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20632 };
20633 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20634     
20635     /**
20636      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20637      * Used by Store query builder to append _requestMeta to params.
20638      * 
20639      */
20640     metaFromRemote : false,
20641     /**
20642      * This method is only used by a DataProxy which has retrieved data from a remote server.
20643      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20644      * @return {Object} data A data block which is used by an Roo.data.Store object as
20645      * a cache of Roo.data.Records.
20646      */
20647     read : function(response){
20648         var json = response.responseText;
20649        
20650         var o = /* eval:var:o */ eval("("+json+")");
20651         if(!o) {
20652             throw {message: "JsonReader.read: Json object not found"};
20653         }
20654         
20655         if(o.metaData){
20656             
20657             delete this.ef;
20658             this.metaFromRemote = true;
20659             this.meta = o.metaData;
20660             this.recordType = Roo.data.Record.create(o.metaData.fields);
20661             this.onMetaChange(this.meta, this.recordType, o);
20662         }
20663         return this.readRecords(o);
20664     },
20665
20666     // private function a store will implement
20667     onMetaChange : function(meta, recordType, o){
20668
20669     },
20670
20671     /**
20672          * @ignore
20673          */
20674     simpleAccess: function(obj, subsc) {
20675         return obj[subsc];
20676     },
20677
20678         /**
20679          * @ignore
20680          */
20681     getJsonAccessor: function(){
20682         var re = /[\[\.]/;
20683         return function(expr) {
20684             try {
20685                 return(re.test(expr))
20686                     ? new Function("obj", "return obj." + expr)
20687                     : function(obj){
20688                         return obj[expr];
20689                     };
20690             } catch(e){}
20691             return Roo.emptyFn;
20692         };
20693     }(),
20694
20695     /**
20696      * Create a data block containing Roo.data.Records from an XML document.
20697      * @param {Object} o An object which contains an Array of row objects in the property specified
20698      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20699      * which contains the total size of the dataset.
20700      * @return {Object} data A data block which is used by an Roo.data.Store object as
20701      * a cache of Roo.data.Records.
20702      */
20703     readRecords : function(o){
20704         /**
20705          * After any data loads, the raw JSON data is available for further custom processing.
20706          * @type Object
20707          */
20708         this.jsonData = o;
20709         var s = this.meta, Record = this.recordType,
20710             f = Record.prototype.fields, fi = f.items, fl = f.length;
20711
20712 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20713         if (!this.ef) {
20714             if(s.totalProperty) {
20715                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20716                 }
20717                 if(s.successProperty) {
20718                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20719                 }
20720                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20721                 if (s.id) {
20722                         var g = this.getJsonAccessor(s.id);
20723                         this.getId = function(rec) {
20724                                 var r = g(rec);
20725                                 return (r === undefined || r === "") ? null : r;
20726                         };
20727                 } else {
20728                         this.getId = function(){return null;};
20729                 }
20730             this.ef = [];
20731             for(var jj = 0; jj < fl; jj++){
20732                 f = fi[jj];
20733                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20734                 this.ef[jj] = this.getJsonAccessor(map);
20735             }
20736         }
20737
20738         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20739         if(s.totalProperty){
20740             var vt = parseInt(this.getTotal(o), 10);
20741             if(!isNaN(vt)){
20742                 totalRecords = vt;
20743             }
20744         }
20745         if(s.successProperty){
20746             var vs = this.getSuccess(o);
20747             if(vs === false || vs === 'false'){
20748                 success = false;
20749             }
20750         }
20751         var records = [];
20752             for(var i = 0; i < c; i++){
20753                     var n = root[i];
20754                 var values = {};
20755                 var id = this.getId(n);
20756                 for(var j = 0; j < fl; j++){
20757                     f = fi[j];
20758                 var v = this.ef[j](n);
20759                 if (!f.convert) {
20760                     Roo.log('missing convert for ' + f.name);
20761                     Roo.log(f);
20762                     continue;
20763                 }
20764                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20765                 }
20766                 var record = new Record(values, id);
20767                 record.json = n;
20768                 records[i] = record;
20769             }
20770             return {
20771                 success : success,
20772                 records : records,
20773                 totalRecords : totalRecords
20774             };
20775     }
20776 });/*
20777  * Based on:
20778  * Ext JS Library 1.1.1
20779  * Copyright(c) 2006-2007, Ext JS, LLC.
20780  *
20781  * Originally Released Under LGPL - original licence link has changed is not relivant.
20782  *
20783  * Fork - LGPL
20784  * <script type="text/javascript">
20785  */
20786
20787 /**
20788  * @class Roo.data.XmlReader
20789  * @extends Roo.data.DataReader
20790  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20791  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20792  * <p>
20793  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20794  * header in the HTTP response must be set to "text/xml".</em>
20795  * <p>
20796  * Example code:
20797  * <pre><code>
20798 var RecordDef = Roo.data.Record.create([
20799    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20800    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20801 ]);
20802 var myReader = new Roo.data.XmlReader({
20803    totalRecords: "results", // The element which contains the total dataset size (optional)
20804    record: "row",           // The repeated element which contains row information
20805    id: "id"                 // The element within the row that provides an ID for the record (optional)
20806 }, RecordDef);
20807 </code></pre>
20808  * <p>
20809  * This would consume an XML file like this:
20810  * <pre><code>
20811 &lt;?xml?>
20812 &lt;dataset>
20813  &lt;results>2&lt;/results>
20814  &lt;row>
20815    &lt;id>1&lt;/id>
20816    &lt;name>Bill&lt;/name>
20817    &lt;occupation>Gardener&lt;/occupation>
20818  &lt;/row>
20819  &lt;row>
20820    &lt;id>2&lt;/id>
20821    &lt;name>Ben&lt;/name>
20822    &lt;occupation>Horticulturalist&lt;/occupation>
20823  &lt;/row>
20824 &lt;/dataset>
20825 </code></pre>
20826  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20827  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20828  * paged from the remote server.
20829  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20830  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20831  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20832  * a record identifier value.
20833  * @constructor
20834  * Create a new XmlReader
20835  * @param {Object} meta Metadata configuration options
20836  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20837  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20838  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20839  */
20840 Roo.data.XmlReader = function(meta, recordType){
20841     meta = meta || {};
20842     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20843 };
20844 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20845     /**
20846      * This method is only used by a DataProxy which has retrieved data from a remote server.
20847          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20848          * to contain a method called 'responseXML' that returns an XML document object.
20849      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20850      * a cache of Roo.data.Records.
20851      */
20852     read : function(response){
20853         var doc = response.responseXML;
20854         if(!doc) {
20855             throw {message: "XmlReader.read: XML Document not available"};
20856         }
20857         return this.readRecords(doc);
20858     },
20859
20860     /**
20861      * Create a data block containing Roo.data.Records from an XML document.
20862          * @param {Object} doc A parsed XML document.
20863      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20864      * a cache of Roo.data.Records.
20865      */
20866     readRecords : function(doc){
20867         /**
20868          * After any data loads/reads, the raw XML Document is available for further custom processing.
20869          * @type XMLDocument
20870          */
20871         this.xmlData = doc;
20872         var root = doc.documentElement || doc;
20873         var q = Roo.DomQuery;
20874         var recordType = this.recordType, fields = recordType.prototype.fields;
20875         var sid = this.meta.id;
20876         var totalRecords = 0, success = true;
20877         if(this.meta.totalRecords){
20878             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20879         }
20880         
20881         if(this.meta.success){
20882             var sv = q.selectValue(this.meta.success, root, true);
20883             success = sv !== false && sv !== 'false';
20884         }
20885         var records = [];
20886         var ns = q.select(this.meta.record, root);
20887         for(var i = 0, len = ns.length; i < len; i++) {
20888                 var n = ns[i];
20889                 var values = {};
20890                 var id = sid ? q.selectValue(sid, n) : undefined;
20891                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20892                     var f = fields.items[j];
20893                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20894                     v = f.convert(v);
20895                     values[f.name] = v;
20896                 }
20897                 var record = new recordType(values, id);
20898                 record.node = n;
20899                 records[records.length] = record;
20900             }
20901
20902             return {
20903                 success : success,
20904                 records : records,
20905                 totalRecords : totalRecords || records.length
20906             };
20907     }
20908 });/*
20909  * Based on:
20910  * Ext JS Library 1.1.1
20911  * Copyright(c) 2006-2007, Ext JS, LLC.
20912  *
20913  * Originally Released Under LGPL - original licence link has changed is not relivant.
20914  *
20915  * Fork - LGPL
20916  * <script type="text/javascript">
20917  */
20918
20919 /**
20920  * @class Roo.data.ArrayReader
20921  * @extends Roo.data.DataReader
20922  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20923  * Each element of that Array represents a row of data fields. The
20924  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20925  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20926  * <p>
20927  * Example code:.
20928  * <pre><code>
20929 var RecordDef = Roo.data.Record.create([
20930     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20931     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20932 ]);
20933 var myReader = new Roo.data.ArrayReader({
20934     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20935 }, RecordDef);
20936 </code></pre>
20937  * <p>
20938  * This would consume an Array like this:
20939  * <pre><code>
20940 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20941   </code></pre>
20942  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20943  * @constructor
20944  * Create a new JsonReader
20945  * @param {Object} meta Metadata configuration options.
20946  * @param {Object} recordType Either an Array of field definition objects
20947  * as specified to {@link Roo.data.Record#create},
20948  * or an {@link Roo.data.Record} object
20949  * created using {@link Roo.data.Record#create}.
20950  */
20951 Roo.data.ArrayReader = function(meta, recordType){
20952     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20953 };
20954
20955 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20956     /**
20957      * Create a data block containing Roo.data.Records from an XML document.
20958      * @param {Object} o An Array of row objects which represents the dataset.
20959      * @return {Object} data A data block which is used by an Roo.data.Store object as
20960      * a cache of Roo.data.Records.
20961      */
20962     readRecords : function(o){
20963         var sid = this.meta ? this.meta.id : null;
20964         var recordType = this.recordType, fields = recordType.prototype.fields;
20965         var records = [];
20966         var root = o;
20967             for(var i = 0; i < root.length; i++){
20968                     var n = root[i];
20969                 var values = {};
20970                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20971                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20972                 var f = fields.items[j];
20973                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20974                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20975                 v = f.convert(v);
20976                 values[f.name] = v;
20977             }
20978                 var record = new recordType(values, id);
20979                 record.json = n;
20980                 records[records.length] = record;
20981             }
20982             return {
20983                 records : records,
20984                 totalRecords : records.length
20985             };
20986     }
20987 });/*
20988  * Based on:
20989  * Ext JS Library 1.1.1
20990  * Copyright(c) 2006-2007, Ext JS, LLC.
20991  *
20992  * Originally Released Under LGPL - original licence link has changed is not relivant.
20993  *
20994  * Fork - LGPL
20995  * <script type="text/javascript">
20996  */
20997
20998
20999 /**
21000  * @class Roo.data.Tree
21001  * @extends Roo.util.Observable
21002  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21003  * in the tree have most standard DOM functionality.
21004  * @constructor
21005  * @param {Node} root (optional) The root node
21006  */
21007 Roo.data.Tree = function(root){
21008    this.nodeHash = {};
21009    /**
21010     * The root node for this tree
21011     * @type Node
21012     */
21013    this.root = null;
21014    if(root){
21015        this.setRootNode(root);
21016    }
21017    this.addEvents({
21018        /**
21019         * @event append
21020         * Fires when a new child node is appended to a node in this tree.
21021         * @param {Tree} tree The owner tree
21022         * @param {Node} parent The parent node
21023         * @param {Node} node The newly appended node
21024         * @param {Number} index The index of the newly appended node
21025         */
21026        "append" : true,
21027        /**
21028         * @event remove
21029         * Fires when a child node is removed from a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The child node removed
21033         */
21034        "remove" : true,
21035        /**
21036         * @event move
21037         * Fires when a node is moved to a new location in the tree
21038         * @param {Tree} tree The owner tree
21039         * @param {Node} node The node moved
21040         * @param {Node} oldParent The old parent of this node
21041         * @param {Node} newParent The new parent of this node
21042         * @param {Number} index The index it was moved to
21043         */
21044        "move" : true,
21045        /**
21046         * @event insert
21047         * Fires when a new child node is inserted in a node in this tree.
21048         * @param {Tree} tree The owner tree
21049         * @param {Node} parent The parent node
21050         * @param {Node} node The child node inserted
21051         * @param {Node} refNode The child node the node was inserted before
21052         */
21053        "insert" : true,
21054        /**
21055         * @event beforeappend
21056         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node to be appended
21060         */
21061        "beforeappend" : true,
21062        /**
21063         * @event beforeremove
21064         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21065         * @param {Tree} tree The owner tree
21066         * @param {Node} parent The parent node
21067         * @param {Node} node The child node to be removed
21068         */
21069        "beforeremove" : true,
21070        /**
21071         * @event beforemove
21072         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21073         * @param {Tree} tree The owner tree
21074         * @param {Node} node The node being moved
21075         * @param {Node} oldParent The parent of the node
21076         * @param {Node} newParent The new parent the node is moving to
21077         * @param {Number} index The index it is being moved to
21078         */
21079        "beforemove" : true,
21080        /**
21081         * @event beforeinsert
21082         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21083         * @param {Tree} tree The owner tree
21084         * @param {Node} parent The parent node
21085         * @param {Node} node The child node to be inserted
21086         * @param {Node} refNode The child node the node is being inserted before
21087         */
21088        "beforeinsert" : true
21089    });
21090
21091     Roo.data.Tree.superclass.constructor.call(this);
21092 };
21093
21094 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21095     pathSeparator: "/",
21096
21097     proxyNodeEvent : function(){
21098         return this.fireEvent.apply(this, arguments);
21099     },
21100
21101     /**
21102      * Returns the root node for this tree.
21103      * @return {Node}
21104      */
21105     getRootNode : function(){
21106         return this.root;
21107     },
21108
21109     /**
21110      * Sets the root node for this tree.
21111      * @param {Node} node
21112      * @return {Node}
21113      */
21114     setRootNode : function(node){
21115         this.root = node;
21116         node.ownerTree = this;
21117         node.isRoot = true;
21118         this.registerNode(node);
21119         return node;
21120     },
21121
21122     /**
21123      * Gets a node in this tree by its id.
21124      * @param {String} id
21125      * @return {Node}
21126      */
21127     getNodeById : function(id){
21128         return this.nodeHash[id];
21129     },
21130
21131     registerNode : function(node){
21132         this.nodeHash[node.id] = node;
21133     },
21134
21135     unregisterNode : function(node){
21136         delete this.nodeHash[node.id];
21137     },
21138
21139     toString : function(){
21140         return "[Tree"+(this.id?" "+this.id:"")+"]";
21141     }
21142 });
21143
21144 /**
21145  * @class Roo.data.Node
21146  * @extends Roo.util.Observable
21147  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21148  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21149  * @constructor
21150  * @param {Object} attributes The attributes/config for the node
21151  */
21152 Roo.data.Node = function(attributes){
21153     /**
21154      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21155      * @type {Object}
21156      */
21157     this.attributes = attributes || {};
21158     this.leaf = this.attributes.leaf;
21159     /**
21160      * The node id. @type String
21161      */
21162     this.id = this.attributes.id;
21163     if(!this.id){
21164         this.id = Roo.id(null, "ynode-");
21165         this.attributes.id = this.id;
21166     }
21167     /**
21168      * All child nodes of this node. @type Array
21169      */
21170     this.childNodes = [];
21171     if(!this.childNodes.indexOf){ // indexOf is a must
21172         this.childNodes.indexOf = function(o){
21173             for(var i = 0, len = this.length; i < len; i++){
21174                 if(this[i] == o) {
21175                     return i;
21176                 }
21177             }
21178             return -1;
21179         };
21180     }
21181     /**
21182      * The parent node for this node. @type Node
21183      */
21184     this.parentNode = null;
21185     /**
21186      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21187      */
21188     this.firstChild = null;
21189     /**
21190      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21191      */
21192     this.lastChild = null;
21193     /**
21194      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21195      */
21196     this.previousSibling = null;
21197     /**
21198      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21199      */
21200     this.nextSibling = null;
21201
21202     this.addEvents({
21203        /**
21204         * @event append
21205         * Fires when a new child node is appended
21206         * @param {Tree} tree The owner tree
21207         * @param {Node} this This node
21208         * @param {Node} node The newly appended node
21209         * @param {Number} index The index of the newly appended node
21210         */
21211        "append" : true,
21212        /**
21213         * @event remove
21214         * Fires when a child node is removed
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The removed node
21218         */
21219        "remove" : true,
21220        /**
21221         * @event move
21222         * Fires when this node is moved to a new location in the tree
21223         * @param {Tree} tree The owner tree
21224         * @param {Node} this This node
21225         * @param {Node} oldParent The old parent of this node
21226         * @param {Node} newParent The new parent of this node
21227         * @param {Number} index The index it was moved to
21228         */
21229        "move" : true,
21230        /**
21231         * @event insert
21232         * Fires when a new child node is inserted.
21233         * @param {Tree} tree The owner tree
21234         * @param {Node} this This node
21235         * @param {Node} node The child node inserted
21236         * @param {Node} refNode The child node the node was inserted before
21237         */
21238        "insert" : true,
21239        /**
21240         * @event beforeappend
21241         * Fires before a new child is appended, return false to cancel the append.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node to be appended
21245         */
21246        "beforeappend" : true,
21247        /**
21248         * @event beforeremove
21249         * Fires before a child is removed, return false to cancel the remove.
21250         * @param {Tree} tree The owner tree
21251         * @param {Node} this This node
21252         * @param {Node} node The child node to be removed
21253         */
21254        "beforeremove" : true,
21255        /**
21256         * @event beforemove
21257         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21258         * @param {Tree} tree The owner tree
21259         * @param {Node} this This node
21260         * @param {Node} oldParent The parent of this node
21261         * @param {Node} newParent The new parent this node is moving to
21262         * @param {Number} index The index it is being moved to
21263         */
21264        "beforemove" : true,
21265        /**
21266         * @event beforeinsert
21267         * Fires before a new child is inserted, return false to cancel the insert.
21268         * @param {Tree} tree The owner tree
21269         * @param {Node} this This node
21270         * @param {Node} node The child node to be inserted
21271         * @param {Node} refNode The child node the node is being inserted before
21272         */
21273        "beforeinsert" : true
21274    });
21275     this.listeners = this.attributes.listeners;
21276     Roo.data.Node.superclass.constructor.call(this);
21277 };
21278
21279 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21280     fireEvent : function(evtName){
21281         // first do standard event for this node
21282         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21283             return false;
21284         }
21285         // then bubble it up to the tree if the event wasn't cancelled
21286         var ot = this.getOwnerTree();
21287         if(ot){
21288             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21289                 return false;
21290             }
21291         }
21292         return true;
21293     },
21294
21295     /**
21296      * Returns true if this node is a leaf
21297      * @return {Boolean}
21298      */
21299     isLeaf : function(){
21300         return this.leaf === true;
21301     },
21302
21303     // private
21304     setFirstChild : function(node){
21305         this.firstChild = node;
21306     },
21307
21308     //private
21309     setLastChild : function(node){
21310         this.lastChild = node;
21311     },
21312
21313
21314     /**
21315      * Returns true if this node is the last child of its parent
21316      * @return {Boolean}
21317      */
21318     isLast : function(){
21319        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21320     },
21321
21322     /**
21323      * Returns true if this node is the first child of its parent
21324      * @return {Boolean}
21325      */
21326     isFirst : function(){
21327        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21328     },
21329
21330     hasChildNodes : function(){
21331         return !this.isLeaf() && this.childNodes.length > 0;
21332     },
21333
21334     /**
21335      * Insert node(s) as the last child node of this node.
21336      * @param {Node/Array} node The node or Array of nodes to append
21337      * @return {Node} The appended node if single append, or null if an array was passed
21338      */
21339     appendChild : function(node){
21340         var multi = false;
21341         if(node instanceof Array){
21342             multi = node;
21343         }else if(arguments.length > 1){
21344             multi = arguments;
21345         }
21346         // if passed an array or multiple args do them one by one
21347         if(multi){
21348             for(var i = 0, len = multi.length; i < len; i++) {
21349                 this.appendChild(multi[i]);
21350             }
21351         }else{
21352             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21353                 return false;
21354             }
21355             var index = this.childNodes.length;
21356             var oldParent = node.parentNode;
21357             // it's a move, make sure we move it cleanly
21358             if(oldParent){
21359                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21360                     return false;
21361                 }
21362                 oldParent.removeChild(node);
21363             }
21364             index = this.childNodes.length;
21365             if(index == 0){
21366                 this.setFirstChild(node);
21367             }
21368             this.childNodes.push(node);
21369             node.parentNode = this;
21370             var ps = this.childNodes[index-1];
21371             if(ps){
21372                 node.previousSibling = ps;
21373                 ps.nextSibling = node;
21374             }else{
21375                 node.previousSibling = null;
21376             }
21377             node.nextSibling = null;
21378             this.setLastChild(node);
21379             node.setOwnerTree(this.getOwnerTree());
21380             this.fireEvent("append", this.ownerTree, this, node, index);
21381             if(oldParent){
21382                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21383             }
21384             return node;
21385         }
21386     },
21387
21388     /**
21389      * Removes a child node from this node.
21390      * @param {Node} node The node to remove
21391      * @return {Node} The removed node
21392      */
21393     removeChild : function(node){
21394         var index = this.childNodes.indexOf(node);
21395         if(index == -1){
21396             return false;
21397         }
21398         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21399             return false;
21400         }
21401
21402         // remove it from childNodes collection
21403         this.childNodes.splice(index, 1);
21404
21405         // update siblings
21406         if(node.previousSibling){
21407             node.previousSibling.nextSibling = node.nextSibling;
21408         }
21409         if(node.nextSibling){
21410             node.nextSibling.previousSibling = node.previousSibling;
21411         }
21412
21413         // update child refs
21414         if(this.firstChild == node){
21415             this.setFirstChild(node.nextSibling);
21416         }
21417         if(this.lastChild == node){
21418             this.setLastChild(node.previousSibling);
21419         }
21420
21421         node.setOwnerTree(null);
21422         // clear any references from the node
21423         node.parentNode = null;
21424         node.previousSibling = null;
21425         node.nextSibling = null;
21426         this.fireEvent("remove", this.ownerTree, this, node);
21427         return node;
21428     },
21429
21430     /**
21431      * Inserts the first node before the second node in this nodes childNodes collection.
21432      * @param {Node} node The node to insert
21433      * @param {Node} refNode The node to insert before (if null the node is appended)
21434      * @return {Node} The inserted node
21435      */
21436     insertBefore : function(node, refNode){
21437         if(!refNode){ // like standard Dom, refNode can be null for append
21438             return this.appendChild(node);
21439         }
21440         // nothing to do
21441         if(node == refNode){
21442             return false;
21443         }
21444
21445         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21446             return false;
21447         }
21448         var index = this.childNodes.indexOf(refNode);
21449         var oldParent = node.parentNode;
21450         var refIndex = index;
21451
21452         // when moving internally, indexes will change after remove
21453         if(oldParent == this && this.childNodes.indexOf(node) < index){
21454             refIndex--;
21455         }
21456
21457         // it's a move, make sure we move it cleanly
21458         if(oldParent){
21459             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21460                 return false;
21461             }
21462             oldParent.removeChild(node);
21463         }
21464         if(refIndex == 0){
21465             this.setFirstChild(node);
21466         }
21467         this.childNodes.splice(refIndex, 0, node);
21468         node.parentNode = this;
21469         var ps = this.childNodes[refIndex-1];
21470         if(ps){
21471             node.previousSibling = ps;
21472             ps.nextSibling = node;
21473         }else{
21474             node.previousSibling = null;
21475         }
21476         node.nextSibling = refNode;
21477         refNode.previousSibling = node;
21478         node.setOwnerTree(this.getOwnerTree());
21479         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21480         if(oldParent){
21481             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21482         }
21483         return node;
21484     },
21485
21486     /**
21487      * Returns the child node at the specified index.
21488      * @param {Number} index
21489      * @return {Node}
21490      */
21491     item : function(index){
21492         return this.childNodes[index];
21493     },
21494
21495     /**
21496      * Replaces one child node in this node with another.
21497      * @param {Node} newChild The replacement node
21498      * @param {Node} oldChild The node to replace
21499      * @return {Node} The replaced node
21500      */
21501     replaceChild : function(newChild, oldChild){
21502         this.insertBefore(newChild, oldChild);
21503         this.removeChild(oldChild);
21504         return oldChild;
21505     },
21506
21507     /**
21508      * Returns the index of a child node
21509      * @param {Node} node
21510      * @return {Number} The index of the node or -1 if it was not found
21511      */
21512     indexOf : function(child){
21513         return this.childNodes.indexOf(child);
21514     },
21515
21516     /**
21517      * Returns the tree this node is in.
21518      * @return {Tree}
21519      */
21520     getOwnerTree : function(){
21521         // if it doesn't have one, look for one
21522         if(!this.ownerTree){
21523             var p = this;
21524             while(p){
21525                 if(p.ownerTree){
21526                     this.ownerTree = p.ownerTree;
21527                     break;
21528                 }
21529                 p = p.parentNode;
21530             }
21531         }
21532         return this.ownerTree;
21533     },
21534
21535     /**
21536      * Returns depth of this node (the root node has a depth of 0)
21537      * @return {Number}
21538      */
21539     getDepth : function(){
21540         var depth = 0;
21541         var p = this;
21542         while(p.parentNode){
21543             ++depth;
21544             p = p.parentNode;
21545         }
21546         return depth;
21547     },
21548
21549     // private
21550     setOwnerTree : function(tree){
21551         // if it's move, we need to update everyone
21552         if(tree != this.ownerTree){
21553             if(this.ownerTree){
21554                 this.ownerTree.unregisterNode(this);
21555             }
21556             this.ownerTree = tree;
21557             var cs = this.childNodes;
21558             for(var i = 0, len = cs.length; i < len; i++) {
21559                 cs[i].setOwnerTree(tree);
21560             }
21561             if(tree){
21562                 tree.registerNode(this);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21569      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21570      * @return {String} The path
21571      */
21572     getPath : function(attr){
21573         attr = attr || "id";
21574         var p = this.parentNode;
21575         var b = [this.attributes[attr]];
21576         while(p){
21577             b.unshift(p.attributes[attr]);
21578             p = p.parentNode;
21579         }
21580         var sep = this.getOwnerTree().pathSeparator;
21581         return sep + b.join(sep);
21582     },
21583
21584     /**
21585      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21586      * function call will be the scope provided or the current node. The arguments to the function
21587      * will be the args provided or the current node. If the function returns false at any point,
21588      * the bubble is stopped.
21589      * @param {Function} fn The function to call
21590      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21591      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21592      */
21593     bubble : function(fn, scope, args){
21594         var p = this;
21595         while(p){
21596             if(fn.call(scope || p, args || p) === false){
21597                 break;
21598             }
21599             p = p.parentNode;
21600         }
21601     },
21602
21603     /**
21604      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21605      * function call will be the scope provided or the current node. The arguments to the function
21606      * will be the args provided or the current node. If the function returns false at any point,
21607      * the cascade is stopped on that branch.
21608      * @param {Function} fn The function to call
21609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21611      */
21612     cascade : function(fn, scope, args){
21613         if(fn.call(scope || this, args || this) !== false){
21614             var cs = this.childNodes;
21615             for(var i = 0, len = cs.length; i < len; i++) {
21616                 cs[i].cascade(fn, scope, args);
21617             }
21618         }
21619     },
21620
21621     /**
21622      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21623      * function call will be the scope provided or the current node. The arguments to the function
21624      * will be the args provided or the current node. If the function returns false at any point,
21625      * the iteration stops.
21626      * @param {Function} fn The function to call
21627      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21628      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21629      */
21630     eachChild : function(fn, scope, args){
21631         var cs = this.childNodes;
21632         for(var i = 0, len = cs.length; i < len; i++) {
21633                 if(fn.call(scope || this, args || cs[i]) === false){
21634                     break;
21635                 }
21636         }
21637     },
21638
21639     /**
21640      * Finds the first child that has the attribute with the specified value.
21641      * @param {String} attribute The attribute name
21642      * @param {Mixed} value The value to search for
21643      * @return {Node} The found child or null if none was found
21644      */
21645     findChild : function(attribute, value){
21646         var cs = this.childNodes;
21647         for(var i = 0, len = cs.length; i < len; i++) {
21648                 if(cs[i].attributes[attribute] == value){
21649                     return cs[i];
21650                 }
21651         }
21652         return null;
21653     },
21654
21655     /**
21656      * Finds the first child by a custom function. The child matches if the function passed
21657      * returns true.
21658      * @param {Function} fn
21659      * @param {Object} scope (optional)
21660      * @return {Node} The found child or null if none was found
21661      */
21662     findChildBy : function(fn, scope){
21663         var cs = this.childNodes;
21664         for(var i = 0, len = cs.length; i < len; i++) {
21665                 if(fn.call(scope||cs[i], cs[i]) === true){
21666                     return cs[i];
21667                 }
21668         }
21669         return null;
21670     },
21671
21672     /**
21673      * Sorts this nodes children using the supplied sort function
21674      * @param {Function} fn
21675      * @param {Object} scope (optional)
21676      */
21677     sort : function(fn, scope){
21678         var cs = this.childNodes;
21679         var len = cs.length;
21680         if(len > 0){
21681             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21682             cs.sort(sortFn);
21683             for(var i = 0; i < len; i++){
21684                 var n = cs[i];
21685                 n.previousSibling = cs[i-1];
21686                 n.nextSibling = cs[i+1];
21687                 if(i == 0){
21688                     this.setFirstChild(n);
21689                 }
21690                 if(i == len-1){
21691                     this.setLastChild(n);
21692                 }
21693             }
21694         }
21695     },
21696
21697     /**
21698      * Returns true if this node is an ancestor (at any point) of the passed node.
21699      * @param {Node} node
21700      * @return {Boolean}
21701      */
21702     contains : function(node){
21703         return node.isAncestor(this);
21704     },
21705
21706     /**
21707      * Returns true if the passed node is an ancestor (at any point) of this node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     isAncestor : function(node){
21712         var p = this.parentNode;
21713         while(p){
21714             if(p == node){
21715                 return true;
21716             }
21717             p = p.parentNode;
21718         }
21719         return false;
21720     },
21721
21722     toString : function(){
21723         return "[Node"+(this.id?" "+this.id:"")+"]";
21724     }
21725 });/*
21726  * Based on:
21727  * Ext JS Library 1.1.1
21728  * Copyright(c) 2006-2007, Ext JS, LLC.
21729  *
21730  * Originally Released Under LGPL - original licence link has changed is not relivant.
21731  *
21732  * Fork - LGPL
21733  * <script type="text/javascript">
21734  */
21735  
21736
21737 /**
21738  * @class Roo.ComponentMgr
21739  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21740  * @singleton
21741  */
21742 Roo.ComponentMgr = function(){
21743     var all = new Roo.util.MixedCollection();
21744
21745     return {
21746         /**
21747          * Registers a component.
21748          * @param {Roo.Component} c The component
21749          */
21750         register : function(c){
21751             all.add(c);
21752         },
21753
21754         /**
21755          * Unregisters a component.
21756          * @param {Roo.Component} c The component
21757          */
21758         unregister : function(c){
21759             all.remove(c);
21760         },
21761
21762         /**
21763          * Returns a component by id
21764          * @param {String} id The component id
21765          */
21766         get : function(id){
21767             return all.get(id);
21768         },
21769
21770         /**
21771          * Registers a function that will be called when a specified component is added to ComponentMgr
21772          * @param {String} id The component id
21773          * @param {Funtction} fn The callback function
21774          * @param {Object} scope The scope of the callback
21775          */
21776         onAvailable : function(id, fn, scope){
21777             all.on("add", function(index, o){
21778                 if(o.id == id){
21779                     fn.call(scope || o, o);
21780                     all.un("add", fn, scope);
21781                 }
21782             });
21783         }
21784     };
21785 }();/*
21786  * Based on:
21787  * Ext JS Library 1.1.1
21788  * Copyright(c) 2006-2007, Ext JS, LLC.
21789  *
21790  * Originally Released Under LGPL - original licence link has changed is not relivant.
21791  *
21792  * Fork - LGPL
21793  * <script type="text/javascript">
21794  */
21795  
21796 /**
21797  * @class Roo.Component
21798  * @extends Roo.util.Observable
21799  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21800  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21801  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21802  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21803  * All visual components (widgets) that require rendering into a layout should subclass Component.
21804  * @constructor
21805  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21806  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21807  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21808  */
21809 Roo.Component = function(config){
21810     config = config || {};
21811     if(config.tagName || config.dom || typeof config == "string"){ // element object
21812         config = {el: config, id: config.id || config};
21813     }
21814     this.initialConfig = config;
21815
21816     Roo.apply(this, config);
21817     this.addEvents({
21818         /**
21819          * @event disable
21820          * Fires after the component is disabled.
21821              * @param {Roo.Component} this
21822              */
21823         disable : true,
21824         /**
21825          * @event enable
21826          * Fires after the component is enabled.
21827              * @param {Roo.Component} this
21828              */
21829         enable : true,
21830         /**
21831          * @event beforeshow
21832          * Fires before the component is shown.  Return false to stop the show.
21833              * @param {Roo.Component} this
21834              */
21835         beforeshow : true,
21836         /**
21837          * @event show
21838          * Fires after the component is shown.
21839              * @param {Roo.Component} this
21840              */
21841         show : true,
21842         /**
21843          * @event beforehide
21844          * Fires before the component is hidden. Return false to stop the hide.
21845              * @param {Roo.Component} this
21846              */
21847         beforehide : true,
21848         /**
21849          * @event hide
21850          * Fires after the component is hidden.
21851              * @param {Roo.Component} this
21852              */
21853         hide : true,
21854         /**
21855          * @event beforerender
21856          * Fires before the component is rendered. Return false to stop the render.
21857              * @param {Roo.Component} this
21858              */
21859         beforerender : true,
21860         /**
21861          * @event render
21862          * Fires after the component is rendered.
21863              * @param {Roo.Component} this
21864              */
21865         render : true,
21866         /**
21867          * @event beforedestroy
21868          * Fires before the component is destroyed. Return false to stop the destroy.
21869              * @param {Roo.Component} this
21870              */
21871         beforedestroy : true,
21872         /**
21873          * @event destroy
21874          * Fires after the component is destroyed.
21875              * @param {Roo.Component} this
21876              */
21877         destroy : true
21878     });
21879     if(!this.id){
21880         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21881     }
21882     Roo.ComponentMgr.register(this);
21883     Roo.Component.superclass.constructor.call(this);
21884     this.initComponent();
21885     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21886         this.render(this.renderTo);
21887         delete this.renderTo;
21888     }
21889 };
21890
21891 /** @private */
21892 Roo.Component.AUTO_ID = 1000;
21893
21894 Roo.extend(Roo.Component, Roo.util.Observable, {
21895     /**
21896      * @scope Roo.Component.prototype
21897      * @type {Boolean}
21898      * true if this component is hidden. Read-only.
21899      */
21900     hidden : false,
21901     /**
21902      * @type {Boolean}
21903      * true if this component is disabled. Read-only.
21904      */
21905     disabled : false,
21906     /**
21907      * @type {Boolean}
21908      * true if this component has been rendered. Read-only.
21909      */
21910     rendered : false,
21911     
21912     /** @cfg {String} disableClass
21913      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21914      */
21915     disabledClass : "x-item-disabled",
21916         /** @cfg {Boolean} allowDomMove
21917          * Whether the component can move the Dom node when rendering (defaults to true).
21918          */
21919     allowDomMove : true,
21920     /** @cfg {String} hideMode
21921      * How this component should hidden. Supported values are
21922      * "visibility" (css visibility), "offsets" (negative offset position) and
21923      * "display" (css display) - defaults to "display".
21924      */
21925     hideMode: 'display',
21926
21927     /** @private */
21928     ctype : "Roo.Component",
21929
21930     /**
21931      * @cfg {String} actionMode 
21932      * which property holds the element that used for  hide() / show() / disable() / enable()
21933      * default is 'el' 
21934      */
21935     actionMode : "el",
21936
21937     /** @private */
21938     getActionEl : function(){
21939         return this[this.actionMode];
21940     },
21941
21942     initComponent : Roo.emptyFn,
21943     /**
21944      * If this is a lazy rendering component, render it to its container element.
21945      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21946      */
21947     render : function(container, position){
21948         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21949             if(!container && this.el){
21950                 this.el = Roo.get(this.el);
21951                 container = this.el.dom.parentNode;
21952                 this.allowDomMove = false;
21953             }
21954             this.container = Roo.get(container);
21955             this.rendered = true;
21956             if(position !== undefined){
21957                 if(typeof position == 'number'){
21958                     position = this.container.dom.childNodes[position];
21959                 }else{
21960                     position = Roo.getDom(position);
21961                 }
21962             }
21963             this.onRender(this.container, position || null);
21964             if(this.cls){
21965                 this.el.addClass(this.cls);
21966                 delete this.cls;
21967             }
21968             if(this.style){
21969                 this.el.applyStyles(this.style);
21970                 delete this.style;
21971             }
21972             this.fireEvent("render", this);
21973             this.afterRender(this.container);
21974             if(this.hidden){
21975                 this.hide();
21976             }
21977             if(this.disabled){
21978                 this.disable();
21979             }
21980         }
21981         return this;
21982     },
21983
21984     /** @private */
21985     // default function is not really useful
21986     onRender : function(ct, position){
21987         if(this.el){
21988             this.el = Roo.get(this.el);
21989             if(this.allowDomMove !== false){
21990                 ct.dom.insertBefore(this.el.dom, position);
21991             }
21992         }
21993     },
21994
21995     /** @private */
21996     getAutoCreate : function(){
21997         var cfg = typeof this.autoCreate == "object" ?
21998                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21999         if(this.id && !cfg.id){
22000             cfg.id = this.id;
22001         }
22002         return cfg;
22003     },
22004
22005     /** @private */
22006     afterRender : Roo.emptyFn,
22007
22008     /**
22009      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22010      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22011      */
22012     destroy : function(){
22013         if(this.fireEvent("beforedestroy", this) !== false){
22014             this.purgeListeners();
22015             this.beforeDestroy();
22016             if(this.rendered){
22017                 this.el.removeAllListeners();
22018                 this.el.remove();
22019                 if(this.actionMode == "container"){
22020                     this.container.remove();
22021                 }
22022             }
22023             this.onDestroy();
22024             Roo.ComponentMgr.unregister(this);
22025             this.fireEvent("destroy", this);
22026         }
22027     },
22028
22029         /** @private */
22030     beforeDestroy : function(){
22031
22032     },
22033
22034         /** @private */
22035         onDestroy : function(){
22036
22037     },
22038
22039     /**
22040      * Returns the underlying {@link Roo.Element}.
22041      * @return {Roo.Element} The element
22042      */
22043     getEl : function(){
22044         return this.el;
22045     },
22046
22047     /**
22048      * Returns the id of this component.
22049      * @return {String}
22050      */
22051     getId : function(){
22052         return this.id;
22053     },
22054
22055     /**
22056      * Try to focus this component.
22057      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22058      * @return {Roo.Component} this
22059      */
22060     focus : function(selectText){
22061         if(this.rendered){
22062             this.el.focus();
22063             if(selectText === true){
22064                 this.el.dom.select();
22065             }
22066         }
22067         return this;
22068     },
22069
22070     /** @private */
22071     blur : function(){
22072         if(this.rendered){
22073             this.el.blur();
22074         }
22075         return this;
22076     },
22077
22078     /**
22079      * Disable this component.
22080      * @return {Roo.Component} this
22081      */
22082     disable : function(){
22083         if(this.rendered){
22084             this.onDisable();
22085         }
22086         this.disabled = true;
22087         this.fireEvent("disable", this);
22088         return this;
22089     },
22090
22091         // private
22092     onDisable : function(){
22093         this.getActionEl().addClass(this.disabledClass);
22094         this.el.dom.disabled = true;
22095     },
22096
22097     /**
22098      * Enable this component.
22099      * @return {Roo.Component} this
22100      */
22101     enable : function(){
22102         if(this.rendered){
22103             this.onEnable();
22104         }
22105         this.disabled = false;
22106         this.fireEvent("enable", this);
22107         return this;
22108     },
22109
22110         // private
22111     onEnable : function(){
22112         this.getActionEl().removeClass(this.disabledClass);
22113         this.el.dom.disabled = false;
22114     },
22115
22116     /**
22117      * Convenience function for setting disabled/enabled by boolean.
22118      * @param {Boolean} disabled
22119      */
22120     setDisabled : function(disabled){
22121         this[disabled ? "disable" : "enable"]();
22122     },
22123
22124     /**
22125      * Show this component.
22126      * @return {Roo.Component} this
22127      */
22128     show: function(){
22129         if(this.fireEvent("beforeshow", this) !== false){
22130             this.hidden = false;
22131             if(this.rendered){
22132                 this.onShow();
22133             }
22134             this.fireEvent("show", this);
22135         }
22136         return this;
22137     },
22138
22139     // private
22140     onShow : function(){
22141         var ae = this.getActionEl();
22142         if(this.hideMode == 'visibility'){
22143             ae.dom.style.visibility = "visible";
22144         }else if(this.hideMode == 'offsets'){
22145             ae.removeClass('x-hidden');
22146         }else{
22147             ae.dom.style.display = "";
22148         }
22149     },
22150
22151     /**
22152      * Hide this component.
22153      * @return {Roo.Component} this
22154      */
22155     hide: function(){
22156         if(this.fireEvent("beforehide", this) !== false){
22157             this.hidden = true;
22158             if(this.rendered){
22159                 this.onHide();
22160             }
22161             this.fireEvent("hide", this);
22162         }
22163         return this;
22164     },
22165
22166     // private
22167     onHide : function(){
22168         var ae = this.getActionEl();
22169         if(this.hideMode == 'visibility'){
22170             ae.dom.style.visibility = "hidden";
22171         }else if(this.hideMode == 'offsets'){
22172             ae.addClass('x-hidden');
22173         }else{
22174             ae.dom.style.display = "none";
22175         }
22176     },
22177
22178     /**
22179      * Convenience function to hide or show this component by boolean.
22180      * @param {Boolean} visible True to show, false to hide
22181      * @return {Roo.Component} this
22182      */
22183     setVisible: function(visible){
22184         if(visible) {
22185             this.show();
22186         }else{
22187             this.hide();
22188         }
22189         return this;
22190     },
22191
22192     /**
22193      * Returns true if this component is visible.
22194      */
22195     isVisible : function(){
22196         return this.getActionEl().isVisible();
22197     },
22198
22199     cloneConfig : function(overrides){
22200         overrides = overrides || {};
22201         var id = overrides.id || Roo.id();
22202         var cfg = Roo.applyIf(overrides, this.initialConfig);
22203         cfg.id = id; // prevent dup id
22204         return new this.constructor(cfg);
22205     }
22206 });/*
22207  * Based on:
22208  * Ext JS Library 1.1.1
22209  * Copyright(c) 2006-2007, Ext JS, LLC.
22210  *
22211  * Originally Released Under LGPL - original licence link has changed is not relivant.
22212  *
22213  * Fork - LGPL
22214  * <script type="text/javascript">
22215  */
22216  (function(){ 
22217 /**
22218  * @class Roo.Layer
22219  * @extends Roo.Element
22220  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22221  * automatic maintaining of shadow/shim positions.
22222  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22223  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22224  * you can pass a string with a CSS class name. False turns off the shadow.
22225  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22226  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22227  * @cfg {String} cls CSS class to add to the element
22228  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22229  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22230  * @constructor
22231  * @param {Object} config An object with config options.
22232  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22233  */
22234
22235 Roo.Layer = function(config, existingEl){
22236     config = config || {};
22237     var dh = Roo.DomHelper;
22238     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22239     if(existingEl){
22240         this.dom = Roo.getDom(existingEl);
22241     }
22242     if(!this.dom){
22243         var o = config.dh || {tag: "div", cls: "x-layer"};
22244         this.dom = dh.append(pel, o);
22245     }
22246     if(config.cls){
22247         this.addClass(config.cls);
22248     }
22249     this.constrain = config.constrain !== false;
22250     this.visibilityMode = Roo.Element.VISIBILITY;
22251     if(config.id){
22252         this.id = this.dom.id = config.id;
22253     }else{
22254         this.id = Roo.id(this.dom);
22255     }
22256     this.zindex = config.zindex || this.getZIndex();
22257     this.position("absolute", this.zindex);
22258     if(config.shadow){
22259         this.shadowOffset = config.shadowOffset || 4;
22260         this.shadow = new Roo.Shadow({
22261             offset : this.shadowOffset,
22262             mode : config.shadow
22263         });
22264     }else{
22265         this.shadowOffset = 0;
22266     }
22267     this.useShim = config.shim !== false && Roo.useShims;
22268     this.useDisplay = config.useDisplay;
22269     this.hide();
22270 };
22271
22272 var supr = Roo.Element.prototype;
22273
22274 // shims are shared among layer to keep from having 100 iframes
22275 var shims = [];
22276
22277 Roo.extend(Roo.Layer, Roo.Element, {
22278
22279     getZIndex : function(){
22280         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22281     },
22282
22283     getShim : function(){
22284         if(!this.useShim){
22285             return null;
22286         }
22287         if(this.shim){
22288             return this.shim;
22289         }
22290         var shim = shims.shift();
22291         if(!shim){
22292             shim = this.createShim();
22293             shim.enableDisplayMode('block');
22294             shim.dom.style.display = 'none';
22295             shim.dom.style.visibility = 'visible';
22296         }
22297         var pn = this.dom.parentNode;
22298         if(shim.dom.parentNode != pn){
22299             pn.insertBefore(shim.dom, this.dom);
22300         }
22301         shim.setStyle('z-index', this.getZIndex()-2);
22302         this.shim = shim;
22303         return shim;
22304     },
22305
22306     hideShim : function(){
22307         if(this.shim){
22308             this.shim.setDisplayed(false);
22309             shims.push(this.shim);
22310             delete this.shim;
22311         }
22312     },
22313
22314     disableShadow : function(){
22315         if(this.shadow){
22316             this.shadowDisabled = true;
22317             this.shadow.hide();
22318             this.lastShadowOffset = this.shadowOffset;
22319             this.shadowOffset = 0;
22320         }
22321     },
22322
22323     enableShadow : function(show){
22324         if(this.shadow){
22325             this.shadowDisabled = false;
22326             this.shadowOffset = this.lastShadowOffset;
22327             delete this.lastShadowOffset;
22328             if(show){
22329                 this.sync(true);
22330             }
22331         }
22332     },
22333
22334     // private
22335     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22336     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22337     sync : function(doShow){
22338         var sw = this.shadow;
22339         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22340             var sh = this.getShim();
22341
22342             var w = this.getWidth(),
22343                 h = this.getHeight();
22344
22345             var l = this.getLeft(true),
22346                 t = this.getTop(true);
22347
22348             if(sw && !this.shadowDisabled){
22349                 if(doShow && !sw.isVisible()){
22350                     sw.show(this);
22351                 }else{
22352                     sw.realign(l, t, w, h);
22353                 }
22354                 if(sh){
22355                     if(doShow){
22356                        sh.show();
22357                     }
22358                     // fit the shim behind the shadow, so it is shimmed too
22359                     var a = sw.adjusts, s = sh.dom.style;
22360                     s.left = (Math.min(l, l+a.l))+"px";
22361                     s.top = (Math.min(t, t+a.t))+"px";
22362                     s.width = (w+a.w)+"px";
22363                     s.height = (h+a.h)+"px";
22364                 }
22365             }else if(sh){
22366                 if(doShow){
22367                    sh.show();
22368                 }
22369                 sh.setSize(w, h);
22370                 sh.setLeftTop(l, t);
22371             }
22372             
22373         }
22374     },
22375
22376     // private
22377     destroy : function(){
22378         this.hideShim();
22379         if(this.shadow){
22380             this.shadow.hide();
22381         }
22382         this.removeAllListeners();
22383         var pn = this.dom.parentNode;
22384         if(pn){
22385             pn.removeChild(this.dom);
22386         }
22387         Roo.Element.uncache(this.id);
22388     },
22389
22390     remove : function(){
22391         this.destroy();
22392     },
22393
22394     // private
22395     beginUpdate : function(){
22396         this.updating = true;
22397     },
22398
22399     // private
22400     endUpdate : function(){
22401         this.updating = false;
22402         this.sync(true);
22403     },
22404
22405     // private
22406     hideUnders : function(negOffset){
22407         if(this.shadow){
22408             this.shadow.hide();
22409         }
22410         this.hideShim();
22411     },
22412
22413     // private
22414     constrainXY : function(){
22415         if(this.constrain){
22416             var vw = Roo.lib.Dom.getViewWidth(),
22417                 vh = Roo.lib.Dom.getViewHeight();
22418             var s = Roo.get(document).getScroll();
22419
22420             var xy = this.getXY();
22421             var x = xy[0], y = xy[1];   
22422             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22423             // only move it if it needs it
22424             var moved = false;
22425             // first validate right/bottom
22426             if((x + w) > vw+s.left){
22427                 x = vw - w - this.shadowOffset;
22428                 moved = true;
22429             }
22430             if((y + h) > vh+s.top){
22431                 y = vh - h - this.shadowOffset;
22432                 moved = true;
22433             }
22434             // then make sure top/left isn't negative
22435             if(x < s.left){
22436                 x = s.left;
22437                 moved = true;
22438             }
22439             if(y < s.top){
22440                 y = s.top;
22441                 moved = true;
22442             }
22443             if(moved){
22444                 if(this.avoidY){
22445                     var ay = this.avoidY;
22446                     if(y <= ay && (y+h) >= ay){
22447                         y = ay-h-5;   
22448                     }
22449                 }
22450                 xy = [x, y];
22451                 this.storeXY(xy);
22452                 supr.setXY.call(this, xy);
22453                 this.sync();
22454             }
22455         }
22456     },
22457
22458     isVisible : function(){
22459         return this.visible;    
22460     },
22461
22462     // private
22463     showAction : function(){
22464         this.visible = true; // track visibility to prevent getStyle calls
22465         if(this.useDisplay === true){
22466             this.setDisplayed("");
22467         }else if(this.lastXY){
22468             supr.setXY.call(this, this.lastXY);
22469         }else if(this.lastLT){
22470             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22471         }
22472     },
22473
22474     // private
22475     hideAction : function(){
22476         this.visible = false;
22477         if(this.useDisplay === true){
22478             this.setDisplayed(false);
22479         }else{
22480             this.setLeftTop(-10000,-10000);
22481         }
22482     },
22483
22484     // overridden Element method
22485     setVisible : function(v, a, d, c, e){
22486         if(v){
22487             this.showAction();
22488         }
22489         if(a && v){
22490             var cb = function(){
22491                 this.sync(true);
22492                 if(c){
22493                     c();
22494                 }
22495             }.createDelegate(this);
22496             supr.setVisible.call(this, true, true, d, cb, e);
22497         }else{
22498             if(!v){
22499                 this.hideUnders(true);
22500             }
22501             var cb = c;
22502             if(a){
22503                 cb = function(){
22504                     this.hideAction();
22505                     if(c){
22506                         c();
22507                     }
22508                 }.createDelegate(this);
22509             }
22510             supr.setVisible.call(this, v, a, d, cb, e);
22511             if(v){
22512                 this.sync(true);
22513             }else if(!a){
22514                 this.hideAction();
22515             }
22516         }
22517     },
22518
22519     storeXY : function(xy){
22520         delete this.lastLT;
22521         this.lastXY = xy;
22522     },
22523
22524     storeLeftTop : function(left, top){
22525         delete this.lastXY;
22526         this.lastLT = [left, top];
22527     },
22528
22529     // private
22530     beforeFx : function(){
22531         this.beforeAction();
22532         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22533     },
22534
22535     // private
22536     afterFx : function(){
22537         Roo.Layer.superclass.afterFx.apply(this, arguments);
22538         this.sync(this.isVisible());
22539     },
22540
22541     // private
22542     beforeAction : function(){
22543         if(!this.updating && this.shadow){
22544             this.shadow.hide();
22545         }
22546     },
22547
22548     // overridden Element method
22549     setLeft : function(left){
22550         this.storeLeftTop(left, this.getTop(true));
22551         supr.setLeft.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setTop : function(top){
22556         this.storeLeftTop(this.getLeft(true), top);
22557         supr.setTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setLeftTop : function(left, top){
22562         this.storeLeftTop(left, top);
22563         supr.setLeftTop.apply(this, arguments);
22564         this.sync();
22565     },
22566
22567     setXY : function(xy, a, d, c, e){
22568         this.fixDisplay();
22569         this.beforeAction();
22570         this.storeXY(xy);
22571         var cb = this.createCB(c);
22572         supr.setXY.call(this, xy, a, d, cb, e);
22573         if(!a){
22574             cb();
22575         }
22576     },
22577
22578     // private
22579     createCB : function(c){
22580         var el = this;
22581         return function(){
22582             el.constrainXY();
22583             el.sync(true);
22584             if(c){
22585                 c();
22586             }
22587         };
22588     },
22589
22590     // overridden Element method
22591     setX : function(x, a, d, c, e){
22592         this.setXY([x, this.getY()], a, d, c, e);
22593     },
22594
22595     // overridden Element method
22596     setY : function(y, a, d, c, e){
22597         this.setXY([this.getX(), y], a, d, c, e);
22598     },
22599
22600     // overridden Element method
22601     setSize : function(w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         supr.setSize.call(this, w, h, a, d, cb, e);
22605         if(!a){
22606             cb();
22607         }
22608     },
22609
22610     // overridden Element method
22611     setWidth : function(w, a, d, c, e){
22612         this.beforeAction();
22613         var cb = this.createCB(c);
22614         supr.setWidth.call(this, w, a, d, cb, e);
22615         if(!a){
22616             cb();
22617         }
22618     },
22619
22620     // overridden Element method
22621     setHeight : function(h, a, d, c, e){
22622         this.beforeAction();
22623         var cb = this.createCB(c);
22624         supr.setHeight.call(this, h, a, d, cb, e);
22625         if(!a){
22626             cb();
22627         }
22628     },
22629
22630     // overridden Element method
22631     setBounds : function(x, y, w, h, a, d, c, e){
22632         this.beforeAction();
22633         var cb = this.createCB(c);
22634         if(!a){
22635             this.storeXY([x, y]);
22636             supr.setXY.call(this, [x, y]);
22637             supr.setSize.call(this, w, h, a, d, cb, e);
22638             cb();
22639         }else{
22640             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22641         }
22642         return this;
22643     },
22644     
22645     /**
22646      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22647      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22648      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22649      * @param {Number} zindex The new z-index to set
22650      * @return {this} The Layer
22651      */
22652     setZIndex : function(zindex){
22653         this.zindex = zindex;
22654         this.setStyle("z-index", zindex + 2);
22655         if(this.shadow){
22656             this.shadow.setZIndex(zindex + 1);
22657         }
22658         if(this.shim){
22659             this.shim.setStyle("z-index", zindex);
22660         }
22661     }
22662 });
22663 })();/*
22664  * Based on:
22665  * Ext JS Library 1.1.1
22666  * Copyright(c) 2006-2007, Ext JS, LLC.
22667  *
22668  * Originally Released Under LGPL - original licence link has changed is not relivant.
22669  *
22670  * Fork - LGPL
22671  * <script type="text/javascript">
22672  */
22673
22674
22675 /**
22676  * @class Roo.Shadow
22677  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22678  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22679  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22680  * @constructor
22681  * Create a new Shadow
22682  * @param {Object} config The config object
22683  */
22684 Roo.Shadow = function(config){
22685     Roo.apply(this, config);
22686     if(typeof this.mode != "string"){
22687         this.mode = this.defaultMode;
22688     }
22689     var o = this.offset, a = {h: 0};
22690     var rad = Math.floor(this.offset/2);
22691     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22692         case "drop":
22693             a.w = 0;
22694             a.l = a.t = o;
22695             a.t -= 1;
22696             if(Roo.isIE){
22697                 a.l -= this.offset + rad;
22698                 a.t -= this.offset + rad;
22699                 a.w -= rad;
22700                 a.h -= rad;
22701                 a.t += 1;
22702             }
22703         break;
22704         case "sides":
22705             a.w = (o*2);
22706             a.l = -o;
22707             a.t = o-1;
22708             if(Roo.isIE){
22709                 a.l -= (this.offset - rad);
22710                 a.t -= this.offset + rad;
22711                 a.l += 1;
22712                 a.w -= (this.offset - rad)*2;
22713                 a.w -= rad + 1;
22714                 a.h -= 1;
22715             }
22716         break;
22717         case "frame":
22718             a.w = a.h = (o*2);
22719             a.l = a.t = -o;
22720             a.t += 1;
22721             a.h -= 2;
22722             if(Roo.isIE){
22723                 a.l -= (this.offset - rad);
22724                 a.t -= (this.offset - rad);
22725                 a.l += 1;
22726                 a.w -= (this.offset + rad + 1);
22727                 a.h -= (this.offset + rad);
22728                 a.h += 1;
22729             }
22730         break;
22731     };
22732
22733     this.adjusts = a;
22734 };
22735
22736 Roo.Shadow.prototype = {
22737     /**
22738      * @cfg {String} mode
22739      * The shadow display mode.  Supports the following options:<br />
22740      * sides: Shadow displays on both sides and bottom only<br />
22741      * frame: Shadow displays equally on all four sides<br />
22742      * drop: Traditional bottom-right drop shadow (default)
22743      */
22744     /**
22745      * @cfg {String} offset
22746      * The number of pixels to offset the shadow from the element (defaults to 4)
22747      */
22748     offset: 4,
22749
22750     // private
22751     defaultMode: "drop",
22752
22753     /**
22754      * Displays the shadow under the target element
22755      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22756      */
22757     show : function(target){
22758         target = Roo.get(target);
22759         if(!this.el){
22760             this.el = Roo.Shadow.Pool.pull();
22761             if(this.el.dom.nextSibling != target.dom){
22762                 this.el.insertBefore(target);
22763             }
22764         }
22765         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22766         if(Roo.isIE){
22767             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22768         }
22769         this.realign(
22770             target.getLeft(true),
22771             target.getTop(true),
22772             target.getWidth(),
22773             target.getHeight()
22774         );
22775         this.el.dom.style.display = "block";
22776     },
22777
22778     /**
22779      * Returns true if the shadow is visible, else false
22780      */
22781     isVisible : function(){
22782         return this.el ? true : false;  
22783     },
22784
22785     /**
22786      * Direct alignment when values are already available. Show must be called at least once before
22787      * calling this method to ensure it is initialized.
22788      * @param {Number} left The target element left position
22789      * @param {Number} top The target element top position
22790      * @param {Number} width The target element width
22791      * @param {Number} height The target element height
22792      */
22793     realign : function(l, t, w, h){
22794         if(!this.el){
22795             return;
22796         }
22797         var a = this.adjusts, d = this.el.dom, s = d.style;
22798         var iea = 0;
22799         s.left = (l+a.l)+"px";
22800         s.top = (t+a.t)+"px";
22801         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22802  
22803         if(s.width != sws || s.height != shs){
22804             s.width = sws;
22805             s.height = shs;
22806             if(!Roo.isIE){
22807                 var cn = d.childNodes;
22808                 var sww = Math.max(0, (sw-12))+"px";
22809                 cn[0].childNodes[1].style.width = sww;
22810                 cn[1].childNodes[1].style.width = sww;
22811                 cn[2].childNodes[1].style.width = sww;
22812                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22813             }
22814         }
22815     },
22816
22817     /**
22818      * Hides this shadow
22819      */
22820     hide : function(){
22821         if(this.el){
22822             this.el.dom.style.display = "none";
22823             Roo.Shadow.Pool.push(this.el);
22824             delete this.el;
22825         }
22826     },
22827
22828     /**
22829      * Adjust the z-index of this shadow
22830      * @param {Number} zindex The new z-index
22831      */
22832     setZIndex : function(z){
22833         this.zIndex = z;
22834         if(this.el){
22835             this.el.setStyle("z-index", z);
22836         }
22837     }
22838 };
22839
22840 // Private utility class that manages the internal Shadow cache
22841 Roo.Shadow.Pool = function(){
22842     var p = [];
22843     var markup = Roo.isIE ?
22844                  '<div class="x-ie-shadow"></div>' :
22845                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22846     return {
22847         pull : function(){
22848             var sh = p.shift();
22849             if(!sh){
22850                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22851                 sh.autoBoxAdjust = false;
22852             }
22853             return sh;
22854         },
22855
22856         push : function(sh){
22857             p.push(sh);
22858         }
22859     };
22860 }();/*
22861  * Based on:
22862  * Ext JS Library 1.1.1
22863  * Copyright(c) 2006-2007, Ext JS, LLC.
22864  *
22865  * Originally Released Under LGPL - original licence link has changed is not relivant.
22866  *
22867  * Fork - LGPL
22868  * <script type="text/javascript">
22869  */
22870
22871 /**
22872  * @class Roo.BoxComponent
22873  * @extends Roo.Component
22874  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22875  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22876  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22877  * layout containers.
22878  * @constructor
22879  * @param {Roo.Element/String/Object} config The configuration options.
22880  */
22881 Roo.BoxComponent = function(config){
22882     Roo.Component.call(this, config);
22883     this.addEvents({
22884         /**
22885          * @event resize
22886          * Fires after the component is resized.
22887              * @param {Roo.Component} this
22888              * @param {Number} adjWidth The box-adjusted width that was set
22889              * @param {Number} adjHeight The box-adjusted height that was set
22890              * @param {Number} rawWidth The width that was originally specified
22891              * @param {Number} rawHeight The height that was originally specified
22892              */
22893         resize : true,
22894         /**
22895          * @event move
22896          * Fires after the component is moved.
22897              * @param {Roo.Component} this
22898              * @param {Number} x The new x position
22899              * @param {Number} y The new y position
22900              */
22901         move : true
22902     });
22903 };
22904
22905 Roo.extend(Roo.BoxComponent, Roo.Component, {
22906     // private, set in afterRender to signify that the component has been rendered
22907     boxReady : false,
22908     // private, used to defer height settings to subclasses
22909     deferHeight: false,
22910     /** @cfg {Number} width
22911      * width (optional) size of component
22912      */
22913      /** @cfg {Number} height
22914      * height (optional) size of component
22915      */
22916      
22917     /**
22918      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22919      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22920      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22921      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22922      * @return {Roo.BoxComponent} this
22923      */
22924     setSize : function(w, h){
22925         // support for standard size objects
22926         if(typeof w == 'object'){
22927             h = w.height;
22928             w = w.width;
22929         }
22930         // not rendered
22931         if(!this.boxReady){
22932             this.width = w;
22933             this.height = h;
22934             return this;
22935         }
22936
22937         // prevent recalcs when not needed
22938         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22939             return this;
22940         }
22941         this.lastSize = {width: w, height: h};
22942
22943         var adj = this.adjustSize(w, h);
22944         var aw = adj.width, ah = adj.height;
22945         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22946             var rz = this.getResizeEl();
22947             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22948                 rz.setSize(aw, ah);
22949             }else if(!this.deferHeight && ah !== undefined){
22950                 rz.setHeight(ah);
22951             }else if(aw !== undefined){
22952                 rz.setWidth(aw);
22953             }
22954             this.onResize(aw, ah, w, h);
22955             this.fireEvent('resize', this, aw, ah, w, h);
22956         }
22957         return this;
22958     },
22959
22960     /**
22961      * Gets the current size of the component's underlying element.
22962      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22963      */
22964     getSize : function(){
22965         return this.el.getSize();
22966     },
22967
22968     /**
22969      * Gets the current XY position of the component's underlying element.
22970      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22971      * @return {Array} The XY position of the element (e.g., [100, 200])
22972      */
22973     getPosition : function(local){
22974         if(local === true){
22975             return [this.el.getLeft(true), this.el.getTop(true)];
22976         }
22977         return this.xy || this.el.getXY();
22978     },
22979
22980     /**
22981      * Gets the current box measurements of the component's underlying element.
22982      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22983      * @returns {Object} box An object in the format {x, y, width, height}
22984      */
22985     getBox : function(local){
22986         var s = this.el.getSize();
22987         if(local){
22988             s.x = this.el.getLeft(true);
22989             s.y = this.el.getTop(true);
22990         }else{
22991             var xy = this.xy || this.el.getXY();
22992             s.x = xy[0];
22993             s.y = xy[1];
22994         }
22995         return s;
22996     },
22997
22998     /**
22999      * Sets the current box measurements of the component's underlying element.
23000      * @param {Object} box An object in the format {x, y, width, height}
23001      * @returns {Roo.BoxComponent} this
23002      */
23003     updateBox : function(box){
23004         this.setSize(box.width, box.height);
23005         this.setPagePosition(box.x, box.y);
23006         return this;
23007     },
23008
23009     // protected
23010     getResizeEl : function(){
23011         return this.resizeEl || this.el;
23012     },
23013
23014     // protected
23015     getPositionEl : function(){
23016         return this.positionEl || this.el;
23017     },
23018
23019     /**
23020      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23021      * This method fires the move event.
23022      * @param {Number} left The new left
23023      * @param {Number} top The new top
23024      * @returns {Roo.BoxComponent} this
23025      */
23026     setPosition : function(x, y){
23027         this.x = x;
23028         this.y = y;
23029         if(!this.boxReady){
23030             return this;
23031         }
23032         var adj = this.adjustPosition(x, y);
23033         var ax = adj.x, ay = adj.y;
23034
23035         var el = this.getPositionEl();
23036         if(ax !== undefined || ay !== undefined){
23037             if(ax !== undefined && ay !== undefined){
23038                 el.setLeftTop(ax, ay);
23039             }else if(ax !== undefined){
23040                 el.setLeft(ax);
23041             }else if(ay !== undefined){
23042                 el.setTop(ay);
23043             }
23044             this.onPosition(ax, ay);
23045             this.fireEvent('move', this, ax, ay);
23046         }
23047         return this;
23048     },
23049
23050     /**
23051      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23052      * This method fires the move event.
23053      * @param {Number} x The new x position
23054      * @param {Number} y The new y position
23055      * @returns {Roo.BoxComponent} this
23056      */
23057     setPagePosition : function(x, y){
23058         this.pageX = x;
23059         this.pageY = y;
23060         if(!this.boxReady){
23061             return;
23062         }
23063         if(x === undefined || y === undefined){ // cannot translate undefined points
23064             return;
23065         }
23066         var p = this.el.translatePoints(x, y);
23067         this.setPosition(p.left, p.top);
23068         return this;
23069     },
23070
23071     // private
23072     onRender : function(ct, position){
23073         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23074         if(this.resizeEl){
23075             this.resizeEl = Roo.get(this.resizeEl);
23076         }
23077         if(this.positionEl){
23078             this.positionEl = Roo.get(this.positionEl);
23079         }
23080     },
23081
23082     // private
23083     afterRender : function(){
23084         Roo.BoxComponent.superclass.afterRender.call(this);
23085         this.boxReady = true;
23086         this.setSize(this.width, this.height);
23087         if(this.x || this.y){
23088             this.setPosition(this.x, this.y);
23089         }
23090         if(this.pageX || this.pageY){
23091             this.setPagePosition(this.pageX, this.pageY);
23092         }
23093     },
23094
23095     /**
23096      * Force the component's size to recalculate based on the underlying element's current height and width.
23097      * @returns {Roo.BoxComponent} this
23098      */
23099     syncSize : function(){
23100         delete this.lastSize;
23101         this.setSize(this.el.getWidth(), this.el.getHeight());
23102         return this;
23103     },
23104
23105     /**
23106      * Called after the component is resized, this method is empty by default but can be implemented by any
23107      * subclass that needs to perform custom logic after a resize occurs.
23108      * @param {Number} adjWidth The box-adjusted width that was set
23109      * @param {Number} adjHeight The box-adjusted height that was set
23110      * @param {Number} rawWidth The width that was originally specified
23111      * @param {Number} rawHeight The height that was originally specified
23112      */
23113     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23114
23115     },
23116
23117     /**
23118      * Called after the component is moved, this method is empty by default but can be implemented by any
23119      * subclass that needs to perform custom logic after a move occurs.
23120      * @param {Number} x The new x position
23121      * @param {Number} y The new y position
23122      */
23123     onPosition : function(x, y){
23124
23125     },
23126
23127     // private
23128     adjustSize : function(w, h){
23129         if(this.autoWidth){
23130             w = 'auto';
23131         }
23132         if(this.autoHeight){
23133             h = 'auto';
23134         }
23135         return {width : w, height: h};
23136     },
23137
23138     // private
23139     adjustPosition : function(x, y){
23140         return {x : x, y: y};
23141     }
23142 });/*
23143  * Based on:
23144  * Ext JS Library 1.1.1
23145  * Copyright(c) 2006-2007, Ext JS, LLC.
23146  *
23147  * Originally Released Under LGPL - original licence link has changed is not relivant.
23148  *
23149  * Fork - LGPL
23150  * <script type="text/javascript">
23151  */
23152
23153
23154 /**
23155  * @class Roo.SplitBar
23156  * @extends Roo.util.Observable
23157  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23158  * <br><br>
23159  * Usage:
23160  * <pre><code>
23161 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23162                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23163 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23164 split.minSize = 100;
23165 split.maxSize = 600;
23166 split.animate = true;
23167 split.on('moved', splitterMoved);
23168 </code></pre>
23169  * @constructor
23170  * Create a new SplitBar
23171  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23172  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23173  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23174  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23175                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23176                         position of the SplitBar).
23177  */
23178 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23179     
23180     /** @private */
23181     this.el = Roo.get(dragElement, true);
23182     this.el.dom.unselectable = "on";
23183     /** @private */
23184     this.resizingEl = Roo.get(resizingElement, true);
23185
23186     /**
23187      * @private
23188      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23189      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23190      * @type Number
23191      */
23192     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23193     
23194     /**
23195      * The minimum size of the resizing element. (Defaults to 0)
23196      * @type Number
23197      */
23198     this.minSize = 0;
23199     
23200     /**
23201      * The maximum size of the resizing element. (Defaults to 2000)
23202      * @type Number
23203      */
23204     this.maxSize = 2000;
23205     
23206     /**
23207      * Whether to animate the transition to the new size
23208      * @type Boolean
23209      */
23210     this.animate = false;
23211     
23212     /**
23213      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23214      * @type Boolean
23215      */
23216     this.useShim = false;
23217     
23218     /** @private */
23219     this.shim = null;
23220     
23221     if(!existingProxy){
23222         /** @private */
23223         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23224     }else{
23225         this.proxy = Roo.get(existingProxy).dom;
23226     }
23227     /** @private */
23228     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23229     
23230     /** @private */
23231     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23232     
23233     /** @private */
23234     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23235     
23236     /** @private */
23237     this.dragSpecs = {};
23238     
23239     /**
23240      * @private The adapter to use to positon and resize elements
23241      */
23242     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23243     this.adapter.init(this);
23244     
23245     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23246         /** @private */
23247         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23248         this.el.addClass("x-splitbar-h");
23249     }else{
23250         /** @private */
23251         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23252         this.el.addClass("x-splitbar-v");
23253     }
23254     
23255     this.addEvents({
23256         /**
23257          * @event resize
23258          * Fires when the splitter is moved (alias for {@link #event-moved})
23259          * @param {Roo.SplitBar} this
23260          * @param {Number} newSize the new width or height
23261          */
23262         "resize" : true,
23263         /**
23264          * @event moved
23265          * Fires when the splitter is moved
23266          * @param {Roo.SplitBar} this
23267          * @param {Number} newSize the new width or height
23268          */
23269         "moved" : true,
23270         /**
23271          * @event beforeresize
23272          * Fires before the splitter is dragged
23273          * @param {Roo.SplitBar} this
23274          */
23275         "beforeresize" : true,
23276
23277         "beforeapply" : true
23278     });
23279
23280     Roo.util.Observable.call(this);
23281 };
23282
23283 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23284     onStartProxyDrag : function(x, y){
23285         this.fireEvent("beforeresize", this);
23286         if(!this.overlay){
23287             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23288             o.unselectable();
23289             o.enableDisplayMode("block");
23290             // all splitbars share the same overlay
23291             Roo.SplitBar.prototype.overlay = o;
23292         }
23293         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23294         this.overlay.show();
23295         Roo.get(this.proxy).setDisplayed("block");
23296         var size = this.adapter.getElementSize(this);
23297         this.activeMinSize = this.getMinimumSize();;
23298         this.activeMaxSize = this.getMaximumSize();;
23299         var c1 = size - this.activeMinSize;
23300         var c2 = Math.max(this.activeMaxSize - size, 0);
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             this.dd.resetConstraints();
23303             this.dd.setXConstraint(
23304                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23305                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23306             );
23307             this.dd.setYConstraint(0, 0);
23308         }else{
23309             this.dd.resetConstraints();
23310             this.dd.setXConstraint(0, 0);
23311             this.dd.setYConstraint(
23312                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23313                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23314             );
23315          }
23316         this.dragSpecs.startSize = size;
23317         this.dragSpecs.startPoint = [x, y];
23318         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23319     },
23320     
23321     /** 
23322      * @private Called after the drag operation by the DDProxy
23323      */
23324     onEndProxyDrag : function(e){
23325         Roo.get(this.proxy).setDisplayed(false);
23326         var endPoint = Roo.lib.Event.getXY(e);
23327         if(this.overlay){
23328             this.overlay.hide();
23329         }
23330         var newSize;
23331         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.LEFT ?
23334                     endPoint[0] - this.dragSpecs.startPoint[0] :
23335                     this.dragSpecs.startPoint[0] - endPoint[0]
23336                 );
23337         }else{
23338             newSize = this.dragSpecs.startSize + 
23339                 (this.placement == Roo.SplitBar.TOP ?
23340                     endPoint[1] - this.dragSpecs.startPoint[1] :
23341                     this.dragSpecs.startPoint[1] - endPoint[1]
23342                 );
23343         }
23344         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23345         if(newSize != this.dragSpecs.startSize){
23346             if(this.fireEvent('beforeapply', this, newSize) !== false){
23347                 this.adapter.setElementSize(this, newSize);
23348                 this.fireEvent("moved", this, newSize);
23349                 this.fireEvent("resize", this, newSize);
23350             }
23351         }
23352     },
23353     
23354     /**
23355      * Get the adapter this SplitBar uses
23356      * @return The adapter object
23357      */
23358     getAdapter : function(){
23359         return this.adapter;
23360     },
23361     
23362     /**
23363      * Set the adapter this SplitBar uses
23364      * @param {Object} adapter A SplitBar adapter object
23365      */
23366     setAdapter : function(adapter){
23367         this.adapter = adapter;
23368         this.adapter.init(this);
23369     },
23370     
23371     /**
23372      * Gets the minimum size for the resizing element
23373      * @return {Number} The minimum size
23374      */
23375     getMinimumSize : function(){
23376         return this.minSize;
23377     },
23378     
23379     /**
23380      * Sets the minimum size for the resizing element
23381      * @param {Number} minSize The minimum size
23382      */
23383     setMinimumSize : function(minSize){
23384         this.minSize = minSize;
23385     },
23386     
23387     /**
23388      * Gets the maximum size for the resizing element
23389      * @return {Number} The maximum size
23390      */
23391     getMaximumSize : function(){
23392         return this.maxSize;
23393     },
23394     
23395     /**
23396      * Sets the maximum size for the resizing element
23397      * @param {Number} maxSize The maximum size
23398      */
23399     setMaximumSize : function(maxSize){
23400         this.maxSize = maxSize;
23401     },
23402     
23403     /**
23404      * Sets the initialize size for the resizing element
23405      * @param {Number} size The initial size
23406      */
23407     setCurrentSize : function(size){
23408         var oldAnimate = this.animate;
23409         this.animate = false;
23410         this.adapter.setElementSize(this, size);
23411         this.animate = oldAnimate;
23412     },
23413     
23414     /**
23415      * Destroy this splitbar. 
23416      * @param {Boolean} removeEl True to remove the element
23417      */
23418     destroy : function(removeEl){
23419         if(this.shim){
23420             this.shim.remove();
23421         }
23422         this.dd.unreg();
23423         this.proxy.parentNode.removeChild(this.proxy);
23424         if(removeEl){
23425             this.el.remove();
23426         }
23427     }
23428 });
23429
23430 /**
23431  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23432  */
23433 Roo.SplitBar.createProxy = function(dir){
23434     var proxy = new Roo.Element(document.createElement("div"));
23435     proxy.unselectable();
23436     var cls = 'x-splitbar-proxy';
23437     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23438     document.body.appendChild(proxy.dom);
23439     return proxy.dom;
23440 };
23441
23442 /** 
23443  * @class Roo.SplitBar.BasicLayoutAdapter
23444  * Default Adapter. It assumes the splitter and resizing element are not positioned
23445  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23446  */
23447 Roo.SplitBar.BasicLayoutAdapter = function(){
23448 };
23449
23450 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23451     // do nothing for now
23452     init : function(s){
23453     
23454     },
23455     /**
23456      * Called before drag operations to get the current size of the resizing element. 
23457      * @param {Roo.SplitBar} s The SplitBar using this adapter
23458      */
23459      getElementSize : function(s){
23460         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23461             return s.resizingEl.getWidth();
23462         }else{
23463             return s.resizingEl.getHeight();
23464         }
23465     },
23466     
23467     /**
23468      * Called after drag operations to set the size of the resizing element.
23469      * @param {Roo.SplitBar} s The SplitBar using this adapter
23470      * @param {Number} newSize The new size to set
23471      * @param {Function} onComplete A function to be invoked when resizing is complete
23472      */
23473     setElementSize : function(s, newSize, onComplete){
23474         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23475             if(!s.animate){
23476                 s.resizingEl.setWidth(newSize);
23477                 if(onComplete){
23478                     onComplete(s, newSize);
23479                 }
23480             }else{
23481                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23482             }
23483         }else{
23484             
23485             if(!s.animate){
23486                 s.resizingEl.setHeight(newSize);
23487                 if(onComplete){
23488                     onComplete(s, newSize);
23489                 }
23490             }else{
23491                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23492             }
23493         }
23494     }
23495 };
23496
23497 /** 
23498  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23499  * @extends Roo.SplitBar.BasicLayoutAdapter
23500  * Adapter that  moves the splitter element to align with the resized sizing element. 
23501  * Used with an absolute positioned SplitBar.
23502  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23503  * document.body, make sure you assign an id to the body element.
23504  */
23505 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23506     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23507     this.container = Roo.get(container);
23508 };
23509
23510 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23511     init : function(s){
23512         this.basic.init(s);
23513     },
23514     
23515     getElementSize : function(s){
23516         return this.basic.getElementSize(s);
23517     },
23518     
23519     setElementSize : function(s, newSize, onComplete){
23520         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23521     },
23522     
23523     moveSplitter : function(s){
23524         var yes = Roo.SplitBar;
23525         switch(s.placement){
23526             case yes.LEFT:
23527                 s.el.setX(s.resizingEl.getRight());
23528                 break;
23529             case yes.RIGHT:
23530                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23531                 break;
23532             case yes.TOP:
23533                 s.el.setY(s.resizingEl.getBottom());
23534                 break;
23535             case yes.BOTTOM:
23536                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23537                 break;
23538         }
23539     }
23540 };
23541
23542 /**
23543  * Orientation constant - Create a vertical SplitBar
23544  * @static
23545  * @type Number
23546  */
23547 Roo.SplitBar.VERTICAL = 1;
23548
23549 /**
23550  * Orientation constant - Create a horizontal SplitBar
23551  * @static
23552  * @type Number
23553  */
23554 Roo.SplitBar.HORIZONTAL = 2;
23555
23556 /**
23557  * Placement constant - The resizing element is to the left of the splitter element
23558  * @static
23559  * @type Number
23560  */
23561 Roo.SplitBar.LEFT = 1;
23562
23563 /**
23564  * Placement constant - The resizing element is to the right of the splitter element
23565  * @static
23566  * @type Number
23567  */
23568 Roo.SplitBar.RIGHT = 2;
23569
23570 /**
23571  * Placement constant - The resizing element is positioned above the splitter element
23572  * @static
23573  * @type Number
23574  */
23575 Roo.SplitBar.TOP = 3;
23576
23577 /**
23578  * Placement constant - The resizing element is positioned under splitter element
23579  * @static
23580  * @type Number
23581  */
23582 Roo.SplitBar.BOTTOM = 4;
23583 /*
23584  * Based on:
23585  * Ext JS Library 1.1.1
23586  * Copyright(c) 2006-2007, Ext JS, LLC.
23587  *
23588  * Originally Released Under LGPL - original licence link has changed is not relivant.
23589  *
23590  * Fork - LGPL
23591  * <script type="text/javascript">
23592  */
23593
23594 /**
23595  * @class Roo.View
23596  * @extends Roo.util.Observable
23597  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23598  * This class also supports single and multi selection modes. <br>
23599  * Create a data model bound view:
23600  <pre><code>
23601  var store = new Roo.data.Store(...);
23602
23603  var view = new Roo.View({
23604     el : "my-element",
23605     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23606  
23607     singleSelect: true,
23608     selectedClass: "ydataview-selected",
23609     store: store
23610  });
23611
23612  // listen for node click?
23613  view.on("click", function(vw, index, node, e){
23614  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23615  });
23616
23617  // load XML data
23618  dataModel.load("foobar.xml");
23619  </code></pre>
23620  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23621  * <br><br>
23622  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23623  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23624  * 
23625  * Note: old style constructor is still suported (container, template, config)
23626  * 
23627  * @constructor
23628  * Create a new View
23629  * @param {Object} config The config object
23630  * 
23631  */
23632 Roo.View = function(config, depreciated_tpl, depreciated_config){
23633     
23634     if (typeof(depreciated_tpl) == 'undefined') {
23635         // new way.. - universal constructor.
23636         Roo.apply(this, config);
23637         this.el  = Roo.get(this.el);
23638     } else {
23639         // old format..
23640         this.el  = Roo.get(config);
23641         this.tpl = depreciated_tpl;
23642         Roo.apply(this, depreciated_config);
23643     }
23644      
23645     
23646     if(typeof(this.tpl) == "string"){
23647         this.tpl = new Roo.Template(this.tpl);
23648     } else {
23649         // support xtype ctors..
23650         this.tpl = new Roo.factory(this.tpl, Roo);
23651     }
23652     
23653     
23654     this.tpl.compile();
23655    
23656
23657      
23658     /** @private */
23659     this.addEvents({
23660         /**
23661          * @event beforeclick
23662          * Fires before a click is processed. Returns false to cancel the default action.
23663          * @param {Roo.View} this
23664          * @param {Number} index The index of the target node
23665          * @param {HTMLElement} node The target node
23666          * @param {Roo.EventObject} e The raw event object
23667          */
23668             "beforeclick" : true,
23669         /**
23670          * @event click
23671          * Fires when a template node is clicked.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "click" : true,
23678         /**
23679          * @event dblclick
23680          * Fires when a template node is double clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "dblclick" : true,
23687         /**
23688          * @event contextmenu
23689          * Fires when a template node is right clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "contextmenu" : true,
23696         /**
23697          * @event selectionchange
23698          * Fires when the selected nodes change.
23699          * @param {Roo.View} this
23700          * @param {Array} selections Array of the selected nodes
23701          */
23702             "selectionchange" : true,
23703     
23704         /**
23705          * @event beforeselect
23706          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23707          * @param {Roo.View} this
23708          * @param {HTMLElement} node The node to be selected
23709          * @param {Array} selections Array of currently selected nodes
23710          */
23711             "beforeselect" : true,
23712         /**
23713          * @event preparedata
23714          * Fires on every row to render, to allow you to change the data.
23715          * @param {Roo.View} this
23716          * @param {Object} data to be rendered (change this)
23717          */
23718           "preparedata" : true
23719         });
23720
23721     this.el.on({
23722         "click": this.onClick,
23723         "dblclick": this.onDblClick,
23724         "contextmenu": this.onContextMenu,
23725         scope:this
23726     });
23727
23728     this.selections = [];
23729     this.nodes = [];
23730     this.cmp = new Roo.CompositeElementLite([]);
23731     if(this.store){
23732         this.store = Roo.factory(this.store, Roo.data);
23733         this.setStore(this.store, true);
23734     }
23735     Roo.View.superclass.constructor.call(this);
23736 };
23737
23738 Roo.extend(Roo.View, Roo.util.Observable, {
23739     
23740      /**
23741      * @cfg {Roo.data.Store} store Data store to load data from.
23742      */
23743     store : false,
23744     
23745     /**
23746      * @cfg {String|Roo.Element} el The container element.
23747      */
23748     el : '',
23749     
23750     /**
23751      * @cfg {String|Roo.Template} tpl The template used by this View 
23752      */
23753     tpl : false,
23754     
23755     /**
23756      * @cfg {String} selectedClass The css class to add to selected nodes
23757      */
23758     selectedClass : "x-view-selected",
23759      /**
23760      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23761      */
23762     emptyText : "",
23763     /**
23764      * @cfg {Boolean} multiSelect Allow multiple selection
23765      */
23766     multiSelect : false,
23767     /**
23768      * @cfg {Boolean} singleSelect Allow single selection
23769      */
23770     singleSelect:  false,
23771     
23772     /**
23773      * @cfg {Boolean} toggleSelect - selecting 
23774      */
23775     toggleSelect : false,
23776     
23777     /**
23778      * Returns the element this view is bound to.
23779      * @return {Roo.Element}
23780      */
23781     getEl : function(){
23782         return this.el;
23783     },
23784
23785     /**
23786      * Refreshes the view.
23787      */
23788     refresh : function(){
23789         var t = this.tpl;
23790         this.clearSelections();
23791         this.el.update("");
23792         var html = [];
23793         var records = this.store.getRange();
23794         if(records.length < 1){
23795             this.el.update(this.emptyText);
23796             return;
23797         }
23798         for(var i = 0, len = records.length; i < len; i++){
23799             var data = this.prepareData(records[i].data, i, records[i]);
23800             this.fireEvent("preparedata", this, data, i, records[i]);
23801             html[html.length] = t.apply(data);
23802         }
23803         this.el.update(html.join(""));
23804         this.nodes = this.el.dom.childNodes;
23805         this.updateIndexes(0);
23806     },
23807
23808     /**
23809      * Function to override to reformat the data that is sent to
23810      * the template for each node.
23811      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23812      * a JSON object for an UpdateManager bound view).
23813      */
23814     prepareData : function(data){
23815         return data;
23816     },
23817
23818     onUpdate : function(ds, record){
23819         this.clearSelections();
23820         var index = this.store.indexOf(record);
23821         var n = this.nodes[index];
23822         this.tpl.insertBefore(n, this.prepareData(record.data));
23823         n.parentNode.removeChild(n);
23824         this.updateIndexes(index, index);
23825     },
23826
23827     onAdd : function(ds, records, index){
23828         this.clearSelections();
23829         if(this.nodes.length == 0){
23830             this.refresh();
23831             return;
23832         }
23833         var n = this.nodes[index];
23834         for(var i = 0, len = records.length; i < len; i++){
23835             var d = this.prepareData(records[i].data);
23836             if(n){
23837                 this.tpl.insertBefore(n, d);
23838             }else{
23839                 this.tpl.append(this.el, d);
23840             }
23841         }
23842         this.updateIndexes(index);
23843     },
23844
23845     onRemove : function(ds, record, index){
23846         this.clearSelections();
23847         this.el.dom.removeChild(this.nodes[index]);
23848         this.updateIndexes(index);
23849     },
23850
23851     /**
23852      * Refresh an individual node.
23853      * @param {Number} index
23854      */
23855     refreshNode : function(index){
23856         this.onUpdate(this.store, this.store.getAt(index));
23857     },
23858
23859     updateIndexes : function(startIndex, endIndex){
23860         var ns = this.nodes;
23861         startIndex = startIndex || 0;
23862         endIndex = endIndex || ns.length - 1;
23863         for(var i = startIndex; i <= endIndex; i++){
23864             ns[i].nodeIndex = i;
23865         }
23866     },
23867
23868     /**
23869      * Changes the data store this view uses and refresh the view.
23870      * @param {Store} store
23871      */
23872     setStore : function(store, initial){
23873         if(!initial && this.store){
23874             this.store.un("datachanged", this.refresh);
23875             this.store.un("add", this.onAdd);
23876             this.store.un("remove", this.onRemove);
23877             this.store.un("update", this.onUpdate);
23878             this.store.un("clear", this.refresh);
23879         }
23880         if(store){
23881           
23882             store.on("datachanged", this.refresh, this);
23883             store.on("add", this.onAdd, this);
23884             store.on("remove", this.onRemove, this);
23885             store.on("update", this.onUpdate, this);
23886             store.on("clear", this.refresh, this);
23887         }
23888         
23889         if(store){
23890             this.refresh();
23891         }
23892     },
23893
23894     /**
23895      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23896      * @param {HTMLElement} node
23897      * @return {HTMLElement} The template node
23898      */
23899     findItemFromChild : function(node){
23900         var el = this.el.dom;
23901         if(!node || node.parentNode == el){
23902                     return node;
23903             }
23904             var p = node.parentNode;
23905             while(p && p != el){
23906             if(p.parentNode == el){
23907                 return p;
23908             }
23909             p = p.parentNode;
23910         }
23911             return null;
23912     },
23913
23914     /** @ignore */
23915     onClick : function(e){
23916         var item = this.findItemFromChild(e.getTarget());
23917         if(item){
23918             var index = this.indexOf(item);
23919             if(this.onItemClick(item, index, e) !== false){
23920                 this.fireEvent("click", this, index, item, e);
23921             }
23922         }else{
23923             this.clearSelections();
23924         }
23925     },
23926
23927     /** @ignore */
23928     onContextMenu : function(e){
23929         var item = this.findItemFromChild(e.getTarget());
23930         if(item){
23931             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23932         }
23933     },
23934
23935     /** @ignore */
23936     onDblClick : function(e){
23937         var item = this.findItemFromChild(e.getTarget());
23938         if(item){
23939             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23940         }
23941     },
23942
23943     onItemClick : function(item, index, e)
23944     {
23945         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23946             return false;
23947         }
23948         if (this.toggleSelect) {
23949             var m = this.isSelected(item) ? 'unselect' : 'select';
23950             Roo.log(m);
23951             var _t = this;
23952             _t[m](item, true, false);
23953             return true;
23954         }
23955         if(this.multiSelect || this.singleSelect){
23956             if(this.multiSelect && e.shiftKey && this.lastSelection){
23957                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23958             }else{
23959                 this.select(item, this.multiSelect && e.ctrlKey);
23960                 this.lastSelection = item;
23961             }
23962             e.preventDefault();
23963         }
23964         return true;
23965     },
23966
23967     /**
23968      * Get the number of selected nodes.
23969      * @return {Number}
23970      */
23971     getSelectionCount : function(){
23972         return this.selections.length;
23973     },
23974
23975     /**
23976      * Get the currently selected nodes.
23977      * @return {Array} An array of HTMLElements
23978      */
23979     getSelectedNodes : function(){
23980         return this.selections;
23981     },
23982
23983     /**
23984      * Get the indexes of the selected nodes.
23985      * @return {Array}
23986      */
23987     getSelectedIndexes : function(){
23988         var indexes = [], s = this.selections;
23989         for(var i = 0, len = s.length; i < len; i++){
23990             indexes.push(s[i].nodeIndex);
23991         }
23992         return indexes;
23993     },
23994
23995     /**
23996      * Clear all selections
23997      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23998      */
23999     clearSelections : function(suppressEvent){
24000         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24001             this.cmp.elements = this.selections;
24002             this.cmp.removeClass(this.selectedClass);
24003             this.selections = [];
24004             if(!suppressEvent){
24005                 this.fireEvent("selectionchange", this, this.selections);
24006             }
24007         }
24008     },
24009
24010     /**
24011      * Returns true if the passed node is selected
24012      * @param {HTMLElement/Number} node The node or node index
24013      * @return {Boolean}
24014      */
24015     isSelected : function(node){
24016         var s = this.selections;
24017         if(s.length < 1){
24018             return false;
24019         }
24020         node = this.getNode(node);
24021         return s.indexOf(node) !== -1;
24022     },
24023
24024     /**
24025      * Selects nodes.
24026      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24027      * @param {Boolean} keepExisting (optional) true to keep existing selections
24028      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24029      */
24030     select : function(nodeInfo, keepExisting, suppressEvent){
24031         if(nodeInfo instanceof Array){
24032             if(!keepExisting){
24033                 this.clearSelections(true);
24034             }
24035             for(var i = 0, len = nodeInfo.length; i < len; i++){
24036                 this.select(nodeInfo[i], true, true);
24037             }
24038             return;
24039         } 
24040         var node = this.getNode(nodeInfo);
24041         if(!node || this.isSelected(node)){
24042             return; // already selected.
24043         }
24044         if(!keepExisting){
24045             this.clearSelections(true);
24046         }
24047         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24048             Roo.fly(node).addClass(this.selectedClass);
24049             this.selections.push(node);
24050             if(!suppressEvent){
24051                 this.fireEvent("selectionchange", this, this.selections);
24052             }
24053         }
24054         
24055         
24056     },
24057       /**
24058      * Unselects nodes.
24059      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24060      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24061      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24062      */
24063     unselect : function(nodeInfo, keepExisting, suppressEvent)
24064     {
24065         if(nodeInfo instanceof Array){
24066             Roo.each(this.selections, function(s) {
24067                 this.unselect(s, nodeInfo);
24068             }, this);
24069             return;
24070         }
24071         var node = this.getNode(nodeInfo);
24072         if(!node || !this.isSelected(node)){
24073             Roo.log("not selected");
24074             return; // not selected.
24075         }
24076         // fireevent???
24077         var ns = [];
24078         Roo.each(this.selections, function(s) {
24079             if (s == node ) {
24080                 Roo.fly(node).removeClass(this.selectedClass);
24081
24082                 return;
24083             }
24084             ns.push(s);
24085         },this);
24086         
24087         this.selections= ns;
24088         this.fireEvent("selectionchange", this, this.selections);
24089     },
24090
24091     /**
24092      * Gets a template node.
24093      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24094      * @return {HTMLElement} The node or null if it wasn't found
24095      */
24096     getNode : function(nodeInfo){
24097         if(typeof nodeInfo == "string"){
24098             return document.getElementById(nodeInfo);
24099         }else if(typeof nodeInfo == "number"){
24100             return this.nodes[nodeInfo];
24101         }
24102         return nodeInfo;
24103     },
24104
24105     /**
24106      * Gets a range template nodes.
24107      * @param {Number} startIndex
24108      * @param {Number} endIndex
24109      * @return {Array} An array of nodes
24110      */
24111     getNodes : function(start, end){
24112         var ns = this.nodes;
24113         start = start || 0;
24114         end = typeof end == "undefined" ? ns.length - 1 : end;
24115         var nodes = [];
24116         if(start <= end){
24117             for(var i = start; i <= end; i++){
24118                 nodes.push(ns[i]);
24119             }
24120         } else{
24121             for(var i = start; i >= end; i--){
24122                 nodes.push(ns[i]);
24123             }
24124         }
24125         return nodes;
24126     },
24127
24128     /**
24129      * Finds the index of the passed node
24130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24131      * @return {Number} The index of the node or -1
24132      */
24133     indexOf : function(node){
24134         node = this.getNode(node);
24135         if(typeof node.nodeIndex == "number"){
24136             return node.nodeIndex;
24137         }
24138         var ns = this.nodes;
24139         for(var i = 0, len = ns.length; i < len; i++){
24140             if(ns[i] == node){
24141                 return i;
24142             }
24143         }
24144         return -1;
24145     }
24146 });
24147 /*
24148  * Based on:
24149  * Ext JS Library 1.1.1
24150  * Copyright(c) 2006-2007, Ext JS, LLC.
24151  *
24152  * Originally Released Under LGPL - original licence link has changed is not relivant.
24153  *
24154  * Fork - LGPL
24155  * <script type="text/javascript">
24156  */
24157
24158 /**
24159  * @class Roo.JsonView
24160  * @extends Roo.View
24161  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24162 <pre><code>
24163 var view = new Roo.JsonView({
24164     container: "my-element",
24165     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24166     multiSelect: true, 
24167     jsonRoot: "data" 
24168 });
24169
24170 // listen for node click?
24171 view.on("click", function(vw, index, node, e){
24172     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24173 });
24174
24175 // direct load of JSON data
24176 view.load("foobar.php");
24177
24178 // Example from my blog list
24179 var tpl = new Roo.Template(
24180     '&lt;div class="entry"&gt;' +
24181     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24182     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24183     "&lt;/div&gt;&lt;hr /&gt;"
24184 );
24185
24186 var moreView = new Roo.JsonView({
24187     container :  "entry-list", 
24188     template : tpl,
24189     jsonRoot: "posts"
24190 });
24191 moreView.on("beforerender", this.sortEntries, this);
24192 moreView.load({
24193     url: "/blog/get-posts.php",
24194     params: "allposts=true",
24195     text: "Loading Blog Entries..."
24196 });
24197 </code></pre>
24198
24199 * Note: old code is supported with arguments : (container, template, config)
24200
24201
24202  * @constructor
24203  * Create a new JsonView
24204  * 
24205  * @param {Object} config The config object
24206  * 
24207  */
24208 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24209     
24210     
24211     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24212
24213     var um = this.el.getUpdateManager();
24214     um.setRenderer(this);
24215     um.on("update", this.onLoad, this);
24216     um.on("failure", this.onLoadException, this);
24217
24218     /**
24219      * @event beforerender
24220      * Fires before rendering of the downloaded JSON data.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      */
24224     /**
24225      * @event load
24226      * Fires when data is loaded.
24227      * @param {Roo.JsonView} this
24228      * @param {Object} data The JSON data loaded
24229      * @param {Object} response The raw Connect response object
24230      */
24231     /**
24232      * @event loadexception
24233      * Fires when loading fails.
24234      * @param {Roo.JsonView} this
24235      * @param {Object} response The raw Connect response object
24236      */
24237     this.addEvents({
24238         'beforerender' : true,
24239         'load' : true,
24240         'loadexception' : true
24241     });
24242 };
24243 Roo.extend(Roo.JsonView, Roo.View, {
24244     /**
24245      * @type {String} The root property in the loaded JSON object that contains the data
24246      */
24247     jsonRoot : "",
24248
24249     /**
24250      * Refreshes the view.
24251      */
24252     refresh : function(){
24253         this.clearSelections();
24254         this.el.update("");
24255         var html = [];
24256         var o = this.jsonData;
24257         if(o && o.length > 0){
24258             for(var i = 0, len = o.length; i < len; i++){
24259                 var data = this.prepareData(o[i], i, o);
24260                 html[html.length] = this.tpl.apply(data);
24261             }
24262         }else{
24263             html.push(this.emptyText);
24264         }
24265         this.el.update(html.join(""));
24266         this.nodes = this.el.dom.childNodes;
24267         this.updateIndexes(0);
24268     },
24269
24270     /**
24271      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24272      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24273      <pre><code>
24274      view.load({
24275          url: "your-url.php",
24276          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24277          callback: yourFunction,
24278          scope: yourObject, //(optional scope)
24279          discardUrl: false,
24280          nocache: false,
24281          text: "Loading...",
24282          timeout: 30,
24283          scripts: false
24284      });
24285      </code></pre>
24286      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24287      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24288      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24289      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24290      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24291      */
24292     load : function(){
24293         var um = this.el.getUpdateManager();
24294         um.update.apply(um, arguments);
24295     },
24296
24297     render : function(el, response){
24298         this.clearSelections();
24299         this.el.update("");
24300         var o;
24301         try{
24302             o = Roo.util.JSON.decode(response.responseText);
24303             if(this.jsonRoot){
24304                 
24305                 o = o[this.jsonRoot];
24306             }
24307         } catch(e){
24308         }
24309         /**
24310          * The current JSON data or null
24311          */
24312         this.jsonData = o;
24313         this.beforeRender();
24314         this.refresh();
24315     },
24316
24317 /**
24318  * Get the number of records in the current JSON dataset
24319  * @return {Number}
24320  */
24321     getCount : function(){
24322         return this.jsonData ? this.jsonData.length : 0;
24323     },
24324
24325 /**
24326  * Returns the JSON object for the specified node(s)
24327  * @param {HTMLElement/Array} node The node or an array of nodes
24328  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24329  * you get the JSON object for the node
24330  */
24331     getNodeData : function(node){
24332         if(node instanceof Array){
24333             var data = [];
24334             for(var i = 0, len = node.length; i < len; i++){
24335                 data.push(this.getNodeData(node[i]));
24336             }
24337             return data;
24338         }
24339         return this.jsonData[this.indexOf(node)] || null;
24340     },
24341
24342     beforeRender : function(){
24343         this.snapshot = this.jsonData;
24344         if(this.sortInfo){
24345             this.sort.apply(this, this.sortInfo);
24346         }
24347         this.fireEvent("beforerender", this, this.jsonData);
24348     },
24349
24350     onLoad : function(el, o){
24351         this.fireEvent("load", this, this.jsonData, o);
24352     },
24353
24354     onLoadException : function(el, o){
24355         this.fireEvent("loadexception", this, o);
24356     },
24357
24358 /**
24359  * Filter the data by a specific property.
24360  * @param {String} property A property on your JSON objects
24361  * @param {String/RegExp} value Either string that the property values
24362  * should start with, or a RegExp to test against the property
24363  */
24364     filter : function(property, value){
24365         if(this.jsonData){
24366             var data = [];
24367             var ss = this.snapshot;
24368             if(typeof value == "string"){
24369                 var vlen = value.length;
24370                 if(vlen == 0){
24371                     this.clearFilter();
24372                     return;
24373                 }
24374                 value = value.toLowerCase();
24375                 for(var i = 0, len = ss.length; i < len; i++){
24376                     var o = ss[i];
24377                     if(o[property].substr(0, vlen).toLowerCase() == value){
24378                         data.push(o);
24379                     }
24380                 }
24381             } else if(value.exec){ // regex?
24382                 for(var i = 0, len = ss.length; i < len; i++){
24383                     var o = ss[i];
24384                     if(value.test(o[property])){
24385                         data.push(o);
24386                     }
24387                 }
24388             } else{
24389                 return;
24390             }
24391             this.jsonData = data;
24392             this.refresh();
24393         }
24394     },
24395
24396 /**
24397  * Filter by a function. The passed function will be called with each
24398  * object in the current dataset. If the function returns true the value is kept,
24399  * otherwise it is filtered.
24400  * @param {Function} fn
24401  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24402  */
24403     filterBy : function(fn, scope){
24404         if(this.jsonData){
24405             var data = [];
24406             var ss = this.snapshot;
24407             for(var i = 0, len = ss.length; i < len; i++){
24408                 var o = ss[i];
24409                 if(fn.call(scope || this, o)){
24410                     data.push(o);
24411                 }
24412             }
24413             this.jsonData = data;
24414             this.refresh();
24415         }
24416     },
24417
24418 /**
24419  * Clears the current filter.
24420  */
24421     clearFilter : function(){
24422         if(this.snapshot && this.jsonData != this.snapshot){
24423             this.jsonData = this.snapshot;
24424             this.refresh();
24425         }
24426     },
24427
24428
24429 /**
24430  * Sorts the data for this view and refreshes it.
24431  * @param {String} property A property on your JSON objects to sort on
24432  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24433  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24434  */
24435     sort : function(property, dir, sortType){
24436         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24437         if(this.jsonData){
24438             var p = property;
24439             var dsc = dir && dir.toLowerCase() == "desc";
24440             var f = function(o1, o2){
24441                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24442                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24443                 ;
24444                 if(v1 < v2){
24445                     return dsc ? +1 : -1;
24446                 } else if(v1 > v2){
24447                     return dsc ? -1 : +1;
24448                 } else{
24449                     return 0;
24450                 }
24451             };
24452             this.jsonData.sort(f);
24453             this.refresh();
24454             if(this.jsonData != this.snapshot){
24455                 this.snapshot.sort(f);
24456             }
24457         }
24458     }
24459 });/*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469  
24470
24471 /**
24472  * @class Roo.ColorPalette
24473  * @extends Roo.Component
24474  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24475  * Here's an example of typical usage:
24476  * <pre><code>
24477 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24478 cp.render('my-div');
24479
24480 cp.on('select', function(palette, selColor){
24481     // do something with selColor
24482 });
24483 </code></pre>
24484  * @constructor
24485  * Create a new ColorPalette
24486  * @param {Object} config The config object
24487  */
24488 Roo.ColorPalette = function(config){
24489     Roo.ColorPalette.superclass.constructor.call(this, config);
24490     this.addEvents({
24491         /**
24492              * @event select
24493              * Fires when a color is selected
24494              * @param {ColorPalette} this
24495              * @param {String} color The 6-digit color hex code (without the # symbol)
24496              */
24497         select: true
24498     });
24499
24500     if(this.handler){
24501         this.on("select", this.handler, this.scope, true);
24502     }
24503 };
24504 Roo.extend(Roo.ColorPalette, Roo.Component, {
24505     /**
24506      * @cfg {String} itemCls
24507      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24508      */
24509     itemCls : "x-color-palette",
24510     /**
24511      * @cfg {String} value
24512      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24513      * the hex codes are case-sensitive.
24514      */
24515     value : null,
24516     clickEvent:'click',
24517     // private
24518     ctype: "Roo.ColorPalette",
24519
24520     /**
24521      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24522      */
24523     allowReselect : false,
24524
24525     /**
24526      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24527      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24528      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24529      * of colors with the width setting until the box is symmetrical.</p>
24530      * <p>You can override individual colors if needed:</p>
24531      * <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors[0] = "FF0000";  // change the first box to red
24534 </code></pre>
24535
24536 Or you can provide a custom array of your own for complete control:
24537 <pre><code>
24538 var cp = new Roo.ColorPalette();
24539 cp.colors = ["000000", "993300", "333300"];
24540 </code></pre>
24541      * @type Array
24542      */
24543     colors : [
24544         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24545         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24546         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24547         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24548         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24549     ],
24550
24551     // private
24552     onRender : function(container, position){
24553         var t = new Roo.MasterTemplate(
24554             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24555         );
24556         var c = this.colors;
24557         for(var i = 0, len = c.length; i < len; i++){
24558             t.add([c[i]]);
24559         }
24560         var el = document.createElement("div");
24561         el.className = this.itemCls;
24562         t.overwrite(el);
24563         container.dom.insertBefore(el, position);
24564         this.el = Roo.get(el);
24565         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24566         if(this.clickEvent != 'click'){
24567             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24568         }
24569     },
24570
24571     // private
24572     afterRender : function(){
24573         Roo.ColorPalette.superclass.afterRender.call(this);
24574         if(this.value){
24575             var s = this.value;
24576             this.value = null;
24577             this.select(s);
24578         }
24579     },
24580
24581     // private
24582     handleClick : function(e, t){
24583         e.preventDefault();
24584         if(!this.disabled){
24585             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24586             this.select(c.toUpperCase());
24587         }
24588     },
24589
24590     /**
24591      * Selects the specified color in the palette (fires the select event)
24592      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24593      */
24594     select : function(color){
24595         color = color.replace("#", "");
24596         if(color != this.value || this.allowReselect){
24597             var el = this.el;
24598             if(this.value){
24599                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24600             }
24601             el.child("a.color-"+color).addClass("x-color-palette-sel");
24602             this.value = color;
24603             this.fireEvent("select", this, color);
24604         }
24605     }
24606 });/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616  
24617 /**
24618  * @class Roo.DatePicker
24619  * @extends Roo.Component
24620  * Simple date picker class.
24621  * @constructor
24622  * Create a new DatePicker
24623  * @param {Object} config The config object
24624  */
24625 Roo.DatePicker = function(config){
24626     Roo.DatePicker.superclass.constructor.call(this, config);
24627
24628     this.value = config && config.value ?
24629                  config.value.clearTime() : new Date().clearTime();
24630
24631     this.addEvents({
24632         /**
24633              * @event select
24634              * Fires when a date is selected
24635              * @param {DatePicker} this
24636              * @param {Date} date The selected date
24637              */
24638         'select': true,
24639         /**
24640              * @event monthchange
24641              * Fires when the displayed month changes 
24642              * @param {DatePicker} this
24643              * @param {Date} date The selected month
24644              */
24645         'monthchange': true
24646     });
24647
24648     if(this.handler){
24649         this.on("select", this.handler,  this.scope || this);
24650     }
24651     // build the disabledDatesRE
24652     if(!this.disabledDatesRE && this.disabledDates){
24653         var dd = this.disabledDates;
24654         var re = "(?:";
24655         for(var i = 0; i < dd.length; i++){
24656             re += dd[i];
24657             if(i != dd.length-1) re += "|";
24658         }
24659         this.disabledDatesRE = new RegExp(re + ")");
24660     }
24661 };
24662
24663 Roo.extend(Roo.DatePicker, Roo.Component, {
24664     /**
24665      * @cfg {String} todayText
24666      * The text to display on the button that selects the current date (defaults to "Today")
24667      */
24668     todayText : "Today",
24669     /**
24670      * @cfg {String} okText
24671      * The text to display on the ok button
24672      */
24673     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24674     /**
24675      * @cfg {String} cancelText
24676      * The text to display on the cancel button
24677      */
24678     cancelText : "Cancel",
24679     /**
24680      * @cfg {String} todayTip
24681      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24682      */
24683     todayTip : "{0} (Spacebar)",
24684     /**
24685      * @cfg {Date} minDate
24686      * Minimum allowable date (JavaScript date object, defaults to null)
24687      */
24688     minDate : null,
24689     /**
24690      * @cfg {Date} maxDate
24691      * Maximum allowable date (JavaScript date object, defaults to null)
24692      */
24693     maxDate : null,
24694     /**
24695      * @cfg {String} minText
24696      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24697      */
24698     minText : "This date is before the minimum date",
24699     /**
24700      * @cfg {String} maxText
24701      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24702      */
24703     maxText : "This date is after the maximum date",
24704     /**
24705      * @cfg {String} format
24706      * The default date format string which can be overriden for localization support.  The format must be
24707      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24708      */
24709     format : "m/d/y",
24710     /**
24711      * @cfg {Array} disabledDays
24712      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24713      */
24714     disabledDays : null,
24715     /**
24716      * @cfg {String} disabledDaysText
24717      * The tooltip to display when the date falls on a disabled day (defaults to "")
24718      */
24719     disabledDaysText : "",
24720     /**
24721      * @cfg {RegExp} disabledDatesRE
24722      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24723      */
24724     disabledDatesRE : null,
24725     /**
24726      * @cfg {String} disabledDatesText
24727      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24728      */
24729     disabledDatesText : "",
24730     /**
24731      * @cfg {Boolean} constrainToViewport
24732      * True to constrain the date picker to the viewport (defaults to true)
24733      */
24734     constrainToViewport : true,
24735     /**
24736      * @cfg {Array} monthNames
24737      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24738      */
24739     monthNames : Date.monthNames,
24740     /**
24741      * @cfg {Array} dayNames
24742      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24743      */
24744     dayNames : Date.dayNames,
24745     /**
24746      * @cfg {String} nextText
24747      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24748      */
24749     nextText: 'Next Month (Control+Right)',
24750     /**
24751      * @cfg {String} prevText
24752      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24753      */
24754     prevText: 'Previous Month (Control+Left)',
24755     /**
24756      * @cfg {String} monthYearText
24757      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24758      */
24759     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24760     /**
24761      * @cfg {Number} startDay
24762      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24763      */
24764     startDay : 0,
24765     /**
24766      * @cfg {Bool} showClear
24767      * Show a clear button (usefull for date form elements that can be blank.)
24768      */
24769     
24770     showClear: false,
24771     
24772     /**
24773      * Sets the value of the date field
24774      * @param {Date} value The date to set
24775      */
24776     setValue : function(value){
24777         var old = this.value;
24778         this.value = value.clearTime(true);
24779         if(this.el){
24780             this.update(this.value);
24781         }
24782     },
24783
24784     /**
24785      * Gets the current selected value of the date field
24786      * @return {Date} The selected date
24787      */
24788     getValue : function(){
24789         return this.value;
24790     },
24791
24792     // private
24793     focus : function(){
24794         if(this.el){
24795             this.update(this.activeDate);
24796         }
24797     },
24798
24799     // private
24800     onRender : function(container, position){
24801         var m = [
24802              '<table cellspacing="0">',
24803                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24804                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24805         var dn = this.dayNames;
24806         for(var i = 0; i < 7; i++){
24807             var d = this.startDay+i;
24808             if(d > 6){
24809                 d = d-7;
24810             }
24811             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24812         }
24813         m[m.length] = "</tr></thead><tbody><tr>";
24814         for(var i = 0; i < 42; i++) {
24815             if(i % 7 == 0 && i != 0){
24816                 m[m.length] = "</tr><tr>";
24817             }
24818             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24819         }
24820         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24821             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24822
24823         var el = document.createElement("div");
24824         el.className = "x-date-picker";
24825         el.innerHTML = m.join("");
24826
24827         container.dom.insertBefore(el, position);
24828
24829         this.el = Roo.get(el);
24830         this.eventEl = Roo.get(el.firstChild);
24831
24832         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24833             handler: this.showPrevMonth,
24834             scope: this,
24835             preventDefault:true,
24836             stopDefault:true
24837         });
24838
24839         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24840             handler: this.showNextMonth,
24841             scope: this,
24842             preventDefault:true,
24843             stopDefault:true
24844         });
24845
24846         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24847
24848         this.monthPicker = this.el.down('div.x-date-mp');
24849         this.monthPicker.enableDisplayMode('block');
24850         
24851         var kn = new Roo.KeyNav(this.eventEl, {
24852             "left" : function(e){
24853                 e.ctrlKey ?
24854                     this.showPrevMonth() :
24855                     this.update(this.activeDate.add("d", -1));
24856             },
24857
24858             "right" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextMonth() :
24861                     this.update(this.activeDate.add("d", 1));
24862             },
24863
24864             "up" : function(e){
24865                 e.ctrlKey ?
24866                     this.showNextYear() :
24867                     this.update(this.activeDate.add("d", -7));
24868             },
24869
24870             "down" : function(e){
24871                 e.ctrlKey ?
24872                     this.showPrevYear() :
24873                     this.update(this.activeDate.add("d", 7));
24874             },
24875
24876             "pageUp" : function(e){
24877                 this.showNextMonth();
24878             },
24879
24880             "pageDown" : function(e){
24881                 this.showPrevMonth();
24882             },
24883
24884             "enter" : function(e){
24885                 e.stopPropagation();
24886                 return true;
24887             },
24888
24889             scope : this
24890         });
24891
24892         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24893
24894         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24895
24896         this.el.unselectable();
24897         
24898         this.cells = this.el.select("table.x-date-inner tbody td");
24899         this.textNodes = this.el.query("table.x-date-inner tbody span");
24900
24901         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24902             text: "&#160;",
24903             tooltip: this.monthYearText
24904         });
24905
24906         this.mbtn.on('click', this.showMonthPicker, this);
24907         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24908
24909
24910         var today = (new Date()).dateFormat(this.format);
24911         
24912         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24913         if (this.showClear) {
24914             baseTb.add( new Roo.Toolbar.Fill());
24915         }
24916         baseTb.add({
24917             text: String.format(this.todayText, today),
24918             tooltip: String.format(this.todayTip, today),
24919             handler: this.selectToday,
24920             scope: this
24921         });
24922         
24923         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24924             
24925         //});
24926         if (this.showClear) {
24927             
24928             baseTb.add( new Roo.Toolbar.Fill());
24929             baseTb.add({
24930                 text: '&#160;',
24931                 cls: 'x-btn-icon x-btn-clear',
24932                 handler: function() {
24933                     //this.value = '';
24934                     this.fireEvent("select", this, '');
24935                 },
24936                 scope: this
24937             });
24938         }
24939         
24940         
24941         if(Roo.isIE){
24942             this.el.repaint();
24943         }
24944         this.update(this.value);
24945     },
24946
24947     createMonthPicker : function(){
24948         if(!this.monthPicker.dom.firstChild){
24949             var buf = ['<table border="0" cellspacing="0">'];
24950             for(var i = 0; i < 6; i++){
24951                 buf.push(
24952                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24953                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24954                     i == 0 ?
24955                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24956                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24957                 );
24958             }
24959             buf.push(
24960                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24961                     this.okText,
24962                     '</button><button type="button" class="x-date-mp-cancel">',
24963                     this.cancelText,
24964                     '</button></td></tr>',
24965                 '</table>'
24966             );
24967             this.monthPicker.update(buf.join(''));
24968             this.monthPicker.on('click', this.onMonthClick, this);
24969             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24970
24971             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24972             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24973
24974             this.mpMonths.each(function(m, a, i){
24975                 i += 1;
24976                 if((i%2) == 0){
24977                     m.dom.xmonth = 5 + Math.round(i * .5);
24978                 }else{
24979                     m.dom.xmonth = Math.round((i-1) * .5);
24980                 }
24981             });
24982         }
24983     },
24984
24985     showMonthPicker : function(){
24986         this.createMonthPicker();
24987         var size = this.el.getSize();
24988         this.monthPicker.setSize(size);
24989         this.monthPicker.child('table').setSize(size);
24990
24991         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24992         this.updateMPMonth(this.mpSelMonth);
24993         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24994         this.updateMPYear(this.mpSelYear);
24995
24996         this.monthPicker.slideIn('t', {duration:.2});
24997     },
24998
24999     updateMPYear : function(y){
25000         this.mpyear = y;
25001         var ys = this.mpYears.elements;
25002         for(var i = 1; i <= 10; i++){
25003             var td = ys[i-1], y2;
25004             if((i%2) == 0){
25005                 y2 = y + Math.round(i * .5);
25006                 td.firstChild.innerHTML = y2;
25007                 td.xyear = y2;
25008             }else{
25009                 y2 = y - (5-Math.round(i * .5));
25010                 td.firstChild.innerHTML = y2;
25011                 td.xyear = y2;
25012             }
25013             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         }
25015     },
25016
25017     updateMPMonth : function(sm){
25018         this.mpMonths.each(function(m, a, i){
25019             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25020         });
25021     },
25022
25023     selectMPMonth: function(m){
25024         
25025     },
25026
25027     onMonthClick : function(e, t){
25028         e.stopEvent();
25029         var el = new Roo.Element(t), pn;
25030         if(el.is('button.x-date-mp-cancel')){
25031             this.hideMonthPicker();
25032         }
25033         else if(el.is('button.x-date-mp-ok')){
25034             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25035             this.hideMonthPicker();
25036         }
25037         else if(pn = el.up('td.x-date-mp-month', 2)){
25038             this.mpMonths.removeClass('x-date-mp-sel');
25039             pn.addClass('x-date-mp-sel');
25040             this.mpSelMonth = pn.dom.xmonth;
25041         }
25042         else if(pn = el.up('td.x-date-mp-year', 2)){
25043             this.mpYears.removeClass('x-date-mp-sel');
25044             pn.addClass('x-date-mp-sel');
25045             this.mpSelYear = pn.dom.xyear;
25046         }
25047         else if(el.is('a.x-date-mp-prev')){
25048             this.updateMPYear(this.mpyear-10);
25049         }
25050         else if(el.is('a.x-date-mp-next')){
25051             this.updateMPYear(this.mpyear+10);
25052         }
25053     },
25054
25055     onMonthDblClick : function(e, t){
25056         e.stopEvent();
25057         var el = new Roo.Element(t), pn;
25058         if(pn = el.up('td.x-date-mp-month', 2)){
25059             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25060             this.hideMonthPicker();
25061         }
25062         else if(pn = el.up('td.x-date-mp-year', 2)){
25063             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25064             this.hideMonthPicker();
25065         }
25066     },
25067
25068     hideMonthPicker : function(disableAnim){
25069         if(this.monthPicker){
25070             if(disableAnim === true){
25071                 this.monthPicker.hide();
25072             }else{
25073                 this.monthPicker.slideOut('t', {duration:.2});
25074             }
25075         }
25076     },
25077
25078     // private
25079     showPrevMonth : function(e){
25080         this.update(this.activeDate.add("mo", -1));
25081     },
25082
25083     // private
25084     showNextMonth : function(e){
25085         this.update(this.activeDate.add("mo", 1));
25086     },
25087
25088     // private
25089     showPrevYear : function(){
25090         this.update(this.activeDate.add("y", -1));
25091     },
25092
25093     // private
25094     showNextYear : function(){
25095         this.update(this.activeDate.add("y", 1));
25096     },
25097
25098     // private
25099     handleMouseWheel : function(e){
25100         var delta = e.getWheelDelta();
25101         if(delta > 0){
25102             this.showPrevMonth();
25103             e.stopEvent();
25104         } else if(delta < 0){
25105             this.showNextMonth();
25106             e.stopEvent();
25107         }
25108     },
25109
25110     // private
25111     handleDateClick : function(e, t){
25112         e.stopEvent();
25113         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25114             this.setValue(new Date(t.dateValue));
25115             this.fireEvent("select", this, this.value);
25116         }
25117     },
25118
25119     // private
25120     selectToday : function(){
25121         this.setValue(new Date().clearTime());
25122         this.fireEvent("select", this, this.value);
25123     },
25124
25125     // private
25126     update : function(date)
25127     {
25128         var vd = this.activeDate;
25129         this.activeDate = date;
25130         if(vd && this.el){
25131             var t = date.getTime();
25132             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25133                 this.cells.removeClass("x-date-selected");
25134                 this.cells.each(function(c){
25135                    if(c.dom.firstChild.dateValue == t){
25136                        c.addClass("x-date-selected");
25137                        setTimeout(function(){
25138                             try{c.dom.firstChild.focus();}catch(e){}
25139                        }, 50);
25140                        return false;
25141                    }
25142                 });
25143                 return;
25144             }
25145         }
25146         
25147         var days = date.getDaysInMonth();
25148         var firstOfMonth = date.getFirstDateOfMonth();
25149         var startingPos = firstOfMonth.getDay()-this.startDay;
25150
25151         if(startingPos <= this.startDay){
25152             startingPos += 7;
25153         }
25154
25155         var pm = date.add("mo", -1);
25156         var prevStart = pm.getDaysInMonth()-startingPos;
25157
25158         var cells = this.cells.elements;
25159         var textEls = this.textNodes;
25160         days += startingPos;
25161
25162         // convert everything to numbers so it's fast
25163         var day = 86400000;
25164         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25165         var today = new Date().clearTime().getTime();
25166         var sel = date.clearTime().getTime();
25167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25169         var ddMatch = this.disabledDatesRE;
25170         var ddText = this.disabledDatesText;
25171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25172         var ddaysText = this.disabledDaysText;
25173         var format = this.format;
25174
25175         var setCellClass = function(cal, cell){
25176             cell.title = "";
25177             var t = d.getTime();
25178             cell.firstChild.dateValue = t;
25179             if(t == today){
25180                 cell.className += " x-date-today";
25181                 cell.title = cal.todayText;
25182             }
25183             if(t == sel){
25184                 cell.className += " x-date-selected";
25185                 setTimeout(function(){
25186                     try{cell.firstChild.focus();}catch(e){}
25187                 }, 50);
25188             }
25189             // disabling
25190             if(t < min) {
25191                 cell.className = " x-date-disabled";
25192                 cell.title = cal.minText;
25193                 return;
25194             }
25195             if(t > max) {
25196                 cell.className = " x-date-disabled";
25197                 cell.title = cal.maxText;
25198                 return;
25199             }
25200             if(ddays){
25201                 if(ddays.indexOf(d.getDay()) != -1){
25202                     cell.title = ddaysText;
25203                     cell.className = " x-date-disabled";
25204                 }
25205             }
25206             if(ddMatch && format){
25207                 var fvalue = d.dateFormat(format);
25208                 if(ddMatch.test(fvalue)){
25209                     cell.title = ddText.replace("%0", fvalue);
25210                     cell.className = " x-date-disabled";
25211                 }
25212             }
25213         };
25214
25215         var i = 0;
25216         for(; i < startingPos; i++) {
25217             textEls[i].innerHTML = (++prevStart);
25218             d.setDate(d.getDate()+1);
25219             cells[i].className = "x-date-prevday";
25220             setCellClass(this, cells[i]);
25221         }
25222         for(; i < days; i++){
25223             intDay = i - startingPos + 1;
25224             textEls[i].innerHTML = (intDay);
25225             d.setDate(d.getDate()+1);
25226             cells[i].className = "x-date-active";
25227             setCellClass(this, cells[i]);
25228         }
25229         var extraDays = 0;
25230         for(; i < 42; i++) {
25231              textEls[i].innerHTML = (++extraDays);
25232              d.setDate(d.getDate()+1);
25233              cells[i].className = "x-date-nextday";
25234              setCellClass(this, cells[i]);
25235         }
25236
25237         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25238         this.fireEvent('monthchange', this, date);
25239         
25240         if(!this.internalRender){
25241             var main = this.el.dom.firstChild;
25242             var w = main.offsetWidth;
25243             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25244             Roo.fly(main).setWidth(w);
25245             this.internalRender = true;
25246             // opera does not respect the auto grow header center column
25247             // then, after it gets a width opera refuses to recalculate
25248             // without a second pass
25249             if(Roo.isOpera && !this.secondPass){
25250                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25251                 this.secondPass = true;
25252                 this.update.defer(10, this, [date]);
25253             }
25254         }
25255         
25256         
25257     }
25258 });        /*
25259  * Based on:
25260  * Ext JS Library 1.1.1
25261  * Copyright(c) 2006-2007, Ext JS, LLC.
25262  *
25263  * Originally Released Under LGPL - original licence link has changed is not relivant.
25264  *
25265  * Fork - LGPL
25266  * <script type="text/javascript">
25267  */
25268 /**
25269  * @class Roo.TabPanel
25270  * @extends Roo.util.Observable
25271  * A lightweight tab container.
25272  * <br><br>
25273  * Usage:
25274  * <pre><code>
25275 // basic tabs 1, built from existing content
25276 var tabs = new Roo.TabPanel("tabs1");
25277 tabs.addTab("script", "View Script");
25278 tabs.addTab("markup", "View Markup");
25279 tabs.activate("script");
25280
25281 // more advanced tabs, built from javascript
25282 var jtabs = new Roo.TabPanel("jtabs");
25283 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25284
25285 // set up the UpdateManager
25286 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25287 var updater = tab2.getUpdateManager();
25288 updater.setDefaultUrl("ajax1.htm");
25289 tab2.on('activate', updater.refresh, updater, true);
25290
25291 // Use setUrl for Ajax loading
25292 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25293 tab3.setUrl("ajax2.htm", null, true);
25294
25295 // Disabled tab
25296 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25297 tab4.disable();
25298
25299 jtabs.activate("jtabs-1");
25300  * </code></pre>
25301  * @constructor
25302  * Create a new TabPanel.
25303  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25304  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25305  */
25306 Roo.TabPanel = function(container, config){
25307     /**
25308     * The container element for this TabPanel.
25309     * @type Roo.Element
25310     */
25311     this.el = Roo.get(container, true);
25312     if(config){
25313         if(typeof config == "boolean"){
25314             this.tabPosition = config ? "bottom" : "top";
25315         }else{
25316             Roo.apply(this, config);
25317         }
25318     }
25319     if(this.tabPosition == "bottom"){
25320         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25321         this.el.addClass("x-tabs-bottom");
25322     }
25323     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25324     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25325     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25326     if(Roo.isIE){
25327         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25328     }
25329     if(this.tabPosition != "bottom"){
25330         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25331          * @type Roo.Element
25332          */
25333         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25334         this.el.addClass("x-tabs-top");
25335     }
25336     this.items = [];
25337
25338     this.bodyEl.setStyle("position", "relative");
25339
25340     this.active = null;
25341     this.activateDelegate = this.activate.createDelegate(this);
25342
25343     this.addEvents({
25344         /**
25345          * @event tabchange
25346          * Fires when the active tab changes
25347          * @param {Roo.TabPanel} this
25348          * @param {Roo.TabPanelItem} activePanel The new active tab
25349          */
25350         "tabchange": true,
25351         /**
25352          * @event beforetabchange
25353          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25354          * @param {Roo.TabPanel} this
25355          * @param {Object} e Set cancel to true on this object to cancel the tab change
25356          * @param {Roo.TabPanelItem} tab The tab being changed to
25357          */
25358         "beforetabchange" : true
25359     });
25360
25361     Roo.EventManager.onWindowResize(this.onResize, this);
25362     this.cpad = this.el.getPadding("lr");
25363     this.hiddenCount = 0;
25364
25365
25366     // toolbar on the tabbar support...
25367     if (this.toolbar) {
25368         var tcfg = this.toolbar;
25369         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25370         this.toolbar = new Roo.Toolbar(tcfg);
25371         if (Roo.isSafari) {
25372             var tbl = tcfg.container.child('table', true);
25373             tbl.setAttribute('width', '100%');
25374         }
25375         
25376     }
25377    
25378
25379
25380     Roo.TabPanel.superclass.constructor.call(this);
25381 };
25382
25383 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25384     /*
25385      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25386      */
25387     tabPosition : "top",
25388     /*
25389      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25390      */
25391     currentTabWidth : 0,
25392     /*
25393      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25394      */
25395     minTabWidth : 40,
25396     /*
25397      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25398      */
25399     maxTabWidth : 250,
25400     /*
25401      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25402      */
25403     preferredTabWidth : 175,
25404     /*
25405      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25406      */
25407     resizeTabs : false,
25408     /*
25409      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25410      */
25411     monitorResize : true,
25412     /*
25413      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25414      */
25415     toolbar : false,
25416
25417     /**
25418      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25419      * @param {String} id The id of the div to use <b>or create</b>
25420      * @param {String} text The text for the tab
25421      * @param {String} content (optional) Content to put in the TabPanelItem body
25422      * @param {Boolean} closable (optional) True to create a close icon on the tab
25423      * @return {Roo.TabPanelItem} The created TabPanelItem
25424      */
25425     addTab : function(id, text, content, closable){
25426         var item = new Roo.TabPanelItem(this, id, text, closable);
25427         this.addTabItem(item);
25428         if(content){
25429             item.setContent(content);
25430         }
25431         return item;
25432     },
25433
25434     /**
25435      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25436      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25437      * @return {Roo.TabPanelItem}
25438      */
25439     getTab : function(id){
25440         return this.items[id];
25441     },
25442
25443     /**
25444      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25445      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25446      */
25447     hideTab : function(id){
25448         var t = this.items[id];
25449         if(!t.isHidden()){
25450            t.setHidden(true);
25451            this.hiddenCount++;
25452            this.autoSizeTabs();
25453         }
25454     },
25455
25456     /**
25457      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25458      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25459      */
25460     unhideTab : function(id){
25461         var t = this.items[id];
25462         if(t.isHidden()){
25463            t.setHidden(false);
25464            this.hiddenCount--;
25465            this.autoSizeTabs();
25466         }
25467     },
25468
25469     /**
25470      * Adds an existing {@link Roo.TabPanelItem}.
25471      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25472      */
25473     addTabItem : function(item){
25474         this.items[item.id] = item;
25475         this.items.push(item);
25476         if(this.resizeTabs){
25477            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25478            this.autoSizeTabs();
25479         }else{
25480             item.autoSize();
25481         }
25482     },
25483
25484     /**
25485      * Removes a {@link Roo.TabPanelItem}.
25486      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25487      */
25488     removeTab : function(id){
25489         var items = this.items;
25490         var tab = items[id];
25491         if(!tab) { return; }
25492         var index = items.indexOf(tab);
25493         if(this.active == tab && items.length > 1){
25494             var newTab = this.getNextAvailable(index);
25495             if(newTab) {
25496                 newTab.activate();
25497             }
25498         }
25499         this.stripEl.dom.removeChild(tab.pnode.dom);
25500         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25501             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25502         }
25503         items.splice(index, 1);
25504         delete this.items[tab.id];
25505         tab.fireEvent("close", tab);
25506         tab.purgeListeners();
25507         this.autoSizeTabs();
25508     },
25509
25510     getNextAvailable : function(start){
25511         var items = this.items;
25512         var index = start;
25513         // look for a next tab that will slide over to
25514         // replace the one being removed
25515         while(index < items.length){
25516             var item = items[++index];
25517             if(item && !item.isHidden()){
25518                 return item;
25519             }
25520         }
25521         // if one isn't found select the previous tab (on the left)
25522         index = start;
25523         while(index >= 0){
25524             var item = items[--index];
25525             if(item && !item.isHidden()){
25526                 return item;
25527             }
25528         }
25529         return null;
25530     },
25531
25532     /**
25533      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25534      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25535      */
25536     disableTab : function(id){
25537         var tab = this.items[id];
25538         if(tab && this.active != tab){
25539             tab.disable();
25540         }
25541     },
25542
25543     /**
25544      * Enables a {@link Roo.TabPanelItem} that is disabled.
25545      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25546      */
25547     enableTab : function(id){
25548         var tab = this.items[id];
25549         tab.enable();
25550     },
25551
25552     /**
25553      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25554      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25555      * @return {Roo.TabPanelItem} The TabPanelItem.
25556      */
25557     activate : function(id){
25558         var tab = this.items[id];
25559         if(!tab){
25560             return null;
25561         }
25562         if(tab == this.active || tab.disabled){
25563             return tab;
25564         }
25565         var e = {};
25566         this.fireEvent("beforetabchange", this, e, tab);
25567         if(e.cancel !== true && !tab.disabled){
25568             if(this.active){
25569                 this.active.hide();
25570             }
25571             this.active = this.items[id];
25572             this.active.show();
25573             this.fireEvent("tabchange", this, this.active);
25574         }
25575         return tab;
25576     },
25577
25578     /**
25579      * Gets the active {@link Roo.TabPanelItem}.
25580      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25581      */
25582     getActiveTab : function(){
25583         return this.active;
25584     },
25585
25586     /**
25587      * Updates the tab body element to fit the height of the container element
25588      * for overflow scrolling
25589      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25590      */
25591     syncHeight : function(targetHeight){
25592         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25593         var bm = this.bodyEl.getMargins();
25594         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25595         this.bodyEl.setHeight(newHeight);
25596         return newHeight;
25597     },
25598
25599     onResize : function(){
25600         if(this.monitorResize){
25601             this.autoSizeTabs();
25602         }
25603     },
25604
25605     /**
25606      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25607      */
25608     beginUpdate : function(){
25609         this.updating = true;
25610     },
25611
25612     /**
25613      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25614      */
25615     endUpdate : function(){
25616         this.updating = false;
25617         this.autoSizeTabs();
25618     },
25619
25620     /**
25621      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25622      */
25623     autoSizeTabs : function(){
25624         var count = this.items.length;
25625         var vcount = count - this.hiddenCount;
25626         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25627         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25628         var availWidth = Math.floor(w / vcount);
25629         var b = this.stripBody;
25630         if(b.getWidth() > w){
25631             var tabs = this.items;
25632             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25633             if(availWidth < this.minTabWidth){
25634                 /*if(!this.sleft){    // incomplete scrolling code
25635                     this.createScrollButtons();
25636                 }
25637                 this.showScroll();
25638                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25639             }
25640         }else{
25641             if(this.currentTabWidth < this.preferredTabWidth){
25642                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25643             }
25644         }
25645     },
25646
25647     /**
25648      * Returns the number of tabs in this TabPanel.
25649      * @return {Number}
25650      */
25651      getCount : function(){
25652          return this.items.length;
25653      },
25654
25655     /**
25656      * Resizes all the tabs to the passed width
25657      * @param {Number} The new width
25658      */
25659     setTabWidth : function(width){
25660         this.currentTabWidth = width;
25661         for(var i = 0, len = this.items.length; i < len; i++) {
25662                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25663         }
25664     },
25665
25666     /**
25667      * Destroys this TabPanel
25668      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25669      */
25670     destroy : function(removeEl){
25671         Roo.EventManager.removeResizeListener(this.onResize, this);
25672         for(var i = 0, len = this.items.length; i < len; i++){
25673             this.items[i].purgeListeners();
25674         }
25675         if(removeEl === true){
25676             this.el.update("");
25677             this.el.remove();
25678         }
25679     }
25680 });
25681
25682 /**
25683  * @class Roo.TabPanelItem
25684  * @extends Roo.util.Observable
25685  * Represents an individual item (tab plus body) in a TabPanel.
25686  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25687  * @param {String} id The id of this TabPanelItem
25688  * @param {String} text The text for the tab of this TabPanelItem
25689  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25690  */
25691 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25692     /**
25693      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25694      * @type Roo.TabPanel
25695      */
25696     this.tabPanel = tabPanel;
25697     /**
25698      * The id for this TabPanelItem
25699      * @type String
25700      */
25701     this.id = id;
25702     /** @private */
25703     this.disabled = false;
25704     /** @private */
25705     this.text = text;
25706     /** @private */
25707     this.loaded = false;
25708     this.closable = closable;
25709
25710     /**
25711      * The body element for this TabPanelItem.
25712      * @type Roo.Element
25713      */
25714     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25715     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25716     this.bodyEl.setStyle("display", "block");
25717     this.bodyEl.setStyle("zoom", "1");
25718     this.hideAction();
25719
25720     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25721     /** @private */
25722     this.el = Roo.get(els.el, true);
25723     this.inner = Roo.get(els.inner, true);
25724     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25725     this.pnode = Roo.get(els.el.parentNode, true);
25726     this.el.on("mousedown", this.onTabMouseDown, this);
25727     this.el.on("click", this.onTabClick, this);
25728     /** @private */
25729     if(closable){
25730         var c = Roo.get(els.close, true);
25731         c.dom.title = this.closeText;
25732         c.addClassOnOver("close-over");
25733         c.on("click", this.closeClick, this);
25734      }
25735
25736     this.addEvents({
25737          /**
25738          * @event activate
25739          * Fires when this tab becomes the active tab.
25740          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25741          * @param {Roo.TabPanelItem} this
25742          */
25743         "activate": true,
25744         /**
25745          * @event beforeclose
25746          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25747          * @param {Roo.TabPanelItem} this
25748          * @param {Object} e Set cancel to true on this object to cancel the close.
25749          */
25750         "beforeclose": true,
25751         /**
25752          * @event close
25753          * Fires when this tab is closed.
25754          * @param {Roo.TabPanelItem} this
25755          */
25756          "close": true,
25757         /**
25758          * @event deactivate
25759          * Fires when this tab is no longer the active tab.
25760          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25761          * @param {Roo.TabPanelItem} this
25762          */
25763          "deactivate" : true
25764     });
25765     this.hidden = false;
25766
25767     Roo.TabPanelItem.superclass.constructor.call(this);
25768 };
25769
25770 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25771     purgeListeners : function(){
25772        Roo.util.Observable.prototype.purgeListeners.call(this);
25773        this.el.removeAllListeners();
25774     },
25775     /**
25776      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25777      */
25778     show : function(){
25779         this.pnode.addClass("on");
25780         this.showAction();
25781         if(Roo.isOpera){
25782             this.tabPanel.stripWrap.repaint();
25783         }
25784         this.fireEvent("activate", this.tabPanel, this);
25785     },
25786
25787     /**
25788      * Returns true if this tab is the active tab.
25789      * @return {Boolean}
25790      */
25791     isActive : function(){
25792         return this.tabPanel.getActiveTab() == this;
25793     },
25794
25795     /**
25796      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25797      */
25798     hide : function(){
25799         this.pnode.removeClass("on");
25800         this.hideAction();
25801         this.fireEvent("deactivate", this.tabPanel, this);
25802     },
25803
25804     hideAction : function(){
25805         this.bodyEl.hide();
25806         this.bodyEl.setStyle("position", "absolute");
25807         this.bodyEl.setLeft("-20000px");
25808         this.bodyEl.setTop("-20000px");
25809     },
25810
25811     showAction : function(){
25812         this.bodyEl.setStyle("position", "relative");
25813         this.bodyEl.setTop("");
25814         this.bodyEl.setLeft("");
25815         this.bodyEl.show();
25816     },
25817
25818     /**
25819      * Set the tooltip for the tab.
25820      * @param {String} tooltip The tab's tooltip
25821      */
25822     setTooltip : function(text){
25823         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25824             this.textEl.dom.qtip = text;
25825             this.textEl.dom.removeAttribute('title');
25826         }else{
25827             this.textEl.dom.title = text;
25828         }
25829     },
25830
25831     onTabClick : function(e){
25832         e.preventDefault();
25833         this.tabPanel.activate(this.id);
25834     },
25835
25836     onTabMouseDown : function(e){
25837         e.preventDefault();
25838         this.tabPanel.activate(this.id);
25839     },
25840
25841     getWidth : function(){
25842         return this.inner.getWidth();
25843     },
25844
25845     setWidth : function(width){
25846         var iwidth = width - this.pnode.getPadding("lr");
25847         this.inner.setWidth(iwidth);
25848         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25849         this.pnode.setWidth(width);
25850     },
25851
25852     /**
25853      * Show or hide the tab
25854      * @param {Boolean} hidden True to hide or false to show.
25855      */
25856     setHidden : function(hidden){
25857         this.hidden = hidden;
25858         this.pnode.setStyle("display", hidden ? "none" : "");
25859     },
25860
25861     /**
25862      * Returns true if this tab is "hidden"
25863      * @return {Boolean}
25864      */
25865     isHidden : function(){
25866         return this.hidden;
25867     },
25868
25869     /**
25870      * Returns the text for this tab
25871      * @return {String}
25872      */
25873     getText : function(){
25874         return this.text;
25875     },
25876
25877     autoSize : function(){
25878         //this.el.beginMeasure();
25879         this.textEl.setWidth(1);
25880         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25881         //this.el.endMeasure();
25882     },
25883
25884     /**
25885      * Sets the text for the tab (Note: this also sets the tooltip text)
25886      * @param {String} text The tab's text and tooltip
25887      */
25888     setText : function(text){
25889         this.text = text;
25890         this.textEl.update(text);
25891         this.setTooltip(text);
25892         if(!this.tabPanel.resizeTabs){
25893             this.autoSize();
25894         }
25895     },
25896     /**
25897      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25898      */
25899     activate : function(){
25900         this.tabPanel.activate(this.id);
25901     },
25902
25903     /**
25904      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25905      */
25906     disable : function(){
25907         if(this.tabPanel.active != this){
25908             this.disabled = true;
25909             this.pnode.addClass("disabled");
25910         }
25911     },
25912
25913     /**
25914      * Enables this TabPanelItem if it was previously disabled.
25915      */
25916     enable : function(){
25917         this.disabled = false;
25918         this.pnode.removeClass("disabled");
25919     },
25920
25921     /**
25922      * Sets the content for this TabPanelItem.
25923      * @param {String} content The content
25924      * @param {Boolean} loadScripts true to look for and load scripts
25925      */
25926     setContent : function(content, loadScripts){
25927         this.bodyEl.update(content, loadScripts);
25928     },
25929
25930     /**
25931      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25932      * @return {Roo.UpdateManager} The UpdateManager
25933      */
25934     getUpdateManager : function(){
25935         return this.bodyEl.getUpdateManager();
25936     },
25937
25938     /**
25939      * Set a URL to be used to load the content for this TabPanelItem.
25940      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25941      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25942      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25943      * @return {Roo.UpdateManager} The UpdateManager
25944      */
25945     setUrl : function(url, params, loadOnce){
25946         if(this.refreshDelegate){
25947             this.un('activate', this.refreshDelegate);
25948         }
25949         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25950         this.on("activate", this.refreshDelegate);
25951         return this.bodyEl.getUpdateManager();
25952     },
25953
25954     /** @private */
25955     _handleRefresh : function(url, params, loadOnce){
25956         if(!loadOnce || !this.loaded){
25957             var updater = this.bodyEl.getUpdateManager();
25958             updater.update(url, params, this._setLoaded.createDelegate(this));
25959         }
25960     },
25961
25962     /**
25963      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25964      *   Will fail silently if the setUrl method has not been called.
25965      *   This does not activate the panel, just updates its content.
25966      */
25967     refresh : function(){
25968         if(this.refreshDelegate){
25969            this.loaded = false;
25970            this.refreshDelegate();
25971         }
25972     },
25973
25974     /** @private */
25975     _setLoaded : function(){
25976         this.loaded = true;
25977     },
25978
25979     /** @private */
25980     closeClick : function(e){
25981         var o = {};
25982         e.stopEvent();
25983         this.fireEvent("beforeclose", this, o);
25984         if(o.cancel !== true){
25985             this.tabPanel.removeTab(this.id);
25986         }
25987     },
25988     /**
25989      * The text displayed in the tooltip for the close icon.
25990      * @type String
25991      */
25992     closeText : "Close this tab"
25993 });
25994
25995 /** @private */
25996 Roo.TabPanel.prototype.createStrip = function(container){
25997     var strip = document.createElement("div");
25998     strip.className = "x-tabs-wrap";
25999     container.appendChild(strip);
26000     return strip;
26001 };
26002 /** @private */
26003 Roo.TabPanel.prototype.createStripList = function(strip){
26004     // div wrapper for retard IE
26005     // returns the "tr" element.
26006     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26007         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26008         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26009     return strip.firstChild.firstChild.firstChild.firstChild;
26010 };
26011 /** @private */
26012 Roo.TabPanel.prototype.createBody = function(container){
26013     var body = document.createElement("div");
26014     Roo.id(body, "tab-body");
26015     Roo.fly(body).addClass("x-tabs-body");
26016     container.appendChild(body);
26017     return body;
26018 };
26019 /** @private */
26020 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26021     var body = Roo.getDom(id);
26022     if(!body){
26023         body = document.createElement("div");
26024         body.id = id;
26025     }
26026     Roo.fly(body).addClass("x-tabs-item-body");
26027     bodyEl.insertBefore(body, bodyEl.firstChild);
26028     return body;
26029 };
26030 /** @private */
26031 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26032     var td = document.createElement("td");
26033     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26034     //stripEl.appendChild(td);
26035     if(closable){
26036         td.className = "x-tabs-closable";
26037         if(!this.closeTpl){
26038             this.closeTpl = new Roo.Template(
26039                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26040                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26041                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26042             );
26043         }
26044         var el = this.closeTpl.overwrite(td, {"text": text});
26045         var close = el.getElementsByTagName("div")[0];
26046         var inner = el.getElementsByTagName("em")[0];
26047         return {"el": el, "close": close, "inner": inner};
26048     } else {
26049         if(!this.tabTpl){
26050             this.tabTpl = new Roo.Template(
26051                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26052                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26053             );
26054         }
26055         var el = this.tabTpl.overwrite(td, {"text": text});
26056         var inner = el.getElementsByTagName("em")[0];
26057         return {"el": el, "inner": inner};
26058     }
26059 };/*
26060  * Based on:
26061  * Ext JS Library 1.1.1
26062  * Copyright(c) 2006-2007, Ext JS, LLC.
26063  *
26064  * Originally Released Under LGPL - original licence link has changed is not relivant.
26065  *
26066  * Fork - LGPL
26067  * <script type="text/javascript">
26068  */
26069
26070 /**
26071  * @class Roo.Button
26072  * @extends Roo.util.Observable
26073  * Simple Button class
26074  * @cfg {String} text The button text
26075  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26076  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26077  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26078  * @cfg {Object} scope The scope of the handler
26079  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26080  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26081  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26082  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26083  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26084  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26085    applies if enableToggle = true)
26086  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26087  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26088   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26089  * @constructor
26090  * Create a new button
26091  * @param {Object} config The config object
26092  */
26093 Roo.Button = function(renderTo, config)
26094 {
26095     if (!config) {
26096         config = renderTo;
26097         renderTo = config.renderTo || false;
26098     }
26099     
26100     Roo.apply(this, config);
26101     this.addEvents({
26102         /**
26103              * @event click
26104              * Fires when this button is clicked
26105              * @param {Button} this
26106              * @param {EventObject} e The click event
26107              */
26108             "click" : true,
26109         /**
26110              * @event toggle
26111              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26112              * @param {Button} this
26113              * @param {Boolean} pressed
26114              */
26115             "toggle" : true,
26116         /**
26117              * @event mouseover
26118              * Fires when the mouse hovers over the button
26119              * @param {Button} this
26120              * @param {Event} e The event object
26121              */
26122         'mouseover' : true,
26123         /**
26124              * @event mouseout
26125              * Fires when the mouse exits the button
26126              * @param {Button} this
26127              * @param {Event} e The event object
26128              */
26129         'mouseout': true,
26130          /**
26131              * @event render
26132              * Fires when the button is rendered
26133              * @param {Button} this
26134              */
26135         'render': true
26136     });
26137     if(this.menu){
26138         this.menu = Roo.menu.MenuMgr.get(this.menu);
26139     }
26140     // register listeners first!!  - so render can be captured..
26141     Roo.util.Observable.call(this);
26142     if(renderTo){
26143         this.render(renderTo);
26144     }
26145     
26146   
26147 };
26148
26149 Roo.extend(Roo.Button, Roo.util.Observable, {
26150     /**
26151      * 
26152      */
26153     
26154     /**
26155      * Read-only. True if this button is hidden
26156      * @type Boolean
26157      */
26158     hidden : false,
26159     /**
26160      * Read-only. True if this button is disabled
26161      * @type Boolean
26162      */
26163     disabled : false,
26164     /**
26165      * Read-only. True if this button is pressed (only if enableToggle = true)
26166      * @type Boolean
26167      */
26168     pressed : false,
26169
26170     /**
26171      * @cfg {Number} tabIndex 
26172      * The DOM tabIndex for this button (defaults to undefined)
26173      */
26174     tabIndex : undefined,
26175
26176     /**
26177      * @cfg {Boolean} enableToggle
26178      * True to enable pressed/not pressed toggling (defaults to false)
26179      */
26180     enableToggle: false,
26181     /**
26182      * @cfg {Mixed} menu
26183      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26184      */
26185     menu : undefined,
26186     /**
26187      * @cfg {String} menuAlign
26188      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26189      */
26190     menuAlign : "tl-bl?",
26191
26192     /**
26193      * @cfg {String} iconCls
26194      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26195      */
26196     iconCls : undefined,
26197     /**
26198      * @cfg {String} type
26199      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26200      */
26201     type : 'button',
26202
26203     // private
26204     menuClassTarget: 'tr',
26205
26206     /**
26207      * @cfg {String} clickEvent
26208      * The type of event to map to the button's event handler (defaults to 'click')
26209      */
26210     clickEvent : 'click',
26211
26212     /**
26213      * @cfg {Boolean} handleMouseEvents
26214      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26215      */
26216     handleMouseEvents : true,
26217
26218     /**
26219      * @cfg {String} tooltipType
26220      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26221      */
26222     tooltipType : 'qtip',
26223
26224     /**
26225      * @cfg {String} cls
26226      * A CSS class to apply to the button's main element.
26227      */
26228     
26229     /**
26230      * @cfg {Roo.Template} template (Optional)
26231      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26232      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26233      * require code modifications if required elements (e.g. a button) aren't present.
26234      */
26235
26236     // private
26237     render : function(renderTo){
26238         var btn;
26239         if(this.hideParent){
26240             this.parentEl = Roo.get(renderTo);
26241         }
26242         if(!this.dhconfig){
26243             if(!this.template){
26244                 if(!Roo.Button.buttonTemplate){
26245                     // hideous table template
26246                     Roo.Button.buttonTemplate = new Roo.Template(
26247                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26248                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26249                         "</tr></tbody></table>");
26250                 }
26251                 this.template = Roo.Button.buttonTemplate;
26252             }
26253             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26254             var btnEl = btn.child("button:first");
26255             btnEl.on('focus', this.onFocus, this);
26256             btnEl.on('blur', this.onBlur, this);
26257             if(this.cls){
26258                 btn.addClass(this.cls);
26259             }
26260             if(this.icon){
26261                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26262             }
26263             if(this.iconCls){
26264                 btnEl.addClass(this.iconCls);
26265                 if(!this.cls){
26266                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26267                 }
26268             }
26269             if(this.tabIndex !== undefined){
26270                 btnEl.dom.tabIndex = this.tabIndex;
26271             }
26272             if(this.tooltip){
26273                 if(typeof this.tooltip == 'object'){
26274                     Roo.QuickTips.tips(Roo.apply({
26275                           target: btnEl.id
26276                     }, this.tooltip));
26277                 } else {
26278                     btnEl.dom[this.tooltipType] = this.tooltip;
26279                 }
26280             }
26281         }else{
26282             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26283         }
26284         this.el = btn;
26285         if(this.id){
26286             this.el.dom.id = this.el.id = this.id;
26287         }
26288         if(this.menu){
26289             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26290             this.menu.on("show", this.onMenuShow, this);
26291             this.menu.on("hide", this.onMenuHide, this);
26292         }
26293         btn.addClass("x-btn");
26294         if(Roo.isIE && !Roo.isIE7){
26295             this.autoWidth.defer(1, this);
26296         }else{
26297             this.autoWidth();
26298         }
26299         if(this.handleMouseEvents){
26300             btn.on("mouseover", this.onMouseOver, this);
26301             btn.on("mouseout", this.onMouseOut, this);
26302             btn.on("mousedown", this.onMouseDown, this);
26303         }
26304         btn.on(this.clickEvent, this.onClick, this);
26305         //btn.on("mouseup", this.onMouseUp, this);
26306         if(this.hidden){
26307             this.hide();
26308         }
26309         if(this.disabled){
26310             this.disable();
26311         }
26312         Roo.ButtonToggleMgr.register(this);
26313         if(this.pressed){
26314             this.el.addClass("x-btn-pressed");
26315         }
26316         if(this.repeat){
26317             var repeater = new Roo.util.ClickRepeater(btn,
26318                 typeof this.repeat == "object" ? this.repeat : {}
26319             );
26320             repeater.on("click", this.onClick,  this);
26321         }
26322         
26323         this.fireEvent('render', this);
26324         
26325     },
26326     /**
26327      * Returns the button's underlying element
26328      * @return {Roo.Element} The element
26329      */
26330     getEl : function(){
26331         return this.el;  
26332     },
26333     
26334     /**
26335      * Destroys this Button and removes any listeners.
26336      */
26337     destroy : function(){
26338         Roo.ButtonToggleMgr.unregister(this);
26339         this.el.removeAllListeners();
26340         this.purgeListeners();
26341         this.el.remove();
26342     },
26343
26344     // private
26345     autoWidth : function(){
26346         if(this.el){
26347             this.el.setWidth("auto");
26348             if(Roo.isIE7 && Roo.isStrict){
26349                 var ib = this.el.child('button');
26350                 if(ib && ib.getWidth() > 20){
26351                     ib.clip();
26352                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26353                 }
26354             }
26355             if(this.minWidth){
26356                 if(this.hidden){
26357                     this.el.beginMeasure();
26358                 }
26359                 if(this.el.getWidth() < this.minWidth){
26360                     this.el.setWidth(this.minWidth);
26361                 }
26362                 if(this.hidden){
26363                     this.el.endMeasure();
26364                 }
26365             }
26366         }
26367     },
26368
26369     /**
26370      * Assigns this button's click handler
26371      * @param {Function} handler The function to call when the button is clicked
26372      * @param {Object} scope (optional) Scope for the function passed in
26373      */
26374     setHandler : function(handler, scope){
26375         this.handler = handler;
26376         this.scope = scope;  
26377     },
26378     
26379     /**
26380      * Sets this button's text
26381      * @param {String} text The button text
26382      */
26383     setText : function(text){
26384         this.text = text;
26385         if(this.el){
26386             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26387         }
26388         this.autoWidth();
26389     },
26390     
26391     /**
26392      * Gets the text for this button
26393      * @return {String} The button text
26394      */
26395     getText : function(){
26396         return this.text;  
26397     },
26398     
26399     /**
26400      * Show this button
26401      */
26402     show: function(){
26403         this.hidden = false;
26404         if(this.el){
26405             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26406         }
26407     },
26408     
26409     /**
26410      * Hide this button
26411      */
26412     hide: function(){
26413         this.hidden = true;
26414         if(this.el){
26415             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26416         }
26417     },
26418     
26419     /**
26420      * Convenience function for boolean show/hide
26421      * @param {Boolean} visible True to show, false to hide
26422      */
26423     setVisible: function(visible){
26424         if(visible) {
26425             this.show();
26426         }else{
26427             this.hide();
26428         }
26429     },
26430     
26431     /**
26432      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26433      * @param {Boolean} state (optional) Force a particular state
26434      */
26435     toggle : function(state){
26436         state = state === undefined ? !this.pressed : state;
26437         if(state != this.pressed){
26438             if(state){
26439                 this.el.addClass("x-btn-pressed");
26440                 this.pressed = true;
26441                 this.fireEvent("toggle", this, true);
26442             }else{
26443                 this.el.removeClass("x-btn-pressed");
26444                 this.pressed = false;
26445                 this.fireEvent("toggle", this, false);
26446             }
26447             if(this.toggleHandler){
26448                 this.toggleHandler.call(this.scope || this, this, state);
26449             }
26450         }
26451     },
26452     
26453     /**
26454      * Focus the button
26455      */
26456     focus : function(){
26457         this.el.child('button:first').focus();
26458     },
26459     
26460     /**
26461      * Disable this button
26462      */
26463     disable : function(){
26464         if(this.el){
26465             this.el.addClass("x-btn-disabled");
26466         }
26467         this.disabled = true;
26468     },
26469     
26470     /**
26471      * Enable this button
26472      */
26473     enable : function(){
26474         if(this.el){
26475             this.el.removeClass("x-btn-disabled");
26476         }
26477         this.disabled = false;
26478     },
26479
26480     /**
26481      * Convenience function for boolean enable/disable
26482      * @param {Boolean} enabled True to enable, false to disable
26483      */
26484     setDisabled : function(v){
26485         this[v !== true ? "enable" : "disable"]();
26486     },
26487
26488     // private
26489     onClick : function(e){
26490         if(e){
26491             e.preventDefault();
26492         }
26493         if(e.button != 0){
26494             return;
26495         }
26496         if(!this.disabled){
26497             if(this.enableToggle){
26498                 this.toggle();
26499             }
26500             if(this.menu && !this.menu.isVisible()){
26501                 this.menu.show(this.el, this.menuAlign);
26502             }
26503             this.fireEvent("click", this, e);
26504             if(this.handler){
26505                 this.el.removeClass("x-btn-over");
26506                 this.handler.call(this.scope || this, this, e);
26507             }
26508         }
26509     },
26510     // private
26511     onMouseOver : function(e){
26512         if(!this.disabled){
26513             this.el.addClass("x-btn-over");
26514             this.fireEvent('mouseover', this, e);
26515         }
26516     },
26517     // private
26518     onMouseOut : function(e){
26519         if(!e.within(this.el,  true)){
26520             this.el.removeClass("x-btn-over");
26521             this.fireEvent('mouseout', this, e);
26522         }
26523     },
26524     // private
26525     onFocus : function(e){
26526         if(!this.disabled){
26527             this.el.addClass("x-btn-focus");
26528         }
26529     },
26530     // private
26531     onBlur : function(e){
26532         this.el.removeClass("x-btn-focus");
26533     },
26534     // private
26535     onMouseDown : function(e){
26536         if(!this.disabled && e.button == 0){
26537             this.el.addClass("x-btn-click");
26538             Roo.get(document).on('mouseup', this.onMouseUp, this);
26539         }
26540     },
26541     // private
26542     onMouseUp : function(e){
26543         if(e.button == 0){
26544             this.el.removeClass("x-btn-click");
26545             Roo.get(document).un('mouseup', this.onMouseUp, this);
26546         }
26547     },
26548     // private
26549     onMenuShow : function(e){
26550         this.el.addClass("x-btn-menu-active");
26551     },
26552     // private
26553     onMenuHide : function(e){
26554         this.el.removeClass("x-btn-menu-active");
26555     }   
26556 });
26557
26558 // Private utility class used by Button
26559 Roo.ButtonToggleMgr = function(){
26560    var groups = {};
26561    
26562    function toggleGroup(btn, state){
26563        if(state){
26564            var g = groups[btn.toggleGroup];
26565            for(var i = 0, l = g.length; i < l; i++){
26566                if(g[i] != btn){
26567                    g[i].toggle(false);
26568                }
26569            }
26570        }
26571    }
26572    
26573    return {
26574        register : function(btn){
26575            if(!btn.toggleGroup){
26576                return;
26577            }
26578            var g = groups[btn.toggleGroup];
26579            if(!g){
26580                g = groups[btn.toggleGroup] = [];
26581            }
26582            g.push(btn);
26583            btn.on("toggle", toggleGroup);
26584        },
26585        
26586        unregister : function(btn){
26587            if(!btn.toggleGroup){
26588                return;
26589            }
26590            var g = groups[btn.toggleGroup];
26591            if(g){
26592                g.remove(btn);
26593                btn.un("toggle", toggleGroup);
26594            }
26595        }
26596    };
26597 }();/*
26598  * Based on:
26599  * Ext JS Library 1.1.1
26600  * Copyright(c) 2006-2007, Ext JS, LLC.
26601  *
26602  * Originally Released Under LGPL - original licence link has changed is not relivant.
26603  *
26604  * Fork - LGPL
26605  * <script type="text/javascript">
26606  */
26607  
26608 /**
26609  * @class Roo.SplitButton
26610  * @extends Roo.Button
26611  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26612  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26613  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26614  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26615  * @cfg {String} arrowTooltip The title attribute of the arrow
26616  * @constructor
26617  * Create a new menu button
26618  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26619  * @param {Object} config The config object
26620  */
26621 Roo.SplitButton = function(renderTo, config){
26622     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26623     /**
26624      * @event arrowclick
26625      * Fires when this button's arrow is clicked
26626      * @param {SplitButton} this
26627      * @param {EventObject} e The click event
26628      */
26629     this.addEvents({"arrowclick":true});
26630 };
26631
26632 Roo.extend(Roo.SplitButton, Roo.Button, {
26633     render : function(renderTo){
26634         // this is one sweet looking template!
26635         var tpl = new Roo.Template(
26636             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26637             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26638             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26639             "</tbody></table></td><td>",
26640             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26641             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26642             "</tbody></table></td></tr></table>"
26643         );
26644         var btn = tpl.append(renderTo, [this.text, this.type], true);
26645         var btnEl = btn.child("button");
26646         if(this.cls){
26647             btn.addClass(this.cls);
26648         }
26649         if(this.icon){
26650             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26651         }
26652         if(this.iconCls){
26653             btnEl.addClass(this.iconCls);
26654             if(!this.cls){
26655                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26656             }
26657         }
26658         this.el = btn;
26659         if(this.handleMouseEvents){
26660             btn.on("mouseover", this.onMouseOver, this);
26661             btn.on("mouseout", this.onMouseOut, this);
26662             btn.on("mousedown", this.onMouseDown, this);
26663             btn.on("mouseup", this.onMouseUp, this);
26664         }
26665         btn.on(this.clickEvent, this.onClick, this);
26666         if(this.tooltip){
26667             if(typeof this.tooltip == 'object'){
26668                 Roo.QuickTips.tips(Roo.apply({
26669                       target: btnEl.id
26670                 }, this.tooltip));
26671             } else {
26672                 btnEl.dom[this.tooltipType] = this.tooltip;
26673             }
26674         }
26675         if(this.arrowTooltip){
26676             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26677         }
26678         if(this.hidden){
26679             this.hide();
26680         }
26681         if(this.disabled){
26682             this.disable();
26683         }
26684         if(this.pressed){
26685             this.el.addClass("x-btn-pressed");
26686         }
26687         if(Roo.isIE && !Roo.isIE7){
26688             this.autoWidth.defer(1, this);
26689         }else{
26690             this.autoWidth();
26691         }
26692         if(this.menu){
26693             this.menu.on("show", this.onMenuShow, this);
26694             this.menu.on("hide", this.onMenuHide, this);
26695         }
26696         this.fireEvent('render', this);
26697     },
26698
26699     // private
26700     autoWidth : function(){
26701         if(this.el){
26702             var tbl = this.el.child("table:first");
26703             var tbl2 = this.el.child("table:last");
26704             this.el.setWidth("auto");
26705             tbl.setWidth("auto");
26706             if(Roo.isIE7 && Roo.isStrict){
26707                 var ib = this.el.child('button:first');
26708                 if(ib && ib.getWidth() > 20){
26709                     ib.clip();
26710                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26711                 }
26712             }
26713             if(this.minWidth){
26714                 if(this.hidden){
26715                     this.el.beginMeasure();
26716                 }
26717                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26718                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26719                 }
26720                 if(this.hidden){
26721                     this.el.endMeasure();
26722                 }
26723             }
26724             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26725         } 
26726     },
26727     /**
26728      * Sets this button's click handler
26729      * @param {Function} handler The function to call when the button is clicked
26730      * @param {Object} scope (optional) Scope for the function passed above
26731      */
26732     setHandler : function(handler, scope){
26733         this.handler = handler;
26734         this.scope = scope;  
26735     },
26736     
26737     /**
26738      * Sets this button's arrow click handler
26739      * @param {Function} handler The function to call when the arrow is clicked
26740      * @param {Object} scope (optional) Scope for the function passed above
26741      */
26742     setArrowHandler : function(handler, scope){
26743         this.arrowHandler = handler;
26744         this.scope = scope;  
26745     },
26746     
26747     /**
26748      * Focus the button
26749      */
26750     focus : function(){
26751         if(this.el){
26752             this.el.child("button:first").focus();
26753         }
26754     },
26755
26756     // private
26757     onClick : function(e){
26758         e.preventDefault();
26759         if(!this.disabled){
26760             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26761                 if(this.menu && !this.menu.isVisible()){
26762                     this.menu.show(this.el, this.menuAlign);
26763                 }
26764                 this.fireEvent("arrowclick", this, e);
26765                 if(this.arrowHandler){
26766                     this.arrowHandler.call(this.scope || this, this, e);
26767                 }
26768             }else{
26769                 this.fireEvent("click", this, e);
26770                 if(this.handler){
26771                     this.handler.call(this.scope || this, this, e);
26772                 }
26773             }
26774         }
26775     },
26776     // private
26777     onMouseDown : function(e){
26778         if(!this.disabled){
26779             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26780         }
26781     },
26782     // private
26783     onMouseUp : function(e){
26784         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26785     }   
26786 });
26787
26788
26789 // backwards compat
26790 Roo.MenuButton = Roo.SplitButton;/*
26791  * Based on:
26792  * Ext JS Library 1.1.1
26793  * Copyright(c) 2006-2007, Ext JS, LLC.
26794  *
26795  * Originally Released Under LGPL - original licence link has changed is not relivant.
26796  *
26797  * Fork - LGPL
26798  * <script type="text/javascript">
26799  */
26800
26801 /**
26802  * @class Roo.Toolbar
26803  * Basic Toolbar class.
26804  * @constructor
26805  * Creates a new Toolbar
26806  * @param {Object} container The config object
26807  */ 
26808 Roo.Toolbar = function(container, buttons, config)
26809 {
26810     /// old consturctor format still supported..
26811     if(container instanceof Array){ // omit the container for later rendering
26812         buttons = container;
26813         config = buttons;
26814         container = null;
26815     }
26816     if (typeof(container) == 'object' && container.xtype) {
26817         config = container;
26818         container = config.container;
26819         buttons = config.buttons || []; // not really - use items!!
26820     }
26821     var xitems = [];
26822     if (config && config.items) {
26823         xitems = config.items;
26824         delete config.items;
26825     }
26826     Roo.apply(this, config);
26827     this.buttons = buttons;
26828     
26829     if(container){
26830         this.render(container);
26831     }
26832     this.xitems = xitems;
26833     Roo.each(xitems, function(b) {
26834         this.add(b);
26835     }, this);
26836     
26837 };
26838
26839 Roo.Toolbar.prototype = {
26840     /**
26841      * @cfg {Array} items
26842      * array of button configs or elements to add (will be converted to a MixedCollection)
26843      */
26844     
26845     /**
26846      * @cfg {String/HTMLElement/Element} container
26847      * The id or element that will contain the toolbar
26848      */
26849     // private
26850     render : function(ct){
26851         this.el = Roo.get(ct);
26852         if(this.cls){
26853             this.el.addClass(this.cls);
26854         }
26855         // using a table allows for vertical alignment
26856         // 100% width is needed by Safari...
26857         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26858         this.tr = this.el.child("tr", true);
26859         var autoId = 0;
26860         this.items = new Roo.util.MixedCollection(false, function(o){
26861             return o.id || ("item" + (++autoId));
26862         });
26863         if(this.buttons){
26864             this.add.apply(this, this.buttons);
26865             delete this.buttons;
26866         }
26867     },
26868
26869     /**
26870      * Adds element(s) to the toolbar -- this function takes a variable number of 
26871      * arguments of mixed type and adds them to the toolbar.
26872      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26873      * <ul>
26874      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26875      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26876      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26877      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26878      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26879      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26880      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26881      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26882      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26883      * </ul>
26884      * @param {Mixed} arg2
26885      * @param {Mixed} etc.
26886      */
26887     add : function(){
26888         var a = arguments, l = a.length;
26889         for(var i = 0; i < l; i++){
26890             this._add(a[i]);
26891         }
26892     },
26893     // private..
26894     _add : function(el) {
26895         
26896         if (el.xtype) {
26897             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26898         }
26899         
26900         if (el.applyTo){ // some kind of form field
26901             return this.addField(el);
26902         } 
26903         if (el.render){ // some kind of Toolbar.Item
26904             return this.addItem(el);
26905         }
26906         if (typeof el == "string"){ // string
26907             if(el == "separator" || el == "-"){
26908                 return this.addSeparator();
26909             }
26910             if (el == " "){
26911                 return this.addSpacer();
26912             }
26913             if(el == "->"){
26914                 return this.addFill();
26915             }
26916             return this.addText(el);
26917             
26918         }
26919         if(el.tagName){ // element
26920             return this.addElement(el);
26921         }
26922         if(typeof el == "object"){ // must be button config?
26923             return this.addButton(el);
26924         }
26925         // and now what?!?!
26926         return false;
26927         
26928     },
26929     
26930     /**
26931      * Add an Xtype element
26932      * @param {Object} xtype Xtype Object
26933      * @return {Object} created Object
26934      */
26935     addxtype : function(e){
26936         return this.add(e);  
26937     },
26938     
26939     /**
26940      * Returns the Element for this toolbar.
26941      * @return {Roo.Element}
26942      */
26943     getEl : function(){
26944         return this.el;  
26945     },
26946     
26947     /**
26948      * Adds a separator
26949      * @return {Roo.Toolbar.Item} The separator item
26950      */
26951     addSeparator : function(){
26952         return this.addItem(new Roo.Toolbar.Separator());
26953     },
26954
26955     /**
26956      * Adds a spacer element
26957      * @return {Roo.Toolbar.Spacer} The spacer item
26958      */
26959     addSpacer : function(){
26960         return this.addItem(new Roo.Toolbar.Spacer());
26961     },
26962
26963     /**
26964      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26965      * @return {Roo.Toolbar.Fill} The fill item
26966      */
26967     addFill : function(){
26968         return this.addItem(new Roo.Toolbar.Fill());
26969     },
26970
26971     /**
26972      * Adds any standard HTML element to the toolbar
26973      * @param {String/HTMLElement/Element} el The element or id of the element to add
26974      * @return {Roo.Toolbar.Item} The element's item
26975      */
26976     addElement : function(el){
26977         return this.addItem(new Roo.Toolbar.Item(el));
26978     },
26979     /**
26980      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26981      * @type Roo.util.MixedCollection  
26982      */
26983     items : false,
26984      
26985     /**
26986      * Adds any Toolbar.Item or subclass
26987      * @param {Roo.Toolbar.Item} item
26988      * @return {Roo.Toolbar.Item} The item
26989      */
26990     addItem : function(item){
26991         var td = this.nextBlock();
26992         item.render(td);
26993         this.items.add(item);
26994         return item;
26995     },
26996     
26997     /**
26998      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26999      * @param {Object/Array} config A button config or array of configs
27000      * @return {Roo.Toolbar.Button/Array}
27001      */
27002     addButton : function(config){
27003         if(config instanceof Array){
27004             var buttons = [];
27005             for(var i = 0, len = config.length; i < len; i++) {
27006                 buttons.push(this.addButton(config[i]));
27007             }
27008             return buttons;
27009         }
27010         var b = config;
27011         if(!(config instanceof Roo.Toolbar.Button)){
27012             b = config.split ?
27013                 new Roo.Toolbar.SplitButton(config) :
27014                 new Roo.Toolbar.Button(config);
27015         }
27016         var td = this.nextBlock();
27017         b.render(td);
27018         this.items.add(b);
27019         return b;
27020     },
27021     
27022     /**
27023      * Adds text to the toolbar
27024      * @param {String} text The text to add
27025      * @return {Roo.Toolbar.Item} The element's item
27026      */
27027     addText : function(text){
27028         return this.addItem(new Roo.Toolbar.TextItem(text));
27029     },
27030     
27031     /**
27032      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27033      * @param {Number} index The index where the item is to be inserted
27034      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27035      * @return {Roo.Toolbar.Button/Item}
27036      */
27037     insertButton : function(index, item){
27038         if(item instanceof Array){
27039             var buttons = [];
27040             for(var i = 0, len = item.length; i < len; i++) {
27041                buttons.push(this.insertButton(index + i, item[i]));
27042             }
27043             return buttons;
27044         }
27045         if (!(item instanceof Roo.Toolbar.Button)){
27046            item = new Roo.Toolbar.Button(item);
27047         }
27048         var td = document.createElement("td");
27049         this.tr.insertBefore(td, this.tr.childNodes[index]);
27050         item.render(td);
27051         this.items.insert(index, item);
27052         return item;
27053     },
27054     
27055     /**
27056      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27057      * @param {Object} config
27058      * @return {Roo.Toolbar.Item} The element's item
27059      */
27060     addDom : function(config, returnEl){
27061         var td = this.nextBlock();
27062         Roo.DomHelper.overwrite(td, config);
27063         var ti = new Roo.Toolbar.Item(td.firstChild);
27064         ti.render(td);
27065         this.items.add(ti);
27066         return ti;
27067     },
27068
27069     /**
27070      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27071      * @type Roo.util.MixedCollection  
27072      */
27073     fields : false,
27074     
27075     /**
27076      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27077      * Note: the field should not have been rendered yet. For a field that has already been
27078      * rendered, use {@link #addElement}.
27079      * @param {Roo.form.Field} field
27080      * @return {Roo.ToolbarItem}
27081      */
27082      
27083       
27084     addField : function(field) {
27085         if (!this.fields) {
27086             var autoId = 0;
27087             this.fields = new Roo.util.MixedCollection(false, function(o){
27088                 return o.id || ("item" + (++autoId));
27089             });
27090
27091         }
27092         
27093         var td = this.nextBlock();
27094         field.render(td);
27095         var ti = new Roo.Toolbar.Item(td.firstChild);
27096         ti.render(td);
27097         this.items.add(ti);
27098         this.fields.add(field);
27099         return ti;
27100     },
27101     /**
27102      * Hide the toolbar
27103      * @method hide
27104      */
27105      
27106       
27107     hide : function()
27108     {
27109         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27110         this.el.child('div').hide();
27111     },
27112     /**
27113      * Show the toolbar
27114      * @method show
27115      */
27116     show : function()
27117     {
27118         this.el.child('div').show();
27119     },
27120       
27121     // private
27122     nextBlock : function(){
27123         var td = document.createElement("td");
27124         this.tr.appendChild(td);
27125         return td;
27126     },
27127
27128     // private
27129     destroy : function(){
27130         if(this.items){ // rendered?
27131             Roo.destroy.apply(Roo, this.items.items);
27132         }
27133         if(this.fields){ // rendered?
27134             Roo.destroy.apply(Roo, this.fields.items);
27135         }
27136         Roo.Element.uncache(this.el, this.tr);
27137     }
27138 };
27139
27140 /**
27141  * @class Roo.Toolbar.Item
27142  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27143  * @constructor
27144  * Creates a new Item
27145  * @param {HTMLElement} el 
27146  */
27147 Roo.Toolbar.Item = function(el){
27148     this.el = Roo.getDom(el);
27149     this.id = Roo.id(this.el);
27150     this.hidden = false;
27151 };
27152
27153 Roo.Toolbar.Item.prototype = {
27154     
27155     /**
27156      * Get this item's HTML Element
27157      * @return {HTMLElement}
27158      */
27159     getEl : function(){
27160        return this.el;  
27161     },
27162
27163     // private
27164     render : function(td){
27165         this.td = td;
27166         td.appendChild(this.el);
27167     },
27168     
27169     /**
27170      * Removes and destroys this item.
27171      */
27172     destroy : function(){
27173         this.td.parentNode.removeChild(this.td);
27174     },
27175     
27176     /**
27177      * Shows this item.
27178      */
27179     show: function(){
27180         this.hidden = false;
27181         this.td.style.display = "";
27182     },
27183     
27184     /**
27185      * Hides this item.
27186      */
27187     hide: function(){
27188         this.hidden = true;
27189         this.td.style.display = "none";
27190     },
27191     
27192     /**
27193      * Convenience function for boolean show/hide.
27194      * @param {Boolean} visible true to show/false to hide
27195      */
27196     setVisible: function(visible){
27197         if(visible) {
27198             this.show();
27199         }else{
27200             this.hide();
27201         }
27202     },
27203     
27204     /**
27205      * Try to focus this item.
27206      */
27207     focus : function(){
27208         Roo.fly(this.el).focus();
27209     },
27210     
27211     /**
27212      * Disables this item.
27213      */
27214     disable : function(){
27215         Roo.fly(this.td).addClass("x-item-disabled");
27216         this.disabled = true;
27217         this.el.disabled = true;
27218     },
27219     
27220     /**
27221      * Enables this item.
27222      */
27223     enable : function(){
27224         Roo.fly(this.td).removeClass("x-item-disabled");
27225         this.disabled = false;
27226         this.el.disabled = false;
27227     }
27228 };
27229
27230
27231 /**
27232  * @class Roo.Toolbar.Separator
27233  * @extends Roo.Toolbar.Item
27234  * A simple toolbar separator class
27235  * @constructor
27236  * Creates a new Separator
27237  */
27238 Roo.Toolbar.Separator = function(){
27239     var s = document.createElement("span");
27240     s.className = "ytb-sep";
27241     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27242 };
27243 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27244     enable:Roo.emptyFn,
27245     disable:Roo.emptyFn,
27246     focus:Roo.emptyFn
27247 });
27248
27249 /**
27250  * @class Roo.Toolbar.Spacer
27251  * @extends Roo.Toolbar.Item
27252  * A simple element that adds extra horizontal space to a toolbar.
27253  * @constructor
27254  * Creates a new Spacer
27255  */
27256 Roo.Toolbar.Spacer = function(){
27257     var s = document.createElement("div");
27258     s.className = "ytb-spacer";
27259     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27260 };
27261 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27262     enable:Roo.emptyFn,
27263     disable:Roo.emptyFn,
27264     focus:Roo.emptyFn
27265 });
27266
27267 /**
27268  * @class Roo.Toolbar.Fill
27269  * @extends Roo.Toolbar.Spacer
27270  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27271  * @constructor
27272  * Creates a new Spacer
27273  */
27274 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27275     // private
27276     render : function(td){
27277         td.style.width = '100%';
27278         Roo.Toolbar.Fill.superclass.render.call(this, td);
27279     }
27280 });
27281
27282 /**
27283  * @class Roo.Toolbar.TextItem
27284  * @extends Roo.Toolbar.Item
27285  * A simple class that renders text directly into a toolbar.
27286  * @constructor
27287  * Creates a new TextItem
27288  * @param {String} text
27289  */
27290 Roo.Toolbar.TextItem = function(text){
27291     if (typeof(text) == 'object') {
27292         text = text.text;
27293     }
27294     var s = document.createElement("span");
27295     s.className = "ytb-text";
27296     s.innerHTML = text;
27297     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27298 };
27299 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27300     enable:Roo.emptyFn,
27301     disable:Roo.emptyFn,
27302     focus:Roo.emptyFn
27303 });
27304
27305 /**
27306  * @class Roo.Toolbar.Button
27307  * @extends Roo.Button
27308  * A button that renders into a toolbar.
27309  * @constructor
27310  * Creates a new Button
27311  * @param {Object} config A standard {@link Roo.Button} config object
27312  */
27313 Roo.Toolbar.Button = function(config){
27314     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27315 };
27316 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27317     render : function(td){
27318         this.td = td;
27319         Roo.Toolbar.Button.superclass.render.call(this, td);
27320     },
27321     
27322     /**
27323      * Removes and destroys this button
27324      */
27325     destroy : function(){
27326         Roo.Toolbar.Button.superclass.destroy.call(this);
27327         this.td.parentNode.removeChild(this.td);
27328     },
27329     
27330     /**
27331      * Shows this button
27332      */
27333     show: function(){
27334         this.hidden = false;
27335         this.td.style.display = "";
27336     },
27337     
27338     /**
27339      * Hides this button
27340      */
27341     hide: function(){
27342         this.hidden = true;
27343         this.td.style.display = "none";
27344     },
27345
27346     /**
27347      * Disables this item
27348      */
27349     disable : function(){
27350         Roo.fly(this.td).addClass("x-item-disabled");
27351         this.disabled = true;
27352     },
27353
27354     /**
27355      * Enables this item
27356      */
27357     enable : function(){
27358         Roo.fly(this.td).removeClass("x-item-disabled");
27359         this.disabled = false;
27360     }
27361 });
27362 // backwards compat
27363 Roo.ToolbarButton = Roo.Toolbar.Button;
27364
27365 /**
27366  * @class Roo.Toolbar.SplitButton
27367  * @extends Roo.SplitButton
27368  * A menu button that renders into a toolbar.
27369  * @constructor
27370  * Creates a new SplitButton
27371  * @param {Object} config A standard {@link Roo.SplitButton} config object
27372  */
27373 Roo.Toolbar.SplitButton = function(config){
27374     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27375 };
27376 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27377     render : function(td){
27378         this.td = td;
27379         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27380     },
27381     
27382     /**
27383      * Removes and destroys this button
27384      */
27385     destroy : function(){
27386         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27387         this.td.parentNode.removeChild(this.td);
27388     },
27389     
27390     /**
27391      * Shows this button
27392      */
27393     show: function(){
27394         this.hidden = false;
27395         this.td.style.display = "";
27396     },
27397     
27398     /**
27399      * Hides this button
27400      */
27401     hide: function(){
27402         this.hidden = true;
27403         this.td.style.display = "none";
27404     }
27405 });
27406
27407 // backwards compat
27408 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27409  * Based on:
27410  * Ext JS Library 1.1.1
27411  * Copyright(c) 2006-2007, Ext JS, LLC.
27412  *
27413  * Originally Released Under LGPL - original licence link has changed is not relivant.
27414  *
27415  * Fork - LGPL
27416  * <script type="text/javascript">
27417  */
27418  
27419 /**
27420  * @class Roo.PagingToolbar
27421  * @extends Roo.Toolbar
27422  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27423  * @constructor
27424  * Create a new PagingToolbar
27425  * @param {Object} config The config object
27426  */
27427 Roo.PagingToolbar = function(el, ds, config)
27428 {
27429     // old args format still supported... - xtype is prefered..
27430     if (typeof(el) == 'object' && el.xtype) {
27431         // created from xtype...
27432         config = el;
27433         ds = el.dataSource;
27434         el = config.container;
27435     }
27436     var items = [];
27437     if (config.items) {
27438         items = config.items;
27439         config.items = [];
27440     }
27441     
27442     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27443     this.ds = ds;
27444     this.cursor = 0;
27445     this.renderButtons(this.el);
27446     this.bind(ds);
27447     
27448     // supprot items array.
27449    
27450     Roo.each(items, function(e) {
27451         this.add(Roo.factory(e));
27452     },this);
27453     
27454 };
27455
27456 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27457     /**
27458      * @cfg {Roo.data.Store} dataSource
27459      * The underlying data store providing the paged data
27460      */
27461     /**
27462      * @cfg {String/HTMLElement/Element} container
27463      * container The id or element that will contain the toolbar
27464      */
27465     /**
27466      * @cfg {Boolean} displayInfo
27467      * True to display the displayMsg (defaults to false)
27468      */
27469     /**
27470      * @cfg {Number} pageSize
27471      * The number of records to display per page (defaults to 20)
27472      */
27473     pageSize: 20,
27474     /**
27475      * @cfg {String} displayMsg
27476      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27477      */
27478     displayMsg : 'Displaying {0} - {1} of {2}',
27479     /**
27480      * @cfg {String} emptyMsg
27481      * The message to display when no records are found (defaults to "No data to display")
27482      */
27483     emptyMsg : 'No data to display',
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Page")
27486      * @type String
27487      */
27488     beforePageText : "Page",
27489     /**
27490      * Customizable piece of the default paging text (defaults to "of %0")
27491      * @type String
27492      */
27493     afterPageText : "of {0}",
27494     /**
27495      * Customizable piece of the default paging text (defaults to "First Page")
27496      * @type String
27497      */
27498     firstText : "First Page",
27499     /**
27500      * Customizable piece of the default paging text (defaults to "Previous Page")
27501      * @type String
27502      */
27503     prevText : "Previous Page",
27504     /**
27505      * Customizable piece of the default paging text (defaults to "Next Page")
27506      * @type String
27507      */
27508     nextText : "Next Page",
27509     /**
27510      * Customizable piece of the default paging text (defaults to "Last Page")
27511      * @type String
27512      */
27513     lastText : "Last Page",
27514     /**
27515      * Customizable piece of the default paging text (defaults to "Refresh")
27516      * @type String
27517      */
27518     refreshText : "Refresh",
27519
27520     // private
27521     renderButtons : function(el){
27522         Roo.PagingToolbar.superclass.render.call(this, el);
27523         this.first = this.addButton({
27524             tooltip: this.firstText,
27525             cls: "x-btn-icon x-grid-page-first",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["first"])
27528         });
27529         this.prev = this.addButton({
27530             tooltip: this.prevText,
27531             cls: "x-btn-icon x-grid-page-prev",
27532             disabled: true,
27533             handler: this.onClick.createDelegate(this, ["prev"])
27534         });
27535         //this.addSeparator();
27536         this.add(this.beforePageText);
27537         this.field = Roo.get(this.addDom({
27538            tag: "input",
27539            type: "text",
27540            size: "3",
27541            value: "1",
27542            cls: "x-grid-page-number"
27543         }).el);
27544         this.field.on("keydown", this.onPagingKeydown, this);
27545         this.field.on("focus", function(){this.dom.select();});
27546         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27547         this.field.setHeight(18);
27548         //this.addSeparator();
27549         this.next = this.addButton({
27550             tooltip: this.nextText,
27551             cls: "x-btn-icon x-grid-page-next",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["next"])
27554         });
27555         this.last = this.addButton({
27556             tooltip: this.lastText,
27557             cls: "x-btn-icon x-grid-page-last",
27558             disabled: true,
27559             handler: this.onClick.createDelegate(this, ["last"])
27560         });
27561         //this.addSeparator();
27562         this.loading = this.addButton({
27563             tooltip: this.refreshText,
27564             cls: "x-btn-icon x-grid-loading",
27565             handler: this.onClick.createDelegate(this, ["refresh"])
27566         });
27567
27568         if(this.displayInfo){
27569             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27570         }
27571     },
27572
27573     // private
27574     updateInfo : function(){
27575         if(this.displayEl){
27576             var count = this.ds.getCount();
27577             var msg = count == 0 ?
27578                 this.emptyMsg :
27579                 String.format(
27580                     this.displayMsg,
27581                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27582                 );
27583             this.displayEl.update(msg);
27584         }
27585     },
27586
27587     // private
27588     onLoad : function(ds, r, o){
27589        this.cursor = o.params ? o.params.start : 0;
27590        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27591
27592        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27593        this.field.dom.value = ap;
27594        this.first.setDisabled(ap == 1);
27595        this.prev.setDisabled(ap == 1);
27596        this.next.setDisabled(ap == ps);
27597        this.last.setDisabled(ap == ps);
27598        this.loading.enable();
27599        this.updateInfo();
27600     },
27601
27602     // private
27603     getPageData : function(){
27604         var total = this.ds.getTotalCount();
27605         return {
27606             total : total,
27607             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27608             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27609         };
27610     },
27611
27612     // private
27613     onLoadError : function(){
27614         this.loading.enable();
27615     },
27616
27617     // private
27618     onPagingKeydown : function(e){
27619         var k = e.getKey();
27620         var d = this.getPageData();
27621         if(k == e.RETURN){
27622             var v = this.field.dom.value, pageNum;
27623             if(!v || isNaN(pageNum = parseInt(v, 10))){
27624                 this.field.dom.value = d.activePage;
27625                 return;
27626             }
27627             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27628             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27629             e.stopEvent();
27630         }
27631         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27632         {
27633           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27634           this.field.dom.value = pageNum;
27635           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27636           e.stopEvent();
27637         }
27638         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27639         {
27640           var v = this.field.dom.value, pageNum; 
27641           var increment = (e.shiftKey) ? 10 : 1;
27642           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27643             increment *= -1;
27644           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27645             this.field.dom.value = d.activePage;
27646             return;
27647           }
27648           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27649           {
27650             this.field.dom.value = parseInt(v, 10) + increment;
27651             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27652             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27653           }
27654           e.stopEvent();
27655         }
27656     },
27657
27658     // private
27659     beforeLoad : function(){
27660         if(this.loading){
27661             this.loading.disable();
27662         }
27663     },
27664
27665     // private
27666     onClick : function(which){
27667         var ds = this.ds;
27668         switch(which){
27669             case "first":
27670                 ds.load({params:{start: 0, limit: this.pageSize}});
27671             break;
27672             case "prev":
27673                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27674             break;
27675             case "next":
27676                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27677             break;
27678             case "last":
27679                 var total = ds.getTotalCount();
27680                 var extra = total % this.pageSize;
27681                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27682                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27683             break;
27684             case "refresh":
27685                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27686             break;
27687         }
27688     },
27689
27690     /**
27691      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27692      * @param {Roo.data.Store} store The data store to unbind
27693      */
27694     unbind : function(ds){
27695         ds.un("beforeload", this.beforeLoad, this);
27696         ds.un("load", this.onLoad, this);
27697         ds.un("loadexception", this.onLoadError, this);
27698         ds.un("remove", this.updateInfo, this);
27699         ds.un("add", this.updateInfo, this);
27700         this.ds = undefined;
27701     },
27702
27703     /**
27704      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27705      * @param {Roo.data.Store} store The data store to bind
27706      */
27707     bind : function(ds){
27708         ds.on("beforeload", this.beforeLoad, this);
27709         ds.on("load", this.onLoad, this);
27710         ds.on("loadexception", this.onLoadError, this);
27711         ds.on("remove", this.updateInfo, this);
27712         ds.on("add", this.updateInfo, this);
27713         this.ds = ds;
27714     }
27715 });/*
27716  * Based on:
27717  * Ext JS Library 1.1.1
27718  * Copyright(c) 2006-2007, Ext JS, LLC.
27719  *
27720  * Originally Released Under LGPL - original licence link has changed is not relivant.
27721  *
27722  * Fork - LGPL
27723  * <script type="text/javascript">
27724  */
27725
27726 /**
27727  * @class Roo.Resizable
27728  * @extends Roo.util.Observable
27729  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27730  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27731  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27732  * the element will be wrapped for you automatically.</p>
27733  * <p>Here is the list of valid resize handles:</p>
27734  * <pre>
27735 Value   Description
27736 ------  -------------------
27737  'n'     north
27738  's'     south
27739  'e'     east
27740  'w'     west
27741  'nw'    northwest
27742  'sw'    southwest
27743  'se'    southeast
27744  'ne'    northeast
27745  'hd'    horizontal drag
27746  'all'   all
27747 </pre>
27748  * <p>Here's an example showing the creation of a typical Resizable:</p>
27749  * <pre><code>
27750 var resizer = new Roo.Resizable("element-id", {
27751     handles: 'all',
27752     minWidth: 200,
27753     minHeight: 100,
27754     maxWidth: 500,
27755     maxHeight: 400,
27756     pinned: true
27757 });
27758 resizer.on("resize", myHandler);
27759 </code></pre>
27760  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27761  * resizer.east.setDisplayed(false);</p>
27762  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27763  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27764  * resize operation's new size (defaults to [0, 0])
27765  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27766  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27767  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27768  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27769  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27770  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27771  * @cfg {Number} width The width of the element in pixels (defaults to null)
27772  * @cfg {Number} height The height of the element in pixels (defaults to null)
27773  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27774  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27775  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27776  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27777  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27778  * in favor of the handles config option (defaults to false)
27779  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27780  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27781  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27782  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27783  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27784  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27785  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27786  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27787  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27788  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27789  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27790  * @constructor
27791  * Create a new resizable component
27792  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27793  * @param {Object} config configuration options
27794   */
27795 Roo.Resizable = function(el, config)
27796 {
27797     this.el = Roo.get(el);
27798
27799     if(config && config.wrap){
27800         config.resizeChild = this.el;
27801         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27802         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27803         this.el.setStyle("overflow", "hidden");
27804         this.el.setPositioning(config.resizeChild.getPositioning());
27805         config.resizeChild.clearPositioning();
27806         if(!config.width || !config.height){
27807             var csize = config.resizeChild.getSize();
27808             this.el.setSize(csize.width, csize.height);
27809         }
27810         if(config.pinned && !config.adjustments){
27811             config.adjustments = "auto";
27812         }
27813     }
27814
27815     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27816     this.proxy.unselectable();
27817     this.proxy.enableDisplayMode('block');
27818
27819     Roo.apply(this, config);
27820
27821     if(this.pinned){
27822         this.disableTrackOver = true;
27823         this.el.addClass("x-resizable-pinned");
27824     }
27825     // if the element isn't positioned, make it relative
27826     var position = this.el.getStyle("position");
27827     if(position != "absolute" && position != "fixed"){
27828         this.el.setStyle("position", "relative");
27829     }
27830     if(!this.handles){ // no handles passed, must be legacy style
27831         this.handles = 's,e,se';
27832         if(this.multiDirectional){
27833             this.handles += ',n,w';
27834         }
27835     }
27836     if(this.handles == "all"){
27837         this.handles = "n s e w ne nw se sw";
27838     }
27839     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27840     var ps = Roo.Resizable.positions;
27841     for(var i = 0, len = hs.length; i < len; i++){
27842         if(hs[i] && ps[hs[i]]){
27843             var pos = ps[hs[i]];
27844             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27845         }
27846     }
27847     // legacy
27848     this.corner = this.southeast;
27849     
27850     // updateBox = the box can move..
27851     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27852         this.updateBox = true;
27853     }
27854
27855     this.activeHandle = null;
27856
27857     if(this.resizeChild){
27858         if(typeof this.resizeChild == "boolean"){
27859             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27860         }else{
27861             this.resizeChild = Roo.get(this.resizeChild, true);
27862         }
27863     }
27864     
27865     if(this.adjustments == "auto"){
27866         var rc = this.resizeChild;
27867         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27868         if(rc && (hw || hn)){
27869             rc.position("relative");
27870             rc.setLeft(hw ? hw.el.getWidth() : 0);
27871             rc.setTop(hn ? hn.el.getHeight() : 0);
27872         }
27873         this.adjustments = [
27874             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27875             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27876         ];
27877     }
27878
27879     if(this.draggable){
27880         this.dd = this.dynamic ?
27881             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27882         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27883     }
27884
27885     // public events
27886     this.addEvents({
27887         /**
27888          * @event beforeresize
27889          * Fired before resize is allowed. Set enabled to false to cancel resize.
27890          * @param {Roo.Resizable} this
27891          * @param {Roo.EventObject} e The mousedown event
27892          */
27893         "beforeresize" : true,
27894         /**
27895          * @event resize
27896          * Fired after a resize.
27897          * @param {Roo.Resizable} this
27898          * @param {Number} width The new width
27899          * @param {Number} height The new height
27900          * @param {Roo.EventObject} e The mouseup event
27901          */
27902         "resize" : true
27903     });
27904
27905     if(this.width !== null && this.height !== null){
27906         this.resizeTo(this.width, this.height);
27907     }else{
27908         this.updateChildSize();
27909     }
27910     if(Roo.isIE){
27911         this.el.dom.style.zoom = 1;
27912     }
27913     Roo.Resizable.superclass.constructor.call(this);
27914 };
27915
27916 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27917         resizeChild : false,
27918         adjustments : [0, 0],
27919         minWidth : 5,
27920         minHeight : 5,
27921         maxWidth : 10000,
27922         maxHeight : 10000,
27923         enabled : true,
27924         animate : false,
27925         duration : .35,
27926         dynamic : false,
27927         handles : false,
27928         multiDirectional : false,
27929         disableTrackOver : false,
27930         easing : 'easeOutStrong',
27931         widthIncrement : 0,
27932         heightIncrement : 0,
27933         pinned : false,
27934         width : null,
27935         height : null,
27936         preserveRatio : false,
27937         transparent: false,
27938         minX: 0,
27939         minY: 0,
27940         draggable: false,
27941
27942         /**
27943          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27944          */
27945         constrainTo: undefined,
27946         /**
27947          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27948          */
27949         resizeRegion: undefined,
27950
27951
27952     /**
27953      * Perform a manual resize
27954      * @param {Number} width
27955      * @param {Number} height
27956      */
27957     resizeTo : function(width, height){
27958         this.el.setSize(width, height);
27959         this.updateChildSize();
27960         this.fireEvent("resize", this, width, height, null);
27961     },
27962
27963     // private
27964     startSizing : function(e, handle){
27965         this.fireEvent("beforeresize", this, e);
27966         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27967
27968             if(!this.overlay){
27969                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27970                 this.overlay.unselectable();
27971                 this.overlay.enableDisplayMode("block");
27972                 this.overlay.on("mousemove", this.onMouseMove, this);
27973                 this.overlay.on("mouseup", this.onMouseUp, this);
27974             }
27975             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27976
27977             this.resizing = true;
27978             this.startBox = this.el.getBox();
27979             this.startPoint = e.getXY();
27980             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27981                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27982
27983             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27984             this.overlay.show();
27985
27986             if(this.constrainTo) {
27987                 var ct = Roo.get(this.constrainTo);
27988                 this.resizeRegion = ct.getRegion().adjust(
27989                     ct.getFrameWidth('t'),
27990                     ct.getFrameWidth('l'),
27991                     -ct.getFrameWidth('b'),
27992                     -ct.getFrameWidth('r')
27993                 );
27994             }
27995
27996             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27997             this.proxy.show();
27998             this.proxy.setBox(this.startBox);
27999             if(!this.dynamic){
28000                 this.proxy.setStyle('visibility', 'visible');
28001             }
28002         }
28003     },
28004
28005     // private
28006     onMouseDown : function(handle, e){
28007         if(this.enabled){
28008             e.stopEvent();
28009             this.activeHandle = handle;
28010             this.startSizing(e, handle);
28011         }
28012     },
28013
28014     // private
28015     onMouseUp : function(e){
28016         var size = this.resizeElement();
28017         this.resizing = false;
28018         this.handleOut();
28019         this.overlay.hide();
28020         this.proxy.hide();
28021         this.fireEvent("resize", this, size.width, size.height, e);
28022     },
28023
28024     // private
28025     updateChildSize : function(){
28026         if(this.resizeChild){
28027             var el = this.el;
28028             var child = this.resizeChild;
28029             var adj = this.adjustments;
28030             if(el.dom.offsetWidth){
28031                 var b = el.getSize(true);
28032                 child.setSize(b.width+adj[0], b.height+adj[1]);
28033             }
28034             // Second call here for IE
28035             // The first call enables instant resizing and
28036             // the second call corrects scroll bars if they
28037             // exist
28038             if(Roo.isIE){
28039                 setTimeout(function(){
28040                     if(el.dom.offsetWidth){
28041                         var b = el.getSize(true);
28042                         child.setSize(b.width+adj[0], b.height+adj[1]);
28043                     }
28044                 }, 10);
28045             }
28046         }
28047     },
28048
28049     // private
28050     snap : function(value, inc, min){
28051         if(!inc || !value) return value;
28052         var newValue = value;
28053         var m = value % inc;
28054         if(m > 0){
28055             if(m > (inc/2)){
28056                 newValue = value + (inc-m);
28057             }else{
28058                 newValue = value - m;
28059             }
28060         }
28061         return Math.max(min, newValue);
28062     },
28063
28064     // private
28065     resizeElement : function(){
28066         var box = this.proxy.getBox();
28067         if(this.updateBox){
28068             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28069         }else{
28070             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28071         }
28072         this.updateChildSize();
28073         if(!this.dynamic){
28074             this.proxy.hide();
28075         }
28076         return box;
28077     },
28078
28079     // private
28080     constrain : function(v, diff, m, mx){
28081         if(v - diff < m){
28082             diff = v - m;
28083         }else if(v - diff > mx){
28084             diff = mx - v;
28085         }
28086         return diff;
28087     },
28088
28089     // private
28090     onMouseMove : function(e){
28091         if(this.enabled){
28092             try{// try catch so if something goes wrong the user doesn't get hung
28093
28094             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28095                 return;
28096             }
28097
28098             //var curXY = this.startPoint;
28099             var curSize = this.curSize || this.startBox;
28100             var x = this.startBox.x, y = this.startBox.y;
28101             var ox = x, oy = y;
28102             var w = curSize.width, h = curSize.height;
28103             var ow = w, oh = h;
28104             var mw = this.minWidth, mh = this.minHeight;
28105             var mxw = this.maxWidth, mxh = this.maxHeight;
28106             var wi = this.widthIncrement;
28107             var hi = this.heightIncrement;
28108
28109             var eventXY = e.getXY();
28110             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28111             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28112
28113             var pos = this.activeHandle.position;
28114
28115             switch(pos){
28116                 case "east":
28117                     w += diffX;
28118                     w = Math.min(Math.max(mw, w), mxw);
28119                     break;
28120              
28121                 case "south":
28122                     h += diffY;
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "southeast":
28126                     w += diffX;
28127                     h += diffY;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     h = Math.min(Math.max(mh, h), mxh);
28130                     break;
28131                 case "north":
28132                     diffY = this.constrain(h, diffY, mh, mxh);
28133                     y += diffY;
28134                     h -= diffY;
28135                     break;
28136                 case "hdrag":
28137                     
28138                     if (wi) {
28139                         var adiffX = Math.abs(diffX);
28140                         var sub = (adiffX % wi); // how much 
28141                         if (sub > (wi/2)) { // far enough to snap
28142                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28143                         } else {
28144                             // remove difference.. 
28145                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28146                         }
28147                     }
28148                     x += diffX;
28149                     x = Math.max(this.minX, x);
28150                     break;
28151                 case "west":
28152                     diffX = this.constrain(w, diffX, mw, mxw);
28153                     x += diffX;
28154                     w -= diffX;
28155                     break;
28156                 case "northeast":
28157                     w += diffX;
28158                     w = Math.min(Math.max(mw, w), mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     break;
28163                 case "northwest":
28164                     diffX = this.constrain(w, diffX, mw, mxw);
28165                     diffY = this.constrain(h, diffY, mh, mxh);
28166                     y += diffY;
28167                     h -= diffY;
28168                     x += diffX;
28169                     w -= diffX;
28170                     break;
28171                case "southwest":
28172                     diffX = this.constrain(w, diffX, mw, mxw);
28173                     h += diffY;
28174                     h = Math.min(Math.max(mh, h), mxh);
28175                     x += diffX;
28176                     w -= diffX;
28177                     break;
28178             }
28179
28180             var sw = this.snap(w, wi, mw);
28181             var sh = this.snap(h, hi, mh);
28182             if(sw != w || sh != h){
28183                 switch(pos){
28184                     case "northeast":
28185                         y -= sh - h;
28186                     break;
28187                     case "north":
28188                         y -= sh - h;
28189                         break;
28190                     case "southwest":
28191                         x -= sw - w;
28192                     break;
28193                     case "west":
28194                         x -= sw - w;
28195                         break;
28196                     case "northwest":
28197                         x -= sw - w;
28198                         y -= sh - h;
28199                     break;
28200                 }
28201                 w = sw;
28202                 h = sh;
28203             }
28204
28205             if(this.preserveRatio){
28206                 switch(pos){
28207                     case "southeast":
28208                     case "east":
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         w = ow * (h/oh);
28212                        break;
28213                     case "south":
28214                         w = ow * (h/oh);
28215                         w = Math.min(Math.max(mw, w), mxw);
28216                         h = oh * (w/ow);
28217                         break;
28218                     case "northeast":
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                     break;
28223                     case "north":
28224                         var tw = w;
28225                         w = ow * (h/oh);
28226                         w = Math.min(Math.max(mw, w), mxw);
28227                         h = oh * (w/ow);
28228                         x += (tw - w) / 2;
28229                         break;
28230                     case "southwest":
28231                         h = oh * (w/ow);
28232                         h = Math.min(Math.max(mh, h), mxh);
28233                         var tw = w;
28234                         w = ow * (h/oh);
28235                         x += tw - w;
28236                         break;
28237                     case "west":
28238                         var th = h;
28239                         h = oh * (w/ow);
28240                         h = Math.min(Math.max(mh, h), mxh);
28241                         y += (th - h) / 2;
28242                         var tw = w;
28243                         w = ow * (h/oh);
28244                         x += tw - w;
28245                        break;
28246                     case "northwest":
28247                         var tw = w;
28248                         var th = h;
28249                         h = oh * (w/ow);
28250                         h = Math.min(Math.max(mh, h), mxh);
28251                         w = ow * (h/oh);
28252                         y += th - h;
28253                         x += tw - w;
28254                        break;
28255
28256                 }
28257             }
28258             if (pos == 'hdrag') {
28259                 w = ow;
28260             }
28261             this.proxy.setBounds(x, y, w, h);
28262             if(this.dynamic){
28263                 this.resizeElement();
28264             }
28265             }catch(e){}
28266         }
28267     },
28268
28269     // private
28270     handleOver : function(){
28271         if(this.enabled){
28272             this.el.addClass("x-resizable-over");
28273         }
28274     },
28275
28276     // private
28277     handleOut : function(){
28278         if(!this.resizing){
28279             this.el.removeClass("x-resizable-over");
28280         }
28281     },
28282
28283     /**
28284      * Returns the element this component is bound to.
28285      * @return {Roo.Element}
28286      */
28287     getEl : function(){
28288         return this.el;
28289     },
28290
28291     /**
28292      * Returns the resizeChild element (or null).
28293      * @return {Roo.Element}
28294      */
28295     getResizeChild : function(){
28296         return this.resizeChild;
28297     },
28298
28299     /**
28300      * Destroys this resizable. If the element was wrapped and
28301      * removeEl is not true then the element remains.
28302      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28303      */
28304     destroy : function(removeEl){
28305         this.proxy.remove();
28306         if(this.overlay){
28307             this.overlay.removeAllListeners();
28308             this.overlay.remove();
28309         }
28310         var ps = Roo.Resizable.positions;
28311         for(var k in ps){
28312             if(typeof ps[k] != "function" && this[ps[k]]){
28313                 var h = this[ps[k]];
28314                 h.el.removeAllListeners();
28315                 h.el.remove();
28316             }
28317         }
28318         if(removeEl){
28319             this.el.update("");
28320             this.el.remove();
28321         }
28322     }
28323 });
28324
28325 // private
28326 // hash to map config positions to true positions
28327 Roo.Resizable.positions = {
28328     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28329     hd: "hdrag"
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28334     if(!this.tpl){
28335         // only initialize the template if resizable is used
28336         var tpl = Roo.DomHelper.createTemplate(
28337             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28338         );
28339         tpl.compile();
28340         Roo.Resizable.Handle.prototype.tpl = tpl;
28341     }
28342     this.position = pos;
28343     this.rz = rz;
28344     // show north drag fro topdra
28345     var handlepos = pos == 'hdrag' ? 'north' : pos;
28346     
28347     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28348     if (pos == 'hdrag') {
28349         this.el.setStyle('cursor', 'pointer');
28350     }
28351     this.el.unselectable();
28352     if(transparent){
28353         this.el.setOpacity(0);
28354     }
28355     this.el.on("mousedown", this.onMouseDown, this);
28356     if(!disableTrackOver){
28357         this.el.on("mouseover", this.onMouseOver, this);
28358         this.el.on("mouseout", this.onMouseOut, this);
28359     }
28360 };
28361
28362 // private
28363 Roo.Resizable.Handle.prototype = {
28364     afterResize : function(rz){
28365         // do nothing
28366     },
28367     // private
28368     onMouseDown : function(e){
28369         this.rz.onMouseDown(this, e);
28370     },
28371     // private
28372     onMouseOver : function(e){
28373         this.rz.handleOver(this, e);
28374     },
28375     // private
28376     onMouseOut : function(e){
28377         this.rz.handleOut(this, e);
28378     }
28379 };/*
28380  * Based on:
28381  * Ext JS Library 1.1.1
28382  * Copyright(c) 2006-2007, Ext JS, LLC.
28383  *
28384  * Originally Released Under LGPL - original licence link has changed is not relivant.
28385  *
28386  * Fork - LGPL
28387  * <script type="text/javascript">
28388  */
28389
28390 /**
28391  * @class Roo.Editor
28392  * @extends Roo.Component
28393  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28394  * @constructor
28395  * Create a new Editor
28396  * @param {Roo.form.Field} field The Field object (or descendant)
28397  * @param {Object} config The config object
28398  */
28399 Roo.Editor = function(field, config){
28400     Roo.Editor.superclass.constructor.call(this, config);
28401     this.field = field;
28402     this.addEvents({
28403         /**
28404              * @event beforestartedit
28405              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28406              * false from the handler of this event.
28407              * @param {Editor} this
28408              * @param {Roo.Element} boundEl The underlying element bound to this editor
28409              * @param {Mixed} value The field value being set
28410              */
28411         "beforestartedit" : true,
28412         /**
28413              * @event startedit
28414              * Fires when this editor is displayed
28415              * @param {Roo.Element} boundEl The underlying element bound to this editor
28416              * @param {Mixed} value The starting field value
28417              */
28418         "startedit" : true,
28419         /**
28420              * @event beforecomplete
28421              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28422              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28423              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28424              * event will not fire since no edit actually occurred.
28425              * @param {Editor} this
28426              * @param {Mixed} value The current field value
28427              * @param {Mixed} startValue The original field value
28428              */
28429         "beforecomplete" : true,
28430         /**
28431              * @event complete
28432              * Fires after editing is complete and any changed value has been written to the underlying field.
28433              * @param {Editor} this
28434              * @param {Mixed} value The current field value
28435              * @param {Mixed} startValue The original field value
28436              */
28437         "complete" : true,
28438         /**
28439          * @event specialkey
28440          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28441          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28442          * @param {Roo.form.Field} this
28443          * @param {Roo.EventObject} e The event object
28444          */
28445         "specialkey" : true
28446     });
28447 };
28448
28449 Roo.extend(Roo.Editor, Roo.Component, {
28450     /**
28451      * @cfg {Boolean/String} autosize
28452      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28453      * or "height" to adopt the height only (defaults to false)
28454      */
28455     /**
28456      * @cfg {Boolean} revertInvalid
28457      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28458      * validation fails (defaults to true)
28459      */
28460     /**
28461      * @cfg {Boolean} ignoreNoChange
28462      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28463      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28464      * will never be ignored.
28465      */
28466     /**
28467      * @cfg {Boolean} hideEl
28468      * False to keep the bound element visible while the editor is displayed (defaults to true)
28469      */
28470     /**
28471      * @cfg {Mixed} value
28472      * The data value of the underlying field (defaults to "")
28473      */
28474     value : "",
28475     /**
28476      * @cfg {String} alignment
28477      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28478      */
28479     alignment: "c-c?",
28480     /**
28481      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28482      * for bottom-right shadow (defaults to "frame")
28483      */
28484     shadow : "frame",
28485     /**
28486      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28487      */
28488     constrain : false,
28489     /**
28490      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28491      */
28492     completeOnEnter : false,
28493     /**
28494      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28495      */
28496     cancelOnEsc : false,
28497     /**
28498      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28499      */
28500     updateEl : false,
28501
28502     // private
28503     onRender : function(ct, position){
28504         this.el = new Roo.Layer({
28505             shadow: this.shadow,
28506             cls: "x-editor",
28507             parentEl : ct,
28508             shim : this.shim,
28509             shadowOffset:4,
28510             id: this.id,
28511             constrain: this.constrain
28512         });
28513         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28514         if(this.field.msgTarget != 'title'){
28515             this.field.msgTarget = 'qtip';
28516         }
28517         this.field.render(this.el);
28518         if(Roo.isGecko){
28519             this.field.el.dom.setAttribute('autocomplete', 'off');
28520         }
28521         this.field.on("specialkey", this.onSpecialKey, this);
28522         if(this.swallowKeys){
28523             this.field.el.swallowEvent(['keydown','keypress']);
28524         }
28525         this.field.show();
28526         this.field.on("blur", this.onBlur, this);
28527         if(this.field.grow){
28528             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28529         }
28530     },
28531
28532     onSpecialKey : function(field, e)
28533     {
28534         //Roo.log('editor onSpecialKey');
28535         if(this.completeOnEnter && e.getKey() == e.ENTER){
28536             e.stopEvent();
28537             this.completeEdit();
28538             return;
28539         }
28540         // do not fire special key otherwise it might hide close the editor...
28541         if(e.getKey() == e.ENTER){    
28542             return;
28543         }
28544         if(this.cancelOnEsc && e.getKey() == e.ESC){
28545             this.cancelEdit();
28546             return;
28547         } 
28548         this.fireEvent('specialkey', field, e);
28549     
28550     },
28551
28552     /**
28553      * Starts the editing process and shows the editor.
28554      * @param {String/HTMLElement/Element} el The element to edit
28555      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28556       * to the innerHTML of el.
28557      */
28558     startEdit : function(el, value){
28559         if(this.editing){
28560             this.completeEdit();
28561         }
28562         this.boundEl = Roo.get(el);
28563         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28564         if(!this.rendered){
28565             this.render(this.parentEl || document.body);
28566         }
28567         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28568             return;
28569         }
28570         this.startValue = v;
28571         this.field.setValue(v);
28572         if(this.autoSize){
28573             var sz = this.boundEl.getSize();
28574             switch(this.autoSize){
28575                 case "width":
28576                 this.setSize(sz.width,  "");
28577                 break;
28578                 case "height":
28579                 this.setSize("",  sz.height);
28580                 break;
28581                 default:
28582                 this.setSize(sz.width,  sz.height);
28583             }
28584         }
28585         this.el.alignTo(this.boundEl, this.alignment);
28586         this.editing = true;
28587         if(Roo.QuickTips){
28588             Roo.QuickTips.disable();
28589         }
28590         this.show();
28591     },
28592
28593     /**
28594      * Sets the height and width of this editor.
28595      * @param {Number} width The new width
28596      * @param {Number} height The new height
28597      */
28598     setSize : function(w, h){
28599         this.field.setSize(w, h);
28600         if(this.el){
28601             this.el.sync();
28602         }
28603     },
28604
28605     /**
28606      * Realigns the editor to the bound field based on the current alignment config value.
28607      */
28608     realign : function(){
28609         this.el.alignTo(this.boundEl, this.alignment);
28610     },
28611
28612     /**
28613      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28614      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28615      */
28616     completeEdit : function(remainVisible){
28617         if(!this.editing){
28618             return;
28619         }
28620         var v = this.getValue();
28621         if(this.revertInvalid !== false && !this.field.isValid()){
28622             v = this.startValue;
28623             this.cancelEdit(true);
28624         }
28625         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28626             this.editing = false;
28627             this.hide();
28628             return;
28629         }
28630         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28631             this.editing = false;
28632             if(this.updateEl && this.boundEl){
28633                 this.boundEl.update(v);
28634             }
28635             if(remainVisible !== true){
28636                 this.hide();
28637             }
28638             this.fireEvent("complete", this, v, this.startValue);
28639         }
28640     },
28641
28642     // private
28643     onShow : function(){
28644         this.el.show();
28645         if(this.hideEl !== false){
28646             this.boundEl.hide();
28647         }
28648         this.field.show();
28649         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28650             this.fixIEFocus = true;
28651             this.deferredFocus.defer(50, this);
28652         }else{
28653             this.field.focus();
28654         }
28655         this.fireEvent("startedit", this.boundEl, this.startValue);
28656     },
28657
28658     deferredFocus : function(){
28659         if(this.editing){
28660             this.field.focus();
28661         }
28662     },
28663
28664     /**
28665      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28666      * reverted to the original starting value.
28667      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28668      * cancel (defaults to false)
28669      */
28670     cancelEdit : function(remainVisible){
28671         if(this.editing){
28672             this.setValue(this.startValue);
28673             if(remainVisible !== true){
28674                 this.hide();
28675             }
28676         }
28677     },
28678
28679     // private
28680     onBlur : function(){
28681         if(this.allowBlur !== true && this.editing){
28682             this.completeEdit();
28683         }
28684     },
28685
28686     // private
28687     onHide : function(){
28688         if(this.editing){
28689             this.completeEdit();
28690             return;
28691         }
28692         this.field.blur();
28693         if(this.field.collapse){
28694             this.field.collapse();
28695         }
28696         this.el.hide();
28697         if(this.hideEl !== false){
28698             this.boundEl.show();
28699         }
28700         if(Roo.QuickTips){
28701             Roo.QuickTips.enable();
28702         }
28703     },
28704
28705     /**
28706      * Sets the data value of the editor
28707      * @param {Mixed} value Any valid value supported by the underlying field
28708      */
28709     setValue : function(v){
28710         this.field.setValue(v);
28711     },
28712
28713     /**
28714      * Gets the data value of the editor
28715      * @return {Mixed} The data value
28716      */
28717     getValue : function(){
28718         return this.field.getValue();
28719     }
28720 });/*
28721  * Based on:
28722  * Ext JS Library 1.1.1
28723  * Copyright(c) 2006-2007, Ext JS, LLC.
28724  *
28725  * Originally Released Under LGPL - original licence link has changed is not relivant.
28726  *
28727  * Fork - LGPL
28728  * <script type="text/javascript">
28729  */
28730  
28731 /**
28732  * @class Roo.BasicDialog
28733  * @extends Roo.util.Observable
28734  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28735  * <pre><code>
28736 var dlg = new Roo.BasicDialog("my-dlg", {
28737     height: 200,
28738     width: 300,
28739     minHeight: 100,
28740     minWidth: 150,
28741     modal: true,
28742     proxyDrag: true,
28743     shadow: true
28744 });
28745 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28746 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28747 dlg.addButton('Cancel', dlg.hide, dlg);
28748 dlg.show();
28749 </code></pre>
28750   <b>A Dialog should always be a direct child of the body element.</b>
28751  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28752  * @cfg {String} title Default text to display in the title bar (defaults to null)
28753  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28754  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28755  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28756  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28757  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28758  * (defaults to null with no animation)
28759  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28760  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28761  * property for valid values (defaults to 'all')
28762  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28763  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28764  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28765  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28766  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28767  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28768  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28769  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28770  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28771  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28772  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28773  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28774  * draggable = true (defaults to false)
28775  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28776  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28777  * shadow (defaults to false)
28778  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28779  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28780  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28781  * @cfg {Array} buttons Array of buttons
28782  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28783  * @constructor
28784  * Create a new BasicDialog.
28785  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28786  * @param {Object} config Configuration options
28787  */
28788 Roo.BasicDialog = function(el, config){
28789     this.el = Roo.get(el);
28790     var dh = Roo.DomHelper;
28791     if(!this.el && config && config.autoCreate){
28792         if(typeof config.autoCreate == "object"){
28793             if(!config.autoCreate.id){
28794                 config.autoCreate.id = el;
28795             }
28796             this.el = dh.append(document.body,
28797                         config.autoCreate, true);
28798         }else{
28799             this.el = dh.append(document.body,
28800                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28801         }
28802     }
28803     el = this.el;
28804     el.setDisplayed(true);
28805     el.hide = this.hideAction;
28806     this.id = el.id;
28807     el.addClass("x-dlg");
28808
28809     Roo.apply(this, config);
28810
28811     this.proxy = el.createProxy("x-dlg-proxy");
28812     this.proxy.hide = this.hideAction;
28813     this.proxy.setOpacity(.5);
28814     this.proxy.hide();
28815
28816     if(config.width){
28817         el.setWidth(config.width);
28818     }
28819     if(config.height){
28820         el.setHeight(config.height);
28821     }
28822     this.size = el.getSize();
28823     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28824         this.xy = [config.x,config.y];
28825     }else{
28826         this.xy = el.getCenterXY(true);
28827     }
28828     /** The header element @type Roo.Element */
28829     this.header = el.child("> .x-dlg-hd");
28830     /** The body element @type Roo.Element */
28831     this.body = el.child("> .x-dlg-bd");
28832     /** The footer element @type Roo.Element */
28833     this.footer = el.child("> .x-dlg-ft");
28834
28835     if(!this.header){
28836         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28837     }
28838     if(!this.body){
28839         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28840     }
28841
28842     this.header.unselectable();
28843     if(this.title){
28844         this.header.update(this.title);
28845     }
28846     // this element allows the dialog to be focused for keyboard event
28847     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28848     this.focusEl.swallowEvent("click", true);
28849
28850     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28851
28852     // wrap the body and footer for special rendering
28853     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28854     if(this.footer){
28855         this.bwrap.dom.appendChild(this.footer.dom);
28856     }
28857
28858     this.bg = this.el.createChild({
28859         tag: "div", cls:"x-dlg-bg",
28860         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28861     });
28862     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28863
28864
28865     if(this.autoScroll !== false && !this.autoTabs){
28866         this.body.setStyle("overflow", "auto");
28867     }
28868
28869     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28870
28871     if(this.closable !== false){
28872         this.el.addClass("x-dlg-closable");
28873         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28874         this.close.on("click", this.closeClick, this);
28875         this.close.addClassOnOver("x-dlg-close-over");
28876     }
28877     if(this.collapsible !== false){
28878         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28879         this.collapseBtn.on("click", this.collapseClick, this);
28880         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28881         this.header.on("dblclick", this.collapseClick, this);
28882     }
28883     if(this.resizable !== false){
28884         this.el.addClass("x-dlg-resizable");
28885         this.resizer = new Roo.Resizable(el, {
28886             minWidth: this.minWidth || 80,
28887             minHeight:this.minHeight || 80,
28888             handles: this.resizeHandles || "all",
28889             pinned: true
28890         });
28891         this.resizer.on("beforeresize", this.beforeResize, this);
28892         this.resizer.on("resize", this.onResize, this);
28893     }
28894     if(this.draggable !== false){
28895         el.addClass("x-dlg-draggable");
28896         if (!this.proxyDrag) {
28897             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28898         }
28899         else {
28900             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28901         }
28902         dd.setHandleElId(this.header.id);
28903         dd.endDrag = this.endMove.createDelegate(this);
28904         dd.startDrag = this.startMove.createDelegate(this);
28905         dd.onDrag = this.onDrag.createDelegate(this);
28906         dd.scroll = false;
28907         this.dd = dd;
28908     }
28909     if(this.modal){
28910         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28911         this.mask.enableDisplayMode("block");
28912         this.mask.hide();
28913         this.el.addClass("x-dlg-modal");
28914     }
28915     if(this.shadow){
28916         this.shadow = new Roo.Shadow({
28917             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28918             offset : this.shadowOffset
28919         });
28920     }else{
28921         this.shadowOffset = 0;
28922     }
28923     if(Roo.useShims && this.shim !== false){
28924         this.shim = this.el.createShim();
28925         this.shim.hide = this.hideAction;
28926         this.shim.hide();
28927     }else{
28928         this.shim = false;
28929     }
28930     if(this.autoTabs){
28931         this.initTabs();
28932     }
28933     if (this.buttons) { 
28934         var bts= this.buttons;
28935         this.buttons = [];
28936         Roo.each(bts, function(b) {
28937             this.addButton(b);
28938         }, this);
28939     }
28940     
28941     
28942     this.addEvents({
28943         /**
28944          * @event keydown
28945          * Fires when a key is pressed
28946          * @param {Roo.BasicDialog} this
28947          * @param {Roo.EventObject} e
28948          */
28949         "keydown" : true,
28950         /**
28951          * @event move
28952          * Fires when this dialog is moved by the user.
28953          * @param {Roo.BasicDialog} this
28954          * @param {Number} x The new page X
28955          * @param {Number} y The new page Y
28956          */
28957         "move" : true,
28958         /**
28959          * @event resize
28960          * Fires when this dialog is resized by the user.
28961          * @param {Roo.BasicDialog} this
28962          * @param {Number} width The new width
28963          * @param {Number} height The new height
28964          */
28965         "resize" : true,
28966         /**
28967          * @event beforehide
28968          * Fires before this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "beforehide" : true,
28972         /**
28973          * @event hide
28974          * Fires when this dialog is hidden.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "hide" : true,
28978         /**
28979          * @event beforeshow
28980          * Fires before this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "beforeshow" : true,
28984         /**
28985          * @event show
28986          * Fires when this dialog is shown.
28987          * @param {Roo.BasicDialog} this
28988          */
28989         "show" : true
28990     });
28991     el.on("keydown", this.onKeyDown, this);
28992     el.on("mousedown", this.toFront, this);
28993     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28994     this.el.hide();
28995     Roo.DialogManager.register(this);
28996     Roo.BasicDialog.superclass.constructor.call(this);
28997 };
28998
28999 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29000     shadowOffset: Roo.isIE ? 6 : 5,
29001     minHeight: 80,
29002     minWidth: 200,
29003     minButtonWidth: 75,
29004     defaultButton: null,
29005     buttonAlign: "right",
29006     tabTag: 'div',
29007     firstShow: true,
29008
29009     /**
29010      * Sets the dialog title text
29011      * @param {String} text The title text to display
29012      * @return {Roo.BasicDialog} this
29013      */
29014     setTitle : function(text){
29015         this.header.update(text);
29016         return this;
29017     },
29018
29019     // private
29020     closeClick : function(){
29021         this.hide();
29022     },
29023
29024     // private
29025     collapseClick : function(){
29026         this[this.collapsed ? "expand" : "collapse"]();
29027     },
29028
29029     /**
29030      * Collapses the dialog to its minimized state (only the title bar is visible).
29031      * Equivalent to the user clicking the collapse dialog button.
29032      */
29033     collapse : function(){
29034         if(!this.collapsed){
29035             this.collapsed = true;
29036             this.el.addClass("x-dlg-collapsed");
29037             this.restoreHeight = this.el.getHeight();
29038             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29039         }
29040     },
29041
29042     /**
29043      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29044      * clicking the expand dialog button.
29045      */
29046     expand : function(){
29047         if(this.collapsed){
29048             this.collapsed = false;
29049             this.el.removeClass("x-dlg-collapsed");
29050             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29051         }
29052     },
29053
29054     /**
29055      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29056      * @return {Roo.TabPanel} The tabs component
29057      */
29058     initTabs : function(){
29059         var tabs = this.getTabs();
29060         while(tabs.getTab(0)){
29061             tabs.removeTab(0);
29062         }
29063         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29064             var dom = el.dom;
29065             tabs.addTab(Roo.id(dom), dom.title);
29066             dom.title = "";
29067         });
29068         tabs.activate(0);
29069         return tabs;
29070     },
29071
29072     // private
29073     beforeResize : function(){
29074         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29075     },
29076
29077     // private
29078     onResize : function(){
29079         this.refreshSize();
29080         this.syncBodyHeight();
29081         this.adjustAssets();
29082         this.focus();
29083         this.fireEvent("resize", this, this.size.width, this.size.height);
29084     },
29085
29086     // private
29087     onKeyDown : function(e){
29088         if(this.isVisible()){
29089             this.fireEvent("keydown", this, e);
29090         }
29091     },
29092
29093     /**
29094      * Resizes the dialog.
29095      * @param {Number} width
29096      * @param {Number} height
29097      * @return {Roo.BasicDialog} this
29098      */
29099     resizeTo : function(width, height){
29100         this.el.setSize(width, height);
29101         this.size = {width: width, height: height};
29102         this.syncBodyHeight();
29103         if(this.fixedcenter){
29104             this.center();
29105         }
29106         if(this.isVisible()){
29107             this.constrainXY();
29108             this.adjustAssets();
29109         }
29110         this.fireEvent("resize", this, width, height);
29111         return this;
29112     },
29113
29114
29115     /**
29116      * Resizes the dialog to fit the specified content size.
29117      * @param {Number} width
29118      * @param {Number} height
29119      * @return {Roo.BasicDialog} this
29120      */
29121     setContentSize : function(w, h){
29122         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29123         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29124         //if(!this.el.isBorderBox()){
29125             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29126             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29127         //}
29128         if(this.tabs){
29129             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29130             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29131         }
29132         this.resizeTo(w, h);
29133         return this;
29134     },
29135
29136     /**
29137      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29138      * executed in response to a particular key being pressed while the dialog is active.
29139      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29140      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29141      * @param {Function} fn The function to call
29142      * @param {Object} scope (optional) The scope of the function
29143      * @return {Roo.BasicDialog} this
29144      */
29145     addKeyListener : function(key, fn, scope){
29146         var keyCode, shift, ctrl, alt;
29147         if(typeof key == "object" && !(key instanceof Array)){
29148             keyCode = key["key"];
29149             shift = key["shift"];
29150             ctrl = key["ctrl"];
29151             alt = key["alt"];
29152         }else{
29153             keyCode = key;
29154         }
29155         var handler = function(dlg, e){
29156             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29157                 var k = e.getKey();
29158                 if(keyCode instanceof Array){
29159                     for(var i = 0, len = keyCode.length; i < len; i++){
29160                         if(keyCode[i] == k){
29161                           fn.call(scope || window, dlg, k, e);
29162                           return;
29163                         }
29164                     }
29165                 }else{
29166                     if(k == keyCode){
29167                         fn.call(scope || window, dlg, k, e);
29168                     }
29169                 }
29170             }
29171         };
29172         this.on("keydown", handler);
29173         return this;
29174     },
29175
29176     /**
29177      * Returns the TabPanel component (creates it if it doesn't exist).
29178      * Note: If you wish to simply check for the existence of tabs without creating them,
29179      * check for a null 'tabs' property.
29180      * @return {Roo.TabPanel} The tabs component
29181      */
29182     getTabs : function(){
29183         if(!this.tabs){
29184             this.el.addClass("x-dlg-auto-tabs");
29185             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29186             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29187         }
29188         return this.tabs;
29189     },
29190
29191     /**
29192      * Adds a button to the footer section of the dialog.
29193      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29194      * object or a valid Roo.DomHelper element config
29195      * @param {Function} handler The function called when the button is clicked
29196      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29197      * @return {Roo.Button} The new button
29198      */
29199     addButton : function(config, handler, scope){
29200         var dh = Roo.DomHelper;
29201         if(!this.footer){
29202             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29203         }
29204         if(!this.btnContainer){
29205             var tb = this.footer.createChild({
29206
29207                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29208                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29209             }, null, true);
29210             this.btnContainer = tb.firstChild.firstChild.firstChild;
29211         }
29212         var bconfig = {
29213             handler: handler,
29214             scope: scope,
29215             minWidth: this.minButtonWidth,
29216             hideParent:true
29217         };
29218         if(typeof config == "string"){
29219             bconfig.text = config;
29220         }else{
29221             if(config.tag){
29222                 bconfig.dhconfig = config;
29223             }else{
29224                 Roo.apply(bconfig, config);
29225             }
29226         }
29227         var fc = false;
29228         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29229             bconfig.position = Math.max(0, bconfig.position);
29230             fc = this.btnContainer.childNodes[bconfig.position];
29231         }
29232          
29233         var btn = new Roo.Button(
29234             fc ? 
29235                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29236                 : this.btnContainer.appendChild(document.createElement("td")),
29237             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29238             bconfig
29239         );
29240         this.syncBodyHeight();
29241         if(!this.buttons){
29242             /**
29243              * Array of all the buttons that have been added to this dialog via addButton
29244              * @type Array
29245              */
29246             this.buttons = [];
29247         }
29248         this.buttons.push(btn);
29249         return btn;
29250     },
29251
29252     /**
29253      * Sets the default button to be focused when the dialog is displayed.
29254      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29255      * @return {Roo.BasicDialog} this
29256      */
29257     setDefaultButton : function(btn){
29258         this.defaultButton = btn;
29259         return this;
29260     },
29261
29262     // private
29263     getHeaderFooterHeight : function(safe){
29264         var height = 0;
29265         if(this.header){
29266            height += this.header.getHeight();
29267         }
29268         if(this.footer){
29269            var fm = this.footer.getMargins();
29270             height += (this.footer.getHeight()+fm.top+fm.bottom);
29271         }
29272         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29273         height += this.centerBg.getPadding("tb");
29274         return height;
29275     },
29276
29277     // private
29278     syncBodyHeight : function(){
29279         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29280         var height = this.size.height - this.getHeaderFooterHeight(false);
29281         bd.setHeight(height-bd.getMargins("tb"));
29282         var hh = this.header.getHeight();
29283         var h = this.size.height-hh;
29284         cb.setHeight(h);
29285         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29286         bw.setHeight(h-cb.getPadding("tb"));
29287         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29288         bd.setWidth(bw.getWidth(true));
29289         if(this.tabs){
29290             this.tabs.syncHeight();
29291             if(Roo.isIE){
29292                 this.tabs.el.repaint();
29293             }
29294         }
29295     },
29296
29297     /**
29298      * Restores the previous state of the dialog if Roo.state is configured.
29299      * @return {Roo.BasicDialog} this
29300      */
29301     restoreState : function(){
29302         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29303         if(box && box.width){
29304             this.xy = [box.x, box.y];
29305             this.resizeTo(box.width, box.height);
29306         }
29307         return this;
29308     },
29309
29310     // private
29311     beforeShow : function(){
29312         this.expand();
29313         if(this.fixedcenter){
29314             this.xy = this.el.getCenterXY(true);
29315         }
29316         if(this.modal){
29317             Roo.get(document.body).addClass("x-body-masked");
29318             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29319             this.mask.show();
29320         }
29321         this.constrainXY();
29322     },
29323
29324     // private
29325     animShow : function(){
29326         var b = Roo.get(this.animateTarget).getBox();
29327         this.proxy.setSize(b.width, b.height);
29328         this.proxy.setLocation(b.x, b.y);
29329         this.proxy.show();
29330         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29331                     true, .35, this.showEl.createDelegate(this));
29332     },
29333
29334     /**
29335      * Shows the dialog.
29336      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29337      * @return {Roo.BasicDialog} this
29338      */
29339     show : function(animateTarget){
29340         if (this.fireEvent("beforeshow", this) === false){
29341             return;
29342         }
29343         if(this.syncHeightBeforeShow){
29344             this.syncBodyHeight();
29345         }else if(this.firstShow){
29346             this.firstShow = false;
29347             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29348         }
29349         this.animateTarget = animateTarget || this.animateTarget;
29350         if(!this.el.isVisible()){
29351             this.beforeShow();
29352             if(this.animateTarget && Roo.get(this.animateTarget)){
29353                 this.animShow();
29354             }else{
29355                 this.showEl();
29356             }
29357         }
29358         return this;
29359     },
29360
29361     // private
29362     showEl : function(){
29363         this.proxy.hide();
29364         this.el.setXY(this.xy);
29365         this.el.show();
29366         this.adjustAssets(true);
29367         this.toFront();
29368         this.focus();
29369         // IE peekaboo bug - fix found by Dave Fenwick
29370         if(Roo.isIE){
29371             this.el.repaint();
29372         }
29373         this.fireEvent("show", this);
29374     },
29375
29376     /**
29377      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29378      * dialog itself will receive focus.
29379      */
29380     focus : function(){
29381         if(this.defaultButton){
29382             this.defaultButton.focus();
29383         }else{
29384             this.focusEl.focus();
29385         }
29386     },
29387
29388     // private
29389     constrainXY : function(){
29390         if(this.constraintoviewport !== false){
29391             if(!this.viewSize){
29392                 if(this.container){
29393                     var s = this.container.getSize();
29394                     this.viewSize = [s.width, s.height];
29395                 }else{
29396                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29397                 }
29398             }
29399             var s = Roo.get(this.container||document).getScroll();
29400
29401             var x = this.xy[0], y = this.xy[1];
29402             var w = this.size.width, h = this.size.height;
29403             var vw = this.viewSize[0], vh = this.viewSize[1];
29404             // only move it if it needs it
29405             var moved = false;
29406             // first validate right/bottom
29407             if(x + w > vw+s.left){
29408                 x = vw - w;
29409                 moved = true;
29410             }
29411             if(y + h > vh+s.top){
29412                 y = vh - h;
29413                 moved = true;
29414             }
29415             // then make sure top/left isn't negative
29416             if(x < s.left){
29417                 x = s.left;
29418                 moved = true;
29419             }
29420             if(y < s.top){
29421                 y = s.top;
29422                 moved = true;
29423             }
29424             if(moved){
29425                 // cache xy
29426                 this.xy = [x, y];
29427                 if(this.isVisible()){
29428                     this.el.setLocation(x, y);
29429                     this.adjustAssets();
29430                 }
29431             }
29432         }
29433     },
29434
29435     // private
29436     onDrag : function(){
29437         if(!this.proxyDrag){
29438             this.xy = this.el.getXY();
29439             this.adjustAssets();
29440         }
29441     },
29442
29443     // private
29444     adjustAssets : function(doShow){
29445         var x = this.xy[0], y = this.xy[1];
29446         var w = this.size.width, h = this.size.height;
29447         if(doShow === true){
29448             if(this.shadow){
29449                 this.shadow.show(this.el);
29450             }
29451             if(this.shim){
29452                 this.shim.show();
29453             }
29454         }
29455         if(this.shadow && this.shadow.isVisible()){
29456             this.shadow.show(this.el);
29457         }
29458         if(this.shim && this.shim.isVisible()){
29459             this.shim.setBounds(x, y, w, h);
29460         }
29461     },
29462
29463     // private
29464     adjustViewport : function(w, h){
29465         if(!w || !h){
29466             w = Roo.lib.Dom.getViewWidth();
29467             h = Roo.lib.Dom.getViewHeight();
29468         }
29469         // cache the size
29470         this.viewSize = [w, h];
29471         if(this.modal && this.mask.isVisible()){
29472             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29473             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29474         }
29475         if(this.isVisible()){
29476             this.constrainXY();
29477         }
29478     },
29479
29480     /**
29481      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29482      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29483      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29484      */
29485     destroy : function(removeEl){
29486         if(this.isVisible()){
29487             this.animateTarget = null;
29488             this.hide();
29489         }
29490         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29491         if(this.tabs){
29492             this.tabs.destroy(removeEl);
29493         }
29494         Roo.destroy(
29495              this.shim,
29496              this.proxy,
29497              this.resizer,
29498              this.close,
29499              this.mask
29500         );
29501         if(this.dd){
29502             this.dd.unreg();
29503         }
29504         if(this.buttons){
29505            for(var i = 0, len = this.buttons.length; i < len; i++){
29506                this.buttons[i].destroy();
29507            }
29508         }
29509         this.el.removeAllListeners();
29510         if(removeEl === true){
29511             this.el.update("");
29512             this.el.remove();
29513         }
29514         Roo.DialogManager.unregister(this);
29515     },
29516
29517     // private
29518     startMove : function(){
29519         if(this.proxyDrag){
29520             this.proxy.show();
29521         }
29522         if(this.constraintoviewport !== false){
29523             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29524         }
29525     },
29526
29527     // private
29528     endMove : function(){
29529         if(!this.proxyDrag){
29530             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29531         }else{
29532             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29533             this.proxy.hide();
29534         }
29535         this.refreshSize();
29536         this.adjustAssets();
29537         this.focus();
29538         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29539     },
29540
29541     /**
29542      * Brings this dialog to the front of any other visible dialogs
29543      * @return {Roo.BasicDialog} this
29544      */
29545     toFront : function(){
29546         Roo.DialogManager.bringToFront(this);
29547         return this;
29548     },
29549
29550     /**
29551      * Sends this dialog to the back (under) of any other visible dialogs
29552      * @return {Roo.BasicDialog} this
29553      */
29554     toBack : function(){
29555         Roo.DialogManager.sendToBack(this);
29556         return this;
29557     },
29558
29559     /**
29560      * Centers this dialog in the viewport
29561      * @return {Roo.BasicDialog} this
29562      */
29563     center : function(){
29564         var xy = this.el.getCenterXY(true);
29565         this.moveTo(xy[0], xy[1]);
29566         return this;
29567     },
29568
29569     /**
29570      * Moves the dialog's top-left corner to the specified point
29571      * @param {Number} x
29572      * @param {Number} y
29573      * @return {Roo.BasicDialog} this
29574      */
29575     moveTo : function(x, y){
29576         this.xy = [x,y];
29577         if(this.isVisible()){
29578             this.el.setXY(this.xy);
29579             this.adjustAssets();
29580         }
29581         return this;
29582     },
29583
29584     /**
29585      * Aligns the dialog to the specified element
29586      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29587      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29588      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29589      * @return {Roo.BasicDialog} this
29590      */
29591     alignTo : function(element, position, offsets){
29592         this.xy = this.el.getAlignToXY(element, position, offsets);
29593         if(this.isVisible()){
29594             this.el.setXY(this.xy);
29595             this.adjustAssets();
29596         }
29597         return this;
29598     },
29599
29600     /**
29601      * Anchors an element to another element and realigns it when the window is resized.
29602      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29603      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29604      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29605      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29606      * is a number, it is used as the buffer delay (defaults to 50ms).
29607      * @return {Roo.BasicDialog} this
29608      */
29609     anchorTo : function(el, alignment, offsets, monitorScroll){
29610         var action = function(){
29611             this.alignTo(el, alignment, offsets);
29612         };
29613         Roo.EventManager.onWindowResize(action, this);
29614         var tm = typeof monitorScroll;
29615         if(tm != 'undefined'){
29616             Roo.EventManager.on(window, 'scroll', action, this,
29617                 {buffer: tm == 'number' ? monitorScroll : 50});
29618         }
29619         action.call(this);
29620         return this;
29621     },
29622
29623     /**
29624      * Returns true if the dialog is visible
29625      * @return {Boolean}
29626      */
29627     isVisible : function(){
29628         return this.el.isVisible();
29629     },
29630
29631     // private
29632     animHide : function(callback){
29633         var b = Roo.get(this.animateTarget).getBox();
29634         this.proxy.show();
29635         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29636         this.el.hide();
29637         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29638                     this.hideEl.createDelegate(this, [callback]));
29639     },
29640
29641     /**
29642      * Hides the dialog.
29643      * @param {Function} callback (optional) Function to call when the dialog is hidden
29644      * @return {Roo.BasicDialog} this
29645      */
29646     hide : function(callback){
29647         if (this.fireEvent("beforehide", this) === false){
29648             return;
29649         }
29650         if(this.shadow){
29651             this.shadow.hide();
29652         }
29653         if(this.shim) {
29654           this.shim.hide();
29655         }
29656         // sometimes animateTarget seems to get set.. causing problems...
29657         // this just double checks..
29658         if(this.animateTarget && Roo.get(this.animateTarget)) {
29659            this.animHide(callback);
29660         }else{
29661             this.el.hide();
29662             this.hideEl(callback);
29663         }
29664         return this;
29665     },
29666
29667     // private
29668     hideEl : function(callback){
29669         this.proxy.hide();
29670         if(this.modal){
29671             this.mask.hide();
29672             Roo.get(document.body).removeClass("x-body-masked");
29673         }
29674         this.fireEvent("hide", this);
29675         if(typeof callback == "function"){
29676             callback();
29677         }
29678     },
29679
29680     // private
29681     hideAction : function(){
29682         this.setLeft("-10000px");
29683         this.setTop("-10000px");
29684         this.setStyle("visibility", "hidden");
29685     },
29686
29687     // private
29688     refreshSize : function(){
29689         this.size = this.el.getSize();
29690         this.xy = this.el.getXY();
29691         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29692     },
29693
29694     // private
29695     // z-index is managed by the DialogManager and may be overwritten at any time
29696     setZIndex : function(index){
29697         if(this.modal){
29698             this.mask.setStyle("z-index", index);
29699         }
29700         if(this.shim){
29701             this.shim.setStyle("z-index", ++index);
29702         }
29703         if(this.shadow){
29704             this.shadow.setZIndex(++index);
29705         }
29706         this.el.setStyle("z-index", ++index);
29707         if(this.proxy){
29708             this.proxy.setStyle("z-index", ++index);
29709         }
29710         if(this.resizer){
29711             this.resizer.proxy.setStyle("z-index", ++index);
29712         }
29713
29714         this.lastZIndex = index;
29715     },
29716
29717     /**
29718      * Returns the element for this dialog
29719      * @return {Roo.Element} The underlying dialog Element
29720      */
29721     getEl : function(){
29722         return this.el;
29723     }
29724 });
29725
29726 /**
29727  * @class Roo.DialogManager
29728  * Provides global access to BasicDialogs that have been created and
29729  * support for z-indexing (layering) multiple open dialogs.
29730  */
29731 Roo.DialogManager = function(){
29732     var list = {};
29733     var accessList = [];
29734     var front = null;
29735
29736     // private
29737     var sortDialogs = function(d1, d2){
29738         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29739     };
29740
29741     // private
29742     var orderDialogs = function(){
29743         accessList.sort(sortDialogs);
29744         var seed = Roo.DialogManager.zseed;
29745         for(var i = 0, len = accessList.length; i < len; i++){
29746             var dlg = accessList[i];
29747             if(dlg){
29748                 dlg.setZIndex(seed + (i*10));
29749             }
29750         }
29751     };
29752
29753     return {
29754         /**
29755          * The starting z-index for BasicDialogs (defaults to 9000)
29756          * @type Number The z-index value
29757          */
29758         zseed : 9000,
29759
29760         // private
29761         register : function(dlg){
29762             list[dlg.id] = dlg;
29763             accessList.push(dlg);
29764         },
29765
29766         // private
29767         unregister : function(dlg){
29768             delete list[dlg.id];
29769             var i=0;
29770             var len=0;
29771             if(!accessList.indexOf){
29772                 for(  i = 0, len = accessList.length; i < len; i++){
29773                     if(accessList[i] == dlg){
29774                         accessList.splice(i, 1);
29775                         return;
29776                     }
29777                 }
29778             }else{
29779                  i = accessList.indexOf(dlg);
29780                 if(i != -1){
29781                     accessList.splice(i, 1);
29782                 }
29783             }
29784         },
29785
29786         /**
29787          * Gets a registered dialog by id
29788          * @param {String/Object} id The id of the dialog or a dialog
29789          * @return {Roo.BasicDialog} this
29790          */
29791         get : function(id){
29792             return typeof id == "object" ? id : list[id];
29793         },
29794
29795         /**
29796          * Brings the specified dialog to the front
29797          * @param {String/Object} dlg The id of the dialog or a dialog
29798          * @return {Roo.BasicDialog} this
29799          */
29800         bringToFront : function(dlg){
29801             dlg = this.get(dlg);
29802             if(dlg != front){
29803                 front = dlg;
29804                 dlg._lastAccess = new Date().getTime();
29805                 orderDialogs();
29806             }
29807             return dlg;
29808         },
29809
29810         /**
29811          * Sends the specified dialog to the back
29812          * @param {String/Object} dlg The id of the dialog or a dialog
29813          * @return {Roo.BasicDialog} this
29814          */
29815         sendToBack : function(dlg){
29816             dlg = this.get(dlg);
29817             dlg._lastAccess = -(new Date().getTime());
29818             orderDialogs();
29819             return dlg;
29820         },
29821
29822         /**
29823          * Hides all dialogs
29824          */
29825         hideAll : function(){
29826             for(var id in list){
29827                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29828                     list[id].hide();
29829                 }
29830             }
29831         }
29832     };
29833 }();
29834
29835 /**
29836  * @class Roo.LayoutDialog
29837  * @extends Roo.BasicDialog
29838  * Dialog which provides adjustments for working with a layout in a Dialog.
29839  * Add your necessary layout config options to the dialog's config.<br>
29840  * Example usage (including a nested layout):
29841  * <pre><code>
29842 if(!dialog){
29843     dialog = new Roo.LayoutDialog("download-dlg", {
29844         modal: true,
29845         width:600,
29846         height:450,
29847         shadow:true,
29848         minWidth:500,
29849         minHeight:350,
29850         autoTabs:true,
29851         proxyDrag:true,
29852         // layout config merges with the dialog config
29853         center:{
29854             tabPosition: "top",
29855             alwaysShowTabs: true
29856         }
29857     });
29858     dialog.addKeyListener(27, dialog.hide, dialog);
29859     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29860     dialog.addButton("Build It!", this.getDownload, this);
29861
29862     // we can even add nested layouts
29863     var innerLayout = new Roo.BorderLayout("dl-inner", {
29864         east: {
29865             initialSize: 200,
29866             autoScroll:true,
29867             split:true
29868         },
29869         center: {
29870             autoScroll:true
29871         }
29872     });
29873     innerLayout.beginUpdate();
29874     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29875     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29876     innerLayout.endUpdate(true);
29877
29878     var layout = dialog.getLayout();
29879     layout.beginUpdate();
29880     layout.add("center", new Roo.ContentPanel("standard-panel",
29881                         {title: "Download the Source", fitToFrame:true}));
29882     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29883                {title: "Build your own roo.js"}));
29884     layout.getRegion("center").showPanel(sp);
29885     layout.endUpdate();
29886 }
29887 </code></pre>
29888     * @constructor
29889     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29890     * @param {Object} config configuration options
29891   */
29892 Roo.LayoutDialog = function(el, cfg){
29893     
29894     var config=  cfg;
29895     if (typeof(cfg) == 'undefined') {
29896         config = Roo.apply({}, el);
29897         // not sure why we use documentElement here.. - it should always be body.
29898         // IE7 borks horribly if we use documentElement.
29899         // webkit also does not like documentElement - it creates a body element...
29900         el = Roo.get( document.body || document.documentElement ).createChild();
29901         //config.autoCreate = true;
29902     }
29903     
29904     
29905     config.autoTabs = false;
29906     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29907     this.body.setStyle({overflow:"hidden", position:"relative"});
29908     this.layout = new Roo.BorderLayout(this.body.dom, config);
29909     this.layout.monitorWindowResize = false;
29910     this.el.addClass("x-dlg-auto-layout");
29911     // fix case when center region overwrites center function
29912     this.center = Roo.BasicDialog.prototype.center;
29913     this.on("show", this.layout.layout, this.layout, true);
29914     if (config.items) {
29915         var xitems = config.items;
29916         delete config.items;
29917         Roo.each(xitems, this.addxtype, this);
29918     }
29919     
29920     
29921 };
29922 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29923     /**
29924      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29925      * @deprecated
29926      */
29927     endUpdate : function(){
29928         this.layout.endUpdate();
29929     },
29930
29931     /**
29932      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29933      *  @deprecated
29934      */
29935     beginUpdate : function(){
29936         this.layout.beginUpdate();
29937     },
29938
29939     /**
29940      * Get the BorderLayout for this dialog
29941      * @return {Roo.BorderLayout}
29942      */
29943     getLayout : function(){
29944         return this.layout;
29945     },
29946
29947     showEl : function(){
29948         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29949         if(Roo.isIE7){
29950             this.layout.layout();
29951         }
29952     },
29953
29954     // private
29955     // Use the syncHeightBeforeShow config option to control this automatically
29956     syncBodyHeight : function(){
29957         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29958         if(this.layout){this.layout.layout();}
29959     },
29960     
29961       /**
29962      * Add an xtype element (actually adds to the layout.)
29963      * @return {Object} xdata xtype object data.
29964      */
29965     
29966     addxtype : function(c) {
29967         return this.layout.addxtype(c);
29968     }
29969 });/*
29970  * Based on:
29971  * Ext JS Library 1.1.1
29972  * Copyright(c) 2006-2007, Ext JS, LLC.
29973  *
29974  * Originally Released Under LGPL - original licence link has changed is not relivant.
29975  *
29976  * Fork - LGPL
29977  * <script type="text/javascript">
29978  */
29979  
29980 /**
29981  * @class Roo.MessageBox
29982  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29983  * Example usage:
29984  *<pre><code>
29985 // Basic alert:
29986 Roo.Msg.alert('Status', 'Changes saved successfully.');
29987
29988 // Prompt for user data:
29989 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29990     if (btn == 'ok'){
29991         // process text value...
29992     }
29993 });
29994
29995 // Show a dialog using config options:
29996 Roo.Msg.show({
29997    title:'Save Changes?',
29998    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29999    buttons: Roo.Msg.YESNOCANCEL,
30000    fn: processResult,
30001    animEl: 'elId'
30002 });
30003 </code></pre>
30004  * @singleton
30005  */
30006 Roo.MessageBox = function(){
30007     var dlg, opt, mask, waitTimer;
30008     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30009     var buttons, activeTextEl, bwidth;
30010
30011     // private
30012     var handleButton = function(button){
30013         dlg.hide();
30014         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30015     };
30016
30017     // private
30018     var handleHide = function(){
30019         if(opt && opt.cls){
30020             dlg.el.removeClass(opt.cls);
30021         }
30022         if(waitTimer){
30023             Roo.TaskMgr.stop(waitTimer);
30024             waitTimer = null;
30025         }
30026     };
30027
30028     // private
30029     var updateButtons = function(b){
30030         var width = 0;
30031         if(!b){
30032             buttons["ok"].hide();
30033             buttons["cancel"].hide();
30034             buttons["yes"].hide();
30035             buttons["no"].hide();
30036             dlg.footer.dom.style.display = 'none';
30037             return width;
30038         }
30039         dlg.footer.dom.style.display = '';
30040         for(var k in buttons){
30041             if(typeof buttons[k] != "function"){
30042                 if(b[k]){
30043                     buttons[k].show();
30044                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30045                     width += buttons[k].el.getWidth()+15;
30046                 }else{
30047                     buttons[k].hide();
30048                 }
30049             }
30050         }
30051         return width;
30052     };
30053
30054     // private
30055     var handleEsc = function(d, k, e){
30056         if(opt && opt.closable !== false){
30057             dlg.hide();
30058         }
30059         if(e){
30060             e.stopEvent();
30061         }
30062     };
30063
30064     return {
30065         /**
30066          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30067          * @return {Roo.BasicDialog} The BasicDialog element
30068          */
30069         getDialog : function(){
30070            if(!dlg){
30071                 dlg = new Roo.BasicDialog("x-msg-box", {
30072                     autoCreate : true,
30073                     shadow: true,
30074                     draggable: true,
30075                     resizable:false,
30076                     constraintoviewport:false,
30077                     fixedcenter:true,
30078                     collapsible : false,
30079                     shim:true,
30080                     modal: true,
30081                     width:400, height:100,
30082                     buttonAlign:"center",
30083                     closeClick : function(){
30084                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30085                             handleButton("no");
30086                         }else{
30087                             handleButton("cancel");
30088                         }
30089                     }
30090                 });
30091                 dlg.on("hide", handleHide);
30092                 mask = dlg.mask;
30093                 dlg.addKeyListener(27, handleEsc);
30094                 buttons = {};
30095                 var bt = this.buttonText;
30096                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30097                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30098                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30099                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30100                 bodyEl = dlg.body.createChild({
30101
30102                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30103                 });
30104                 msgEl = bodyEl.dom.firstChild;
30105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30106                 textboxEl.enableDisplayMode();
30107                 textboxEl.addKeyListener([10,13], function(){
30108                     if(dlg.isVisible() && opt && opt.buttons){
30109                         if(opt.buttons.ok){
30110                             handleButton("ok");
30111                         }else if(opt.buttons.yes){
30112                             handleButton("yes");
30113                         }
30114                     }
30115                 });
30116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30117                 textareaEl.enableDisplayMode();
30118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30119                 progressEl.enableDisplayMode();
30120                 var pf = progressEl.dom.firstChild;
30121                 if (pf) {
30122                     pp = Roo.get(pf.firstChild);
30123                     pp.setHeight(pf.offsetHeight);
30124                 }
30125                 
30126             }
30127             return dlg;
30128         },
30129
30130         /**
30131          * Updates the message box body text
30132          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30133          * the XHTML-compliant non-breaking space character '&amp;#160;')
30134          * @return {Roo.MessageBox} This message box
30135          */
30136         updateText : function(text){
30137             if(!dlg.isVisible() && !opt.width){
30138                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30139             }
30140             msgEl.innerHTML = text || '&#160;';
30141             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30142                         Math.max(opt.minWidth || this.minWidth, bwidth));
30143             if(opt.prompt){
30144                 activeTextEl.setWidth(w);
30145             }
30146             if(dlg.isVisible()){
30147                 dlg.fixedcenter = false;
30148             }
30149             // to big, make it scoll.
30150             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30151                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30152                 bodyEl.dom.style.overflowY = 'auto';
30153             } else {
30154                 bodyEl.dom.style.height = '';
30155                 bodyEl.dom.style.overflowY = '';
30156             }
30157             
30158             dlg.setContentSize(w, bodyEl.getHeight());
30159             if(dlg.isVisible()){
30160                 dlg.fixedcenter = true;
30161             }
30162             return this;
30163         },
30164
30165         /**
30166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30170          * @return {Roo.MessageBox} This message box
30171          */
30172         updateProgress : function(value, text){
30173             if(text){
30174                 this.updateText(text);
30175             }
30176             if (pp) { // weird bug on my firefox - for some reason this is not defined
30177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30178             }
30179             return this;
30180         },        
30181
30182         /**
30183          * Returns true if the message box is currently displayed
30184          * @return {Boolean} True if the message box is visible, else false
30185          */
30186         isVisible : function(){
30187             return dlg && dlg.isVisible();  
30188         },
30189
30190         /**
30191          * Hides the message box if it is displayed
30192          */
30193         hide : function(){
30194             if(this.isVisible()){
30195                 dlg.hide();
30196             }  
30197         },
30198
30199         /**
30200          * Displays a new message box, or reinitializes an existing message box, based on the config options
30201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30202          * The following config object properties are supported:
30203          * <pre>
30204 Property    Type             Description
30205 ----------  ---------------  ------------------------------------------------------------------------------------
30206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30207                                    closes (defaults to undefined)
30208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30211                                    progress and wait dialogs will ignore this property and always hide the
30212                                    close button as they can only be closed programmatically.
30213 cls               String           A custom CSS class to apply to the message box element
30214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30215                                    displayed (defaults to 75)
30216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30217                                    function will be btn (the name of the button that was clicked, if applicable,
30218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30219                                    Progress and wait dialogs will ignore this option since they do not respond to
30220                                    user actions and can only be closed programmatically, so any required function
30221                                    should be called by the same code after it closes the dialog.
30222 icon              String           A CSS class that provides a background image to be used as an icon for
30223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30226 modal             Boolean          False to allow user interaction with the page while the message box is
30227                                    displayed (defaults to true)
30228 msg               String           A string that will replace the existing message box body text (defaults
30229                                    to the XHTML-compliant non-breaking space character '&#160;')
30230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30231 progress          Boolean          True to display a progress bar (defaults to false)
30232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30235 title             String           The title text
30236 value             String           The string value to set into the active textbox element if displayed
30237 wait              Boolean          True to display a progress bar (defaults to false)
30238 width             Number           The width of the dialog in pixels
30239 </pre>
30240          *
30241          * Example usage:
30242          * <pre><code>
30243 Roo.Msg.show({
30244    title: 'Address',
30245    msg: 'Please enter your address:',
30246    width: 300,
30247    buttons: Roo.MessageBox.OKCANCEL,
30248    multiline: true,
30249    fn: saveAddress,
30250    animEl: 'addAddressBtn'
30251 });
30252 </code></pre>
30253          * @param {Object} config Configuration options
30254          * @return {Roo.MessageBox} This message box
30255          */
30256         show : function(options)
30257         {
30258             
30259             // this causes nightmares if you show one dialog after another
30260             // especially on callbacks..
30261              
30262             if(this.isVisible()){
30263                 
30264                 this.hide();
30265                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30266                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30267                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30268                 
30269             }
30270             var d = this.getDialog();
30271             opt = options;
30272             d.setTitle(opt.title || "&#160;");
30273             d.close.setDisplayed(opt.closable !== false);
30274             activeTextEl = textboxEl;
30275             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30276             if(opt.prompt){
30277                 if(opt.multiline){
30278                     textboxEl.hide();
30279                     textareaEl.show();
30280                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30281                         opt.multiline : this.defaultTextHeight);
30282                     activeTextEl = textareaEl;
30283                 }else{
30284                     textboxEl.show();
30285                     textareaEl.hide();
30286                 }
30287             }else{
30288                 textboxEl.hide();
30289                 textareaEl.hide();
30290             }
30291             progressEl.setDisplayed(opt.progress === true);
30292             this.updateProgress(0);
30293             activeTextEl.dom.value = opt.value || "";
30294             if(opt.prompt){
30295                 dlg.setDefaultButton(activeTextEl);
30296             }else{
30297                 var bs = opt.buttons;
30298                 var db = null;
30299                 if(bs && bs.ok){
30300                     db = buttons["ok"];
30301                 }else if(bs && bs.yes){
30302                     db = buttons["yes"];
30303                 }
30304                 dlg.setDefaultButton(db);
30305             }
30306             bwidth = updateButtons(opt.buttons);
30307             this.updateText(opt.msg);
30308             if(opt.cls){
30309                 d.el.addClass(opt.cls);
30310             }
30311             d.proxyDrag = opt.proxyDrag === true;
30312             d.modal = opt.modal !== false;
30313             d.mask = opt.modal !== false ? mask : false;
30314             if(!d.isVisible()){
30315                 // force it to the end of the z-index stack so it gets a cursor in FF
30316                 document.body.appendChild(dlg.el.dom);
30317                 d.animateTarget = null;
30318                 d.show(options.animEl);
30319             }
30320             return this;
30321         },
30322
30323         /**
30324          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30325          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30326          * and closing the message box when the process is complete.
30327          * @param {String} title The title bar text
30328          * @param {String} msg The message box body text
30329          * @return {Roo.MessageBox} This message box
30330          */
30331         progress : function(title, msg){
30332             this.show({
30333                 title : title,
30334                 msg : msg,
30335                 buttons: false,
30336                 progress:true,
30337                 closable:false,
30338                 minWidth: this.minProgressWidth,
30339                 modal : true
30340             });
30341             return this;
30342         },
30343
30344         /**
30345          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30346          * If a callback function is passed it will be called after the user clicks the button, and the
30347          * id of the button that was clicked will be passed as the only parameter to the callback
30348          * (could also be the top-right close button).
30349          * @param {String} title The title bar text
30350          * @param {String} msg The message box body text
30351          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30352          * @param {Object} scope (optional) The scope of the callback function
30353          * @return {Roo.MessageBox} This message box
30354          */
30355         alert : function(title, msg, fn, scope){
30356             this.show({
30357                 title : title,
30358                 msg : msg,
30359                 buttons: this.OK,
30360                 fn: fn,
30361                 scope : scope,
30362                 modal : true
30363             });
30364             return this;
30365         },
30366
30367         /**
30368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30370          * You are responsible for closing the message box when the process is complete.
30371          * @param {String} msg The message box body text
30372          * @param {String} title (optional) The title bar text
30373          * @return {Roo.MessageBox} This message box
30374          */
30375         wait : function(msg, title){
30376             this.show({
30377                 title : title,
30378                 msg : msg,
30379                 buttons: false,
30380                 closable:false,
30381                 progress:true,
30382                 modal:true,
30383                 width:300,
30384                 wait:true
30385             });
30386             waitTimer = Roo.TaskMgr.start({
30387                 run: function(i){
30388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30389                 },
30390                 interval: 1000
30391             });
30392             return this;
30393         },
30394
30395         /**
30396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30399          * @param {String} title The title bar text
30400          * @param {String} msg The message box body text
30401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30402          * @param {Object} scope (optional) The scope of the callback function
30403          * @return {Roo.MessageBox} This message box
30404          */
30405         confirm : function(title, msg, fn, scope){
30406             this.show({
30407                 title : title,
30408                 msg : msg,
30409                 buttons: this.YESNO,
30410                 fn: fn,
30411                 scope : scope,
30412                 modal : true
30413             });
30414             return this;
30415         },
30416
30417         /**
30418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30421          * (could also be the top-right close button) and the text that was entered will be passed as the two
30422          * parameters to the callback.
30423          * @param {String} title The title bar text
30424          * @param {String} msg The message box body text
30425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30426          * @param {Object} scope (optional) The scope of the callback function
30427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30429          * @return {Roo.MessageBox} This message box
30430          */
30431         prompt : function(title, msg, fn, scope, multiline){
30432             this.show({
30433                 title : title,
30434                 msg : msg,
30435                 buttons: this.OKCANCEL,
30436                 fn: fn,
30437                 minWidth:250,
30438                 scope : scope,
30439                 prompt:true,
30440                 multiline: multiline,
30441                 modal : true
30442             });
30443             return this;
30444         },
30445
30446         /**
30447          * Button config that displays a single OK button
30448          * @type Object
30449          */
30450         OK : {ok:true},
30451         /**
30452          * Button config that displays Yes and No buttons
30453          * @type Object
30454          */
30455         YESNO : {yes:true, no:true},
30456         /**
30457          * Button config that displays OK and Cancel buttons
30458          * @type Object
30459          */
30460         OKCANCEL : {ok:true, cancel:true},
30461         /**
30462          * Button config that displays Yes, No and Cancel buttons
30463          * @type Object
30464          */
30465         YESNOCANCEL : {yes:true, no:true, cancel:true},
30466
30467         /**
30468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30469          * @type Number
30470          */
30471         defaultTextHeight : 75,
30472         /**
30473          * The maximum width in pixels of the message box (defaults to 600)
30474          * @type Number
30475          */
30476         maxWidth : 600,
30477         /**
30478          * The minimum width in pixels of the message box (defaults to 100)
30479          * @type Number
30480          */
30481         minWidth : 100,
30482         /**
30483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30485          * @type Number
30486          */
30487         minProgressWidth : 250,
30488         /**
30489          * An object containing the default button text strings that can be overriden for localized language support.
30490          * Supported properties are: ok, cancel, yes and no.
30491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30492          * @type Object
30493          */
30494         buttonText : {
30495             ok : "OK",
30496             cancel : "Cancel",
30497             yes : "Yes",
30498             no : "No"
30499         }
30500     };
30501 }();
30502
30503 /**
30504  * Shorthand for {@link Roo.MessageBox}
30505  */
30506 Roo.Msg = Roo.MessageBox;/*
30507  * Based on:
30508  * Ext JS Library 1.1.1
30509  * Copyright(c) 2006-2007, Ext JS, LLC.
30510  *
30511  * Originally Released Under LGPL - original licence link has changed is not relivant.
30512  *
30513  * Fork - LGPL
30514  * <script type="text/javascript">
30515  */
30516 /**
30517  * @class Roo.QuickTips
30518  * Provides attractive and customizable tooltips for any element.
30519  * @singleton
30520  */
30521 Roo.QuickTips = function(){
30522     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30523     var ce, bd, xy, dd;
30524     var visible = false, disabled = true, inited = false;
30525     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30526     
30527     var onOver = function(e){
30528         if(disabled){
30529             return;
30530         }
30531         var t = e.getTarget();
30532         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30533             return;
30534         }
30535         if(ce && t == ce.el){
30536             clearTimeout(hideProc);
30537             return;
30538         }
30539         if(t && tagEls[t.id]){
30540             tagEls[t.id].el = t;
30541             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30542             return;
30543         }
30544         var ttp, et = Roo.fly(t);
30545         var ns = cfg.namespace;
30546         if(tm.interceptTitles && t.title){
30547             ttp = t.title;
30548             t.qtip = ttp;
30549             t.removeAttribute("title");
30550             e.preventDefault();
30551         }else{
30552             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30553         }
30554         if(ttp){
30555             showProc = show.defer(tm.showDelay, tm, [{
30556                 el: t, 
30557                 text: ttp, 
30558                 width: et.getAttributeNS(ns, cfg.width),
30559                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30560                 title: et.getAttributeNS(ns, cfg.title),
30561                     cls: et.getAttributeNS(ns, cfg.cls)
30562             }]);
30563         }
30564     };
30565     
30566     var onOut = function(e){
30567         clearTimeout(showProc);
30568         var t = e.getTarget();
30569         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30570             hideProc = setTimeout(hide, tm.hideDelay);
30571         }
30572     };
30573     
30574     var onMove = function(e){
30575         if(disabled){
30576             return;
30577         }
30578         xy = e.getXY();
30579         xy[1] += 18;
30580         if(tm.trackMouse && ce){
30581             el.setXY(xy);
30582         }
30583     };
30584     
30585     var onDown = function(e){
30586         clearTimeout(showProc);
30587         clearTimeout(hideProc);
30588         if(!e.within(el)){
30589             if(tm.hideOnClick){
30590                 hide();
30591                 tm.disable();
30592                 tm.enable.defer(100, tm);
30593             }
30594         }
30595     };
30596     
30597     var getPad = function(){
30598         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30599     };
30600
30601     var show = function(o){
30602         if(disabled){
30603             return;
30604         }
30605         clearTimeout(dismissProc);
30606         ce = o;
30607         if(removeCls){ // in case manually hidden
30608             el.removeClass(removeCls);
30609             removeCls = null;
30610         }
30611         if(ce.cls){
30612             el.addClass(ce.cls);
30613             removeCls = ce.cls;
30614         }
30615         if(ce.title){
30616             tipTitle.update(ce.title);
30617             tipTitle.show();
30618         }else{
30619             tipTitle.update('');
30620             tipTitle.hide();
30621         }
30622         el.dom.style.width  = tm.maxWidth+'px';
30623         //tipBody.dom.style.width = '';
30624         tipBodyText.update(o.text);
30625         var p = getPad(), w = ce.width;
30626         if(!w){
30627             var td = tipBodyText.dom;
30628             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30629             if(aw > tm.maxWidth){
30630                 w = tm.maxWidth;
30631             }else if(aw < tm.minWidth){
30632                 w = tm.minWidth;
30633             }else{
30634                 w = aw;
30635             }
30636         }
30637         //tipBody.setWidth(w);
30638         el.setWidth(parseInt(w, 10) + p);
30639         if(ce.autoHide === false){
30640             close.setDisplayed(true);
30641             if(dd){
30642                 dd.unlock();
30643             }
30644         }else{
30645             close.setDisplayed(false);
30646             if(dd){
30647                 dd.lock();
30648             }
30649         }
30650         if(xy){
30651             el.avoidY = xy[1]-18;
30652             el.setXY(xy);
30653         }
30654         if(tm.animate){
30655             el.setOpacity(.1);
30656             el.setStyle("visibility", "visible");
30657             el.fadeIn({callback: afterShow});
30658         }else{
30659             afterShow();
30660         }
30661     };
30662     
30663     var afterShow = function(){
30664         if(ce){
30665             el.show();
30666             esc.enable();
30667             if(tm.autoDismiss && ce.autoHide !== false){
30668                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30669             }
30670         }
30671     };
30672     
30673     var hide = function(noanim){
30674         clearTimeout(dismissProc);
30675         clearTimeout(hideProc);
30676         ce = null;
30677         if(el.isVisible()){
30678             esc.disable();
30679             if(noanim !== true && tm.animate){
30680                 el.fadeOut({callback: afterHide});
30681             }else{
30682                 afterHide();
30683             } 
30684         }
30685     };
30686     
30687     var afterHide = function(){
30688         el.hide();
30689         if(removeCls){
30690             el.removeClass(removeCls);
30691             removeCls = null;
30692         }
30693     };
30694     
30695     return {
30696         /**
30697         * @cfg {Number} minWidth
30698         * The minimum width of the quick tip (defaults to 40)
30699         */
30700        minWidth : 40,
30701         /**
30702         * @cfg {Number} maxWidth
30703         * The maximum width of the quick tip (defaults to 300)
30704         */
30705        maxWidth : 300,
30706         /**
30707         * @cfg {Boolean} interceptTitles
30708         * True to automatically use the element's DOM title value if available (defaults to false)
30709         */
30710        interceptTitles : false,
30711         /**
30712         * @cfg {Boolean} trackMouse
30713         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30714         */
30715        trackMouse : false,
30716         /**
30717         * @cfg {Boolean} hideOnClick
30718         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30719         */
30720        hideOnClick : true,
30721         /**
30722         * @cfg {Number} showDelay
30723         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30724         */
30725        showDelay : 500,
30726         /**
30727         * @cfg {Number} hideDelay
30728         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30729         */
30730        hideDelay : 200,
30731         /**
30732         * @cfg {Boolean} autoHide
30733         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30734         * Used in conjunction with hideDelay.
30735         */
30736        autoHide : true,
30737         /**
30738         * @cfg {Boolean}
30739         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30740         * (defaults to true).  Used in conjunction with autoDismissDelay.
30741         */
30742        autoDismiss : true,
30743         /**
30744         * @cfg {Number}
30745         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30746         */
30747        autoDismissDelay : 5000,
30748        /**
30749         * @cfg {Boolean} animate
30750         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30751         */
30752        animate : false,
30753
30754        /**
30755         * @cfg {String} title
30756         * Title text to display (defaults to '').  This can be any valid HTML markup.
30757         */
30758         title: '',
30759        /**
30760         * @cfg {String} text
30761         * Body text to display (defaults to '').  This can be any valid HTML markup.
30762         */
30763         text : '',
30764        /**
30765         * @cfg {String} cls
30766         * A CSS class to apply to the base quick tip element (defaults to '').
30767         */
30768         cls : '',
30769        /**
30770         * @cfg {Number} width
30771         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30772         * minWidth or maxWidth.
30773         */
30774         width : null,
30775
30776     /**
30777      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30778      * or display QuickTips in a page.
30779      */
30780        init : function(){
30781           tm = Roo.QuickTips;
30782           cfg = tm.tagConfig;
30783           if(!inited){
30784               if(!Roo.isReady){ // allow calling of init() before onReady
30785                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30786                   return;
30787               }
30788               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30789               el.fxDefaults = {stopFx: true};
30790               // maximum custom styling
30791               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30792               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30793               tipTitle = el.child('h3');
30794               tipTitle.enableDisplayMode("block");
30795               tipBody = el.child('div.x-tip-bd');
30796               tipBodyText = el.child('div.x-tip-bd-inner');
30797               //bdLeft = el.child('div.x-tip-bd-left');
30798               //bdRight = el.child('div.x-tip-bd-right');
30799               close = el.child('div.x-tip-close');
30800               close.enableDisplayMode("block");
30801               close.on("click", hide);
30802               var d = Roo.get(document);
30803               d.on("mousedown", onDown);
30804               d.on("mouseover", onOver);
30805               d.on("mouseout", onOut);
30806               d.on("mousemove", onMove);
30807               esc = d.addKeyListener(27, hide);
30808               esc.disable();
30809               if(Roo.dd.DD){
30810                   dd = el.initDD("default", null, {
30811                       onDrag : function(){
30812                           el.sync();  
30813                       }
30814                   });
30815                   dd.setHandleElId(tipTitle.id);
30816                   dd.lock();
30817               }
30818               inited = true;
30819           }
30820           this.enable(); 
30821        },
30822
30823     /**
30824      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30825      * are supported:
30826      * <pre>
30827 Property    Type                   Description
30828 ----------  ---------------------  ------------------------------------------------------------------------
30829 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30830      * </ul>
30831      * @param {Object} config The config object
30832      */
30833        register : function(config){
30834            var cs = config instanceof Array ? config : arguments;
30835            for(var i = 0, len = cs.length; i < len; i++) {
30836                var c = cs[i];
30837                var target = c.target;
30838                if(target){
30839                    if(target instanceof Array){
30840                        for(var j = 0, jlen = target.length; j < jlen; j++){
30841                            tagEls[target[j]] = c;
30842                        }
30843                    }else{
30844                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30845                    }
30846                }
30847            }
30848        },
30849
30850     /**
30851      * Removes this quick tip from its element and destroys it.
30852      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30853      */
30854        unregister : function(el){
30855            delete tagEls[Roo.id(el)];
30856        },
30857
30858     /**
30859      * Enable this quick tip.
30860      */
30861        enable : function(){
30862            if(inited && disabled){
30863                locks.pop();
30864                if(locks.length < 1){
30865                    disabled = false;
30866                }
30867            }
30868        },
30869
30870     /**
30871      * Disable this quick tip.
30872      */
30873        disable : function(){
30874           disabled = true;
30875           clearTimeout(showProc);
30876           clearTimeout(hideProc);
30877           clearTimeout(dismissProc);
30878           if(ce){
30879               hide(true);
30880           }
30881           locks.push(1);
30882        },
30883
30884     /**
30885      * Returns true if the quick tip is enabled, else false.
30886      */
30887        isEnabled : function(){
30888             return !disabled;
30889        },
30890
30891         // private
30892        tagConfig : {
30893            namespace : "ext",
30894            attribute : "qtip",
30895            width : "width",
30896            target : "target",
30897            title : "qtitle",
30898            hide : "hide",
30899            cls : "qclass"
30900        }
30901    };
30902 }();
30903
30904 // backwards compat
30905 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30906  * Based on:
30907  * Ext JS Library 1.1.1
30908  * Copyright(c) 2006-2007, Ext JS, LLC.
30909  *
30910  * Originally Released Under LGPL - original licence link has changed is not relivant.
30911  *
30912  * Fork - LGPL
30913  * <script type="text/javascript">
30914  */
30915  
30916
30917 /**
30918  * @class Roo.tree.TreePanel
30919  * @extends Roo.data.Tree
30920
30921  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30922  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30923  * @cfg {Boolean} enableDD true to enable drag and drop
30924  * @cfg {Boolean} enableDrag true to enable just drag
30925  * @cfg {Boolean} enableDrop true to enable just drop
30926  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30927  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30928  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30929  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30930  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30931  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30932  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30933  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30934  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30935  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30936  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30937  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30938  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30939  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30940  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30941  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30942  * 
30943  * @constructor
30944  * @param {String/HTMLElement/Element} el The container element
30945  * @param {Object} config
30946  */
30947 Roo.tree.TreePanel = function(el, config){
30948     var root = false;
30949     var loader = false;
30950     if (config.root) {
30951         root = config.root;
30952         delete config.root;
30953     }
30954     if (config.loader) {
30955         loader = config.loader;
30956         delete config.loader;
30957     }
30958     
30959     Roo.apply(this, config);
30960     Roo.tree.TreePanel.superclass.constructor.call(this);
30961     this.el = Roo.get(el);
30962     this.el.addClass('x-tree');
30963     //console.log(root);
30964     if (root) {
30965         this.setRootNode( Roo.factory(root, Roo.tree));
30966     }
30967     if (loader) {
30968         this.loader = Roo.factory(loader, Roo.tree);
30969     }
30970    /**
30971     * Read-only. The id of the container element becomes this TreePanel's id.
30972     */
30973     this.id = this.el.id;
30974     this.addEvents({
30975         /**
30976         * @event beforeload
30977         * Fires before a node is loaded, return false to cancel
30978         * @param {Node} node The node being loaded
30979         */
30980         "beforeload" : true,
30981         /**
30982         * @event load
30983         * Fires when a node is loaded
30984         * @param {Node} node The node that was loaded
30985         */
30986         "load" : true,
30987         /**
30988         * @event textchange
30989         * Fires when the text for a node is changed
30990         * @param {Node} node The node
30991         * @param {String} text The new text
30992         * @param {String} oldText The old text
30993         */
30994         "textchange" : true,
30995         /**
30996         * @event beforeexpand
30997         * Fires before a node is expanded, return false to cancel.
30998         * @param {Node} node The node
30999         * @param {Boolean} deep
31000         * @param {Boolean} anim
31001         */
31002         "beforeexpand" : true,
31003         /**
31004         * @event beforecollapse
31005         * Fires before a node is collapsed, return false to cancel.
31006         * @param {Node} node The node
31007         * @param {Boolean} deep
31008         * @param {Boolean} anim
31009         */
31010         "beforecollapse" : true,
31011         /**
31012         * @event expand
31013         * Fires when a node is expanded
31014         * @param {Node} node The node
31015         */
31016         "expand" : true,
31017         /**
31018         * @event disabledchange
31019         * Fires when the disabled status of a node changes
31020         * @param {Node} node The node
31021         * @param {Boolean} disabled
31022         */
31023         "disabledchange" : true,
31024         /**
31025         * @event collapse
31026         * Fires when a node is collapsed
31027         * @param {Node} node The node
31028         */
31029         "collapse" : true,
31030         /**
31031         * @event beforeclick
31032         * Fires before click processing on a node. Return false to cancel the default action.
31033         * @param {Node} node The node
31034         * @param {Roo.EventObject} e The event object
31035         */
31036         "beforeclick":true,
31037         /**
31038         * @event checkchange
31039         * Fires when a node with a checkbox's checked property changes
31040         * @param {Node} this This node
31041         * @param {Boolean} checked
31042         */
31043         "checkchange":true,
31044         /**
31045         * @event click
31046         * Fires when a node is clicked
31047         * @param {Node} node The node
31048         * @param {Roo.EventObject} e The event object
31049         */
31050         "click":true,
31051         /**
31052         * @event dblclick
31053         * Fires when a node is double clicked
31054         * @param {Node} node The node
31055         * @param {Roo.EventObject} e The event object
31056         */
31057         "dblclick":true,
31058         /**
31059         * @event contextmenu
31060         * Fires when a node is right clicked
31061         * @param {Node} node The node
31062         * @param {Roo.EventObject} e The event object
31063         */
31064         "contextmenu":true,
31065         /**
31066         * @event beforechildrenrendered
31067         * Fires right before the child nodes for a node are rendered
31068         * @param {Node} node The node
31069         */
31070         "beforechildrenrendered":true,
31071         /**
31072         * @event startdrag
31073         * Fires when a node starts being dragged
31074         * @param {Roo.tree.TreePanel} this
31075         * @param {Roo.tree.TreeNode} node
31076         * @param {event} e The raw browser event
31077         */ 
31078        "startdrag" : true,
31079        /**
31080         * @event enddrag
31081         * Fires when a drag operation is complete
31082         * @param {Roo.tree.TreePanel} this
31083         * @param {Roo.tree.TreeNode} node
31084         * @param {event} e The raw browser event
31085         */
31086        "enddrag" : true,
31087        /**
31088         * @event dragdrop
31089         * Fires when a dragged node is dropped on a valid DD target
31090         * @param {Roo.tree.TreePanel} this
31091         * @param {Roo.tree.TreeNode} node
31092         * @param {DD} dd The dd it was dropped on
31093         * @param {event} e The raw browser event
31094         */
31095        "dragdrop" : true,
31096        /**
31097         * @event beforenodedrop
31098         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31099         * passed to handlers has the following properties:<br />
31100         * <ul style="padding:5px;padding-left:16px;">
31101         * <li>tree - The TreePanel</li>
31102         * <li>target - The node being targeted for the drop</li>
31103         * <li>data - The drag data from the drag source</li>
31104         * <li>point - The point of the drop - append, above or below</li>
31105         * <li>source - The drag source</li>
31106         * <li>rawEvent - Raw mouse event</li>
31107         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31108         * to be inserted by setting them on this object.</li>
31109         * <li>cancel - Set this to true to cancel the drop.</li>
31110         * </ul>
31111         * @param {Object} dropEvent
31112         */
31113        "beforenodedrop" : true,
31114        /**
31115         * @event nodedrop
31116         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31117         * passed to handlers has the following properties:<br />
31118         * <ul style="padding:5px;padding-left:16px;">
31119         * <li>tree - The TreePanel</li>
31120         * <li>target - The node being targeted for the drop</li>
31121         * <li>data - The drag data from the drag source</li>
31122         * <li>point - The point of the drop - append, above or below</li>
31123         * <li>source - The drag source</li>
31124         * <li>rawEvent - Raw mouse event</li>
31125         * <li>dropNode - Dropped node(s).</li>
31126         * </ul>
31127         * @param {Object} dropEvent
31128         */
31129        "nodedrop" : true,
31130         /**
31131         * @event nodedragover
31132         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31133         * passed to handlers has the following properties:<br />
31134         * <ul style="padding:5px;padding-left:16px;">
31135         * <li>tree - The TreePanel</li>
31136         * <li>target - The node being targeted for the drop</li>
31137         * <li>data - The drag data from the drag source</li>
31138         * <li>point - The point of the drop - append, above or below</li>
31139         * <li>source - The drag source</li>
31140         * <li>rawEvent - Raw mouse event</li>
31141         * <li>dropNode - Drop node(s) provided by the source.</li>
31142         * <li>cancel - Set this to true to signal drop not allowed.</li>
31143         * </ul>
31144         * @param {Object} dragOverEvent
31145         */
31146        "nodedragover" : true
31147         
31148     });
31149     if(this.singleExpand){
31150        this.on("beforeexpand", this.restrictExpand, this);
31151     }
31152     if (this.editor) {
31153         this.editor.tree = this;
31154         this.editor = Roo.factory(this.editor, Roo.tree);
31155     }
31156     
31157     if (this.selModel) {
31158         this.selModel = Roo.factory(this.selModel, Roo.tree);
31159     }
31160    
31161 };
31162 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31163     rootVisible : true,
31164     animate: Roo.enableFx,
31165     lines : true,
31166     enableDD : false,
31167     hlDrop : Roo.enableFx,
31168   
31169     renderer: false,
31170     
31171     rendererTip: false,
31172     // private
31173     restrictExpand : function(node){
31174         var p = node.parentNode;
31175         if(p){
31176             if(p.expandedChild && p.expandedChild.parentNode == p){
31177                 p.expandedChild.collapse();
31178             }
31179             p.expandedChild = node;
31180         }
31181     },
31182
31183     // private override
31184     setRootNode : function(node){
31185         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31186         if(!this.rootVisible){
31187             node.ui = new Roo.tree.RootTreeNodeUI(node);
31188         }
31189         return node;
31190     },
31191
31192     /**
31193      * Returns the container element for this TreePanel
31194      */
31195     getEl : function(){
31196         return this.el;
31197     },
31198
31199     /**
31200      * Returns the default TreeLoader for this TreePanel
31201      */
31202     getLoader : function(){
31203         return this.loader;
31204     },
31205
31206     /**
31207      * Expand all nodes
31208      */
31209     expandAll : function(){
31210         this.root.expand(true);
31211     },
31212
31213     /**
31214      * Collapse all nodes
31215      */
31216     collapseAll : function(){
31217         this.root.collapse(true);
31218     },
31219
31220     /**
31221      * Returns the selection model used by this TreePanel
31222      */
31223     getSelectionModel : function(){
31224         if(!this.selModel){
31225             this.selModel = new Roo.tree.DefaultSelectionModel();
31226         }
31227         return this.selModel;
31228     },
31229
31230     /**
31231      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31232      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31233      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31234      * @return {Array}
31235      */
31236     getChecked : function(a, startNode){
31237         startNode = startNode || this.root;
31238         var r = [];
31239         var f = function(){
31240             if(this.attributes.checked){
31241                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31242             }
31243         }
31244         startNode.cascade(f);
31245         return r;
31246     },
31247
31248     /**
31249      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31250      * @param {String} path
31251      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31252      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31253      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31254      */
31255     expandPath : function(path, attr, callback){
31256         attr = attr || "id";
31257         var keys = path.split(this.pathSeparator);
31258         var curNode = this.root;
31259         if(curNode.attributes[attr] != keys[1]){ // invalid root
31260             if(callback){
31261                 callback(false, null);
31262             }
31263             return;
31264         }
31265         var index = 1;
31266         var f = function(){
31267             if(++index == keys.length){
31268                 if(callback){
31269                     callback(true, curNode);
31270                 }
31271                 return;
31272             }
31273             var c = curNode.findChild(attr, keys[index]);
31274             if(!c){
31275                 if(callback){
31276                     callback(false, curNode);
31277                 }
31278                 return;
31279             }
31280             curNode = c;
31281             c.expand(false, false, f);
31282         };
31283         curNode.expand(false, false, f);
31284     },
31285
31286     /**
31287      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31288      * @param {String} path
31289      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31290      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31291      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31292      */
31293     selectPath : function(path, attr, callback){
31294         attr = attr || "id";
31295         var keys = path.split(this.pathSeparator);
31296         var v = keys.pop();
31297         if(keys.length > 0){
31298             var f = function(success, node){
31299                 if(success && node){
31300                     var n = node.findChild(attr, v);
31301                     if(n){
31302                         n.select();
31303                         if(callback){
31304                             callback(true, n);
31305                         }
31306                     }else if(callback){
31307                         callback(false, n);
31308                     }
31309                 }else{
31310                     if(callback){
31311                         callback(false, n);
31312                     }
31313                 }
31314             };
31315             this.expandPath(keys.join(this.pathSeparator), attr, f);
31316         }else{
31317             this.root.select();
31318             if(callback){
31319                 callback(true, this.root);
31320             }
31321         }
31322     },
31323
31324     getTreeEl : function(){
31325         return this.el;
31326     },
31327
31328     /**
31329      * Trigger rendering of this TreePanel
31330      */
31331     render : function(){
31332         if (this.innerCt) {
31333             return this; // stop it rendering more than once!!
31334         }
31335         
31336         this.innerCt = this.el.createChild({tag:"ul",
31337                cls:"x-tree-root-ct " +
31338                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31339
31340         if(this.containerScroll){
31341             Roo.dd.ScrollManager.register(this.el);
31342         }
31343         if((this.enableDD || this.enableDrop) && !this.dropZone){
31344            /**
31345             * The dropZone used by this tree if drop is enabled
31346             * @type Roo.tree.TreeDropZone
31347             */
31348              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31349                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31350            });
31351         }
31352         if((this.enableDD || this.enableDrag) && !this.dragZone){
31353            /**
31354             * The dragZone used by this tree if drag is enabled
31355             * @type Roo.tree.TreeDragZone
31356             */
31357             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31358                ddGroup: this.ddGroup || "TreeDD",
31359                scroll: this.ddScroll
31360            });
31361         }
31362         this.getSelectionModel().init(this);
31363         if (!this.root) {
31364             console.log("ROOT not set in tree");
31365             return;
31366         }
31367         this.root.render();
31368         if(!this.rootVisible){
31369             this.root.renderChildren();
31370         }
31371         return this;
31372     }
31373 });/*
31374  * Based on:
31375  * Ext JS Library 1.1.1
31376  * Copyright(c) 2006-2007, Ext JS, LLC.
31377  *
31378  * Originally Released Under LGPL - original licence link has changed is not relivant.
31379  *
31380  * Fork - LGPL
31381  * <script type="text/javascript">
31382  */
31383  
31384
31385 /**
31386  * @class Roo.tree.DefaultSelectionModel
31387  * @extends Roo.util.Observable
31388  * The default single selection for a TreePanel.
31389  * @param {Object} cfg Configuration
31390  */
31391 Roo.tree.DefaultSelectionModel = function(cfg){
31392    this.selNode = null;
31393    
31394    
31395    
31396    this.addEvents({
31397        /**
31398         * @event selectionchange
31399         * Fires when the selected node changes
31400         * @param {DefaultSelectionModel} this
31401         * @param {TreeNode} node the new selection
31402         */
31403        "selectionchange" : true,
31404
31405        /**
31406         * @event beforeselect
31407         * Fires before the selected node changes, return false to cancel the change
31408         * @param {DefaultSelectionModel} this
31409         * @param {TreeNode} node the new selection
31410         * @param {TreeNode} node the old selection
31411         */
31412        "beforeselect" : true
31413    });
31414    
31415     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31416 };
31417
31418 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31419     init : function(tree){
31420         this.tree = tree;
31421         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31422         tree.on("click", this.onNodeClick, this);
31423     },
31424     
31425     onNodeClick : function(node, e){
31426         if (e.ctrlKey && this.selNode == node)  {
31427             this.unselect(node);
31428             return;
31429         }
31430         this.select(node);
31431     },
31432     
31433     /**
31434      * Select a node.
31435      * @param {TreeNode} node The node to select
31436      * @return {TreeNode} The selected node
31437      */
31438     select : function(node){
31439         var last = this.selNode;
31440         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31441             if(last){
31442                 last.ui.onSelectedChange(false);
31443             }
31444             this.selNode = node;
31445             node.ui.onSelectedChange(true);
31446             this.fireEvent("selectionchange", this, node, last);
31447         }
31448         return node;
31449     },
31450     
31451     /**
31452      * Deselect a node.
31453      * @param {TreeNode} node The node to unselect
31454      */
31455     unselect : function(node){
31456         if(this.selNode == node){
31457             this.clearSelections();
31458         }    
31459     },
31460     
31461     /**
31462      * Clear all selections
31463      */
31464     clearSelections : function(){
31465         var n = this.selNode;
31466         if(n){
31467             n.ui.onSelectedChange(false);
31468             this.selNode = null;
31469             this.fireEvent("selectionchange", this, null);
31470         }
31471         return n;
31472     },
31473     
31474     /**
31475      * Get the selected node
31476      * @return {TreeNode} The selected node
31477      */
31478     getSelectedNode : function(){
31479         return this.selNode;    
31480     },
31481     
31482     /**
31483      * Returns true if the node is selected
31484      * @param {TreeNode} node The node to check
31485      * @return {Boolean}
31486      */
31487     isSelected : function(node){
31488         return this.selNode == node;  
31489     },
31490
31491     /**
31492      * Selects the node above the selected node in the tree, intelligently walking the nodes
31493      * @return TreeNode The new selection
31494      */
31495     selectPrevious : function(){
31496         var s = this.selNode || this.lastSelNode;
31497         if(!s){
31498             return null;
31499         }
31500         var ps = s.previousSibling;
31501         if(ps){
31502             if(!ps.isExpanded() || ps.childNodes.length < 1){
31503                 return this.select(ps);
31504             } else{
31505                 var lc = ps.lastChild;
31506                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31507                     lc = lc.lastChild;
31508                 }
31509                 return this.select(lc);
31510             }
31511         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31512             return this.select(s.parentNode);
31513         }
31514         return null;
31515     },
31516
31517     /**
31518      * Selects the node above the selected node in the tree, intelligently walking the nodes
31519      * @return TreeNode The new selection
31520      */
31521     selectNext : function(){
31522         var s = this.selNode || this.lastSelNode;
31523         if(!s){
31524             return null;
31525         }
31526         if(s.firstChild && s.isExpanded()){
31527              return this.select(s.firstChild);
31528          }else if(s.nextSibling){
31529              return this.select(s.nextSibling);
31530          }else if(s.parentNode){
31531             var newS = null;
31532             s.parentNode.bubble(function(){
31533                 if(this.nextSibling){
31534                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31535                     return false;
31536                 }
31537             });
31538             return newS;
31539          }
31540         return null;
31541     },
31542
31543     onKeyDown : function(e){
31544         var s = this.selNode || this.lastSelNode;
31545         // undesirable, but required
31546         var sm = this;
31547         if(!s){
31548             return;
31549         }
31550         var k = e.getKey();
31551         switch(k){
31552              case e.DOWN:
31553                  e.stopEvent();
31554                  this.selectNext();
31555              break;
31556              case e.UP:
31557                  e.stopEvent();
31558                  this.selectPrevious();
31559              break;
31560              case e.RIGHT:
31561                  e.preventDefault();
31562                  if(s.hasChildNodes()){
31563                      if(!s.isExpanded()){
31564                          s.expand();
31565                      }else if(s.firstChild){
31566                          this.select(s.firstChild, e);
31567                      }
31568                  }
31569              break;
31570              case e.LEFT:
31571                  e.preventDefault();
31572                  if(s.hasChildNodes() && s.isExpanded()){
31573                      s.collapse();
31574                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31575                      this.select(s.parentNode, e);
31576                  }
31577              break;
31578         };
31579     }
31580 });
31581
31582 /**
31583  * @class Roo.tree.MultiSelectionModel
31584  * @extends Roo.util.Observable
31585  * Multi selection for a TreePanel.
31586  * @param {Object} cfg Configuration
31587  */
31588 Roo.tree.MultiSelectionModel = function(){
31589    this.selNodes = [];
31590    this.selMap = {};
31591    this.addEvents({
31592        /**
31593         * @event selectionchange
31594         * Fires when the selected nodes change
31595         * @param {MultiSelectionModel} this
31596         * @param {Array} nodes Array of the selected nodes
31597         */
31598        "selectionchange" : true
31599    });
31600    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31601    
31602 };
31603
31604 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31605     init : function(tree){
31606         this.tree = tree;
31607         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31608         tree.on("click", this.onNodeClick, this);
31609     },
31610     
31611     onNodeClick : function(node, e){
31612         this.select(node, e, e.ctrlKey);
31613     },
31614     
31615     /**
31616      * Select a node.
31617      * @param {TreeNode} node The node to select
31618      * @param {EventObject} e (optional) An event associated with the selection
31619      * @param {Boolean} keepExisting True to retain existing selections
31620      * @return {TreeNode} The selected node
31621      */
31622     select : function(node, e, keepExisting){
31623         if(keepExisting !== true){
31624             this.clearSelections(true);
31625         }
31626         if(this.isSelected(node)){
31627             this.lastSelNode = node;
31628             return node;
31629         }
31630         this.selNodes.push(node);
31631         this.selMap[node.id] = node;
31632         this.lastSelNode = node;
31633         node.ui.onSelectedChange(true);
31634         this.fireEvent("selectionchange", this, this.selNodes);
31635         return node;
31636     },
31637     
31638     /**
31639      * Deselect a node.
31640      * @param {TreeNode} node The node to unselect
31641      */
31642     unselect : function(node){
31643         if(this.selMap[node.id]){
31644             node.ui.onSelectedChange(false);
31645             var sn = this.selNodes;
31646             var index = -1;
31647             if(sn.indexOf){
31648                 index = sn.indexOf(node);
31649             }else{
31650                 for(var i = 0, len = sn.length; i < len; i++){
31651                     if(sn[i] == node){
31652                         index = i;
31653                         break;
31654                     }
31655                 }
31656             }
31657             if(index != -1){
31658                 this.selNodes.splice(index, 1);
31659             }
31660             delete this.selMap[node.id];
31661             this.fireEvent("selectionchange", this, this.selNodes);
31662         }
31663     },
31664     
31665     /**
31666      * Clear all selections
31667      */
31668     clearSelections : function(suppressEvent){
31669         var sn = this.selNodes;
31670         if(sn.length > 0){
31671             for(var i = 0, len = sn.length; i < len; i++){
31672                 sn[i].ui.onSelectedChange(false);
31673             }
31674             this.selNodes = [];
31675             this.selMap = {};
31676             if(suppressEvent !== true){
31677                 this.fireEvent("selectionchange", this, this.selNodes);
31678             }
31679         }
31680     },
31681     
31682     /**
31683      * Returns true if the node is selected
31684      * @param {TreeNode} node The node to check
31685      * @return {Boolean}
31686      */
31687     isSelected : function(node){
31688         return this.selMap[node.id] ? true : false;  
31689     },
31690     
31691     /**
31692      * Returns an array of the selected nodes
31693      * @return {Array}
31694      */
31695     getSelectedNodes : function(){
31696         return this.selNodes;    
31697     },
31698
31699     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31700
31701     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31702
31703     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31704 });/*
31705  * Based on:
31706  * Ext JS Library 1.1.1
31707  * Copyright(c) 2006-2007, Ext JS, LLC.
31708  *
31709  * Originally Released Under LGPL - original licence link has changed is not relivant.
31710  *
31711  * Fork - LGPL
31712  * <script type="text/javascript">
31713  */
31714  
31715 /**
31716  * @class Roo.tree.TreeNode
31717  * @extends Roo.data.Node
31718  * @cfg {String} text The text for this node
31719  * @cfg {Boolean} expanded true to start the node expanded
31720  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31721  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31722  * @cfg {Boolean} disabled true to start the node disabled
31723  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31724  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31725  * @cfg {String} cls A css class to be added to the node
31726  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31727  * @cfg {String} href URL of the link used for the node (defaults to #)
31728  * @cfg {String} hrefTarget target frame for the link
31729  * @cfg {String} qtip An Ext QuickTip for the node
31730  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31731  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31732  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31733  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31734  * (defaults to undefined with no checkbox rendered)
31735  * @constructor
31736  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31737  */
31738 Roo.tree.TreeNode = function(attributes){
31739     attributes = attributes || {};
31740     if(typeof attributes == "string"){
31741         attributes = {text: attributes};
31742     }
31743     this.childrenRendered = false;
31744     this.rendered = false;
31745     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31746     this.expanded = attributes.expanded === true;
31747     this.isTarget = attributes.isTarget !== false;
31748     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31749     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31750
31751     /**
31752      * Read-only. The text for this node. To change it use setText().
31753      * @type String
31754      */
31755     this.text = attributes.text;
31756     /**
31757      * True if this node is disabled.
31758      * @type Boolean
31759      */
31760     this.disabled = attributes.disabled === true;
31761
31762     this.addEvents({
31763         /**
31764         * @event textchange
31765         * Fires when the text for this node is changed
31766         * @param {Node} this This node
31767         * @param {String} text The new text
31768         * @param {String} oldText The old text
31769         */
31770         "textchange" : true,
31771         /**
31772         * @event beforeexpand
31773         * Fires before this node is expanded, return false to cancel.
31774         * @param {Node} this This node
31775         * @param {Boolean} deep
31776         * @param {Boolean} anim
31777         */
31778         "beforeexpand" : true,
31779         /**
31780         * @event beforecollapse
31781         * Fires before this node is collapsed, return false to cancel.
31782         * @param {Node} this This node
31783         * @param {Boolean} deep
31784         * @param {Boolean} anim
31785         */
31786         "beforecollapse" : true,
31787         /**
31788         * @event expand
31789         * Fires when this node is expanded
31790         * @param {Node} this This node
31791         */
31792         "expand" : true,
31793         /**
31794         * @event disabledchange
31795         * Fires when the disabled status of this node changes
31796         * @param {Node} this This node
31797         * @param {Boolean} disabled
31798         */
31799         "disabledchange" : true,
31800         /**
31801         * @event collapse
31802         * Fires when this node is collapsed
31803         * @param {Node} this This node
31804         */
31805         "collapse" : true,
31806         /**
31807         * @event beforeclick
31808         * Fires before click processing. Return false to cancel the default action.
31809         * @param {Node} this This node
31810         * @param {Roo.EventObject} e The event object
31811         */
31812         "beforeclick":true,
31813         /**
31814         * @event checkchange
31815         * Fires when a node with a checkbox's checked property changes
31816         * @param {Node} this This node
31817         * @param {Boolean} checked
31818         */
31819         "checkchange":true,
31820         /**
31821         * @event click
31822         * Fires when this node is clicked
31823         * @param {Node} this This node
31824         * @param {Roo.EventObject} e The event object
31825         */
31826         "click":true,
31827         /**
31828         * @event dblclick
31829         * Fires when this node is double clicked
31830         * @param {Node} this This node
31831         * @param {Roo.EventObject} e The event object
31832         */
31833         "dblclick":true,
31834         /**
31835         * @event contextmenu
31836         * Fires when this node is right clicked
31837         * @param {Node} this This node
31838         * @param {Roo.EventObject} e The event object
31839         */
31840         "contextmenu":true,
31841         /**
31842         * @event beforechildrenrendered
31843         * Fires right before the child nodes for this node are rendered
31844         * @param {Node} this This node
31845         */
31846         "beforechildrenrendered":true
31847     });
31848
31849     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31850
31851     /**
31852      * Read-only. The UI for this node
31853      * @type TreeNodeUI
31854      */
31855     this.ui = new uiClass(this);
31856 };
31857 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31858     preventHScroll: true,
31859     /**
31860      * Returns true if this node is expanded
31861      * @return {Boolean}
31862      */
31863     isExpanded : function(){
31864         return this.expanded;
31865     },
31866
31867     /**
31868      * Returns the UI object for this node
31869      * @return {TreeNodeUI}
31870      */
31871     getUI : function(){
31872         return this.ui;
31873     },
31874
31875     // private override
31876     setFirstChild : function(node){
31877         var of = this.firstChild;
31878         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31879         if(this.childrenRendered && of && node != of){
31880             of.renderIndent(true, true);
31881         }
31882         if(this.rendered){
31883             this.renderIndent(true, true);
31884         }
31885     },
31886
31887     // private override
31888     setLastChild : function(node){
31889         var ol = this.lastChild;
31890         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31891         if(this.childrenRendered && ol && node != ol){
31892             ol.renderIndent(true, true);
31893         }
31894         if(this.rendered){
31895             this.renderIndent(true, true);
31896         }
31897     },
31898
31899     // these methods are overridden to provide lazy rendering support
31900     // private override
31901     appendChild : function(){
31902         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31903         if(node && this.childrenRendered){
31904             node.render();
31905         }
31906         this.ui.updateExpandIcon();
31907         return node;
31908     },
31909
31910     // private override
31911     removeChild : function(node){
31912         this.ownerTree.getSelectionModel().unselect(node);
31913         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31914         // if it's been rendered remove dom node
31915         if(this.childrenRendered){
31916             node.ui.remove();
31917         }
31918         if(this.childNodes.length < 1){
31919             this.collapse(false, false);
31920         }else{
31921             this.ui.updateExpandIcon();
31922         }
31923         if(!this.firstChild) {
31924             this.childrenRendered = false;
31925         }
31926         return node;
31927     },
31928
31929     // private override
31930     insertBefore : function(node, refNode){
31931         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31932         if(newNode && refNode && this.childrenRendered){
31933             node.render();
31934         }
31935         this.ui.updateExpandIcon();
31936         return newNode;
31937     },
31938
31939     /**
31940      * Sets the text for this node
31941      * @param {String} text
31942      */
31943     setText : function(text){
31944         var oldText = this.text;
31945         this.text = text;
31946         this.attributes.text = text;
31947         if(this.rendered){ // event without subscribing
31948             this.ui.onTextChange(this, text, oldText);
31949         }
31950         this.fireEvent("textchange", this, text, oldText);
31951     },
31952
31953     /**
31954      * Triggers selection of this node
31955      */
31956     select : function(){
31957         this.getOwnerTree().getSelectionModel().select(this);
31958     },
31959
31960     /**
31961      * Triggers deselection of this node
31962      */
31963     unselect : function(){
31964         this.getOwnerTree().getSelectionModel().unselect(this);
31965     },
31966
31967     /**
31968      * Returns true if this node is selected
31969      * @return {Boolean}
31970      */
31971     isSelected : function(){
31972         return this.getOwnerTree().getSelectionModel().isSelected(this);
31973     },
31974
31975     /**
31976      * Expand this node.
31977      * @param {Boolean} deep (optional) True to expand all children as well
31978      * @param {Boolean} anim (optional) false to cancel the default animation
31979      * @param {Function} callback (optional) A callback to be called when
31980      * expanding this node completes (does not wait for deep expand to complete).
31981      * Called with 1 parameter, this node.
31982      */
31983     expand : function(deep, anim, callback){
31984         if(!this.expanded){
31985             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31986                 return;
31987             }
31988             if(!this.childrenRendered){
31989                 this.renderChildren();
31990             }
31991             this.expanded = true;
31992             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31993                 this.ui.animExpand(function(){
31994                     this.fireEvent("expand", this);
31995                     if(typeof callback == "function"){
31996                         callback(this);
31997                     }
31998                     if(deep === true){
31999                         this.expandChildNodes(true);
32000                     }
32001                 }.createDelegate(this));
32002                 return;
32003             }else{
32004                 this.ui.expand();
32005                 this.fireEvent("expand", this);
32006                 if(typeof callback == "function"){
32007                     callback(this);
32008                 }
32009             }
32010         }else{
32011            if(typeof callback == "function"){
32012                callback(this);
32013            }
32014         }
32015         if(deep === true){
32016             this.expandChildNodes(true);
32017         }
32018     },
32019
32020     isHiddenRoot : function(){
32021         return this.isRoot && !this.getOwnerTree().rootVisible;
32022     },
32023
32024     /**
32025      * Collapse this node.
32026      * @param {Boolean} deep (optional) True to collapse all children as well
32027      * @param {Boolean} anim (optional) false to cancel the default animation
32028      */
32029     collapse : function(deep, anim){
32030         if(this.expanded && !this.isHiddenRoot()){
32031             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32032                 return;
32033             }
32034             this.expanded = false;
32035             if((this.getOwnerTree().animate && anim !== false) || anim){
32036                 this.ui.animCollapse(function(){
32037                     this.fireEvent("collapse", this);
32038                     if(deep === true){
32039                         this.collapseChildNodes(true);
32040                     }
32041                 }.createDelegate(this));
32042                 return;
32043             }else{
32044                 this.ui.collapse();
32045                 this.fireEvent("collapse", this);
32046             }
32047         }
32048         if(deep === true){
32049             var cs = this.childNodes;
32050             for(var i = 0, len = cs.length; i < len; i++) {
32051                 cs[i].collapse(true, false);
32052             }
32053         }
32054     },
32055
32056     // private
32057     delayedExpand : function(delay){
32058         if(!this.expandProcId){
32059             this.expandProcId = this.expand.defer(delay, this);
32060         }
32061     },
32062
32063     // private
32064     cancelExpand : function(){
32065         if(this.expandProcId){
32066             clearTimeout(this.expandProcId);
32067         }
32068         this.expandProcId = false;
32069     },
32070
32071     /**
32072      * Toggles expanded/collapsed state of the node
32073      */
32074     toggle : function(){
32075         if(this.expanded){
32076             this.collapse();
32077         }else{
32078             this.expand();
32079         }
32080     },
32081
32082     /**
32083      * Ensures all parent nodes are expanded
32084      */
32085     ensureVisible : function(callback){
32086         var tree = this.getOwnerTree();
32087         tree.expandPath(this.parentNode.getPath(), false, function(){
32088             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32089             Roo.callback(callback);
32090         }.createDelegate(this));
32091     },
32092
32093     /**
32094      * Expand all child nodes
32095      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32096      */
32097     expandChildNodes : function(deep){
32098         var cs = this.childNodes;
32099         for(var i = 0, len = cs.length; i < len; i++) {
32100                 cs[i].expand(deep);
32101         }
32102     },
32103
32104     /**
32105      * Collapse all child nodes
32106      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32107      */
32108     collapseChildNodes : function(deep){
32109         var cs = this.childNodes;
32110         for(var i = 0, len = cs.length; i < len; i++) {
32111                 cs[i].collapse(deep);
32112         }
32113     },
32114
32115     /**
32116      * Disables this node
32117      */
32118     disable : function(){
32119         this.disabled = true;
32120         this.unselect();
32121         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32122             this.ui.onDisableChange(this, true);
32123         }
32124         this.fireEvent("disabledchange", this, true);
32125     },
32126
32127     /**
32128      * Enables this node
32129      */
32130     enable : function(){
32131         this.disabled = false;
32132         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32133             this.ui.onDisableChange(this, false);
32134         }
32135         this.fireEvent("disabledchange", this, false);
32136     },
32137
32138     // private
32139     renderChildren : function(suppressEvent){
32140         if(suppressEvent !== false){
32141             this.fireEvent("beforechildrenrendered", this);
32142         }
32143         var cs = this.childNodes;
32144         for(var i = 0, len = cs.length; i < len; i++){
32145             cs[i].render(true);
32146         }
32147         this.childrenRendered = true;
32148     },
32149
32150     // private
32151     sort : function(fn, scope){
32152         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32153         if(this.childrenRendered){
32154             var cs = this.childNodes;
32155             for(var i = 0, len = cs.length; i < len; i++){
32156                 cs[i].render(true);
32157             }
32158         }
32159     },
32160
32161     // private
32162     render : function(bulkRender){
32163         this.ui.render(bulkRender);
32164         if(!this.rendered){
32165             this.rendered = true;
32166             if(this.expanded){
32167                 this.expanded = false;
32168                 this.expand(false, false);
32169             }
32170         }
32171     },
32172
32173     // private
32174     renderIndent : function(deep, refresh){
32175         if(refresh){
32176             this.ui.childIndent = null;
32177         }
32178         this.ui.renderIndent();
32179         if(deep === true && this.childrenRendered){
32180             var cs = this.childNodes;
32181             for(var i = 0, len = cs.length; i < len; i++){
32182                 cs[i].renderIndent(true, refresh);
32183             }
32184         }
32185     }
32186 });/*
32187  * Based on:
32188  * Ext JS Library 1.1.1
32189  * Copyright(c) 2006-2007, Ext JS, LLC.
32190  *
32191  * Originally Released Under LGPL - original licence link has changed is not relivant.
32192  *
32193  * Fork - LGPL
32194  * <script type="text/javascript">
32195  */
32196  
32197 /**
32198  * @class Roo.tree.AsyncTreeNode
32199  * @extends Roo.tree.TreeNode
32200  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32201  * @constructor
32202  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32203  */
32204  Roo.tree.AsyncTreeNode = function(config){
32205     this.loaded = false;
32206     this.loading = false;
32207     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32208     /**
32209     * @event beforeload
32210     * Fires before this node is loaded, return false to cancel
32211     * @param {Node} this This node
32212     */
32213     this.addEvents({'beforeload':true, 'load': true});
32214     /**
32215     * @event load
32216     * Fires when this node is loaded
32217     * @param {Node} this This node
32218     */
32219     /**
32220      * The loader used by this node (defaults to using the tree's defined loader)
32221      * @type TreeLoader
32222      * @property loader
32223      */
32224 };
32225 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32226     expand : function(deep, anim, callback){
32227         if(this.loading){ // if an async load is already running, waiting til it's done
32228             var timer;
32229             var f = function(){
32230                 if(!this.loading){ // done loading
32231                     clearInterval(timer);
32232                     this.expand(deep, anim, callback);
32233                 }
32234             }.createDelegate(this);
32235             timer = setInterval(f, 200);
32236             return;
32237         }
32238         if(!this.loaded){
32239             if(this.fireEvent("beforeload", this) === false){
32240                 return;
32241             }
32242             this.loading = true;
32243             this.ui.beforeLoad(this);
32244             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32245             if(loader){
32246                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32247                 return;
32248             }
32249         }
32250         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32251     },
32252     
32253     /**
32254      * Returns true if this node is currently loading
32255      * @return {Boolean}
32256      */
32257     isLoading : function(){
32258         return this.loading;  
32259     },
32260     
32261     loadComplete : function(deep, anim, callback){
32262         this.loading = false;
32263         this.loaded = true;
32264         this.ui.afterLoad(this);
32265         this.fireEvent("load", this);
32266         this.expand(deep, anim, callback);
32267     },
32268     
32269     /**
32270      * Returns true if this node has been loaded
32271      * @return {Boolean}
32272      */
32273     isLoaded : function(){
32274         return this.loaded;
32275     },
32276     
32277     hasChildNodes : function(){
32278         if(!this.isLeaf() && !this.loaded){
32279             return true;
32280         }else{
32281             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32282         }
32283     },
32284
32285     /**
32286      * Trigger a reload for this node
32287      * @param {Function} callback
32288      */
32289     reload : function(callback){
32290         this.collapse(false, false);
32291         while(this.firstChild){
32292             this.removeChild(this.firstChild);
32293         }
32294         this.childrenRendered = false;
32295         this.loaded = false;
32296         if(this.isHiddenRoot()){
32297             this.expanded = false;
32298         }
32299         this.expand(false, false, callback);
32300     }
32301 });/*
32302  * Based on:
32303  * Ext JS Library 1.1.1
32304  * Copyright(c) 2006-2007, Ext JS, LLC.
32305  *
32306  * Originally Released Under LGPL - original licence link has changed is not relivant.
32307  *
32308  * Fork - LGPL
32309  * <script type="text/javascript">
32310  */
32311  
32312 /**
32313  * @class Roo.tree.TreeNodeUI
32314  * @constructor
32315  * @param {Object} node The node to render
32316  * The TreeNode UI implementation is separate from the
32317  * tree implementation. Unless you are customizing the tree UI,
32318  * you should never have to use this directly.
32319  */
32320 Roo.tree.TreeNodeUI = function(node){
32321     this.node = node;
32322     this.rendered = false;
32323     this.animating = false;
32324     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32325 };
32326
32327 Roo.tree.TreeNodeUI.prototype = {
32328     removeChild : function(node){
32329         if(this.rendered){
32330             this.ctNode.removeChild(node.ui.getEl());
32331         }
32332     },
32333
32334     beforeLoad : function(){
32335          this.addClass("x-tree-node-loading");
32336     },
32337
32338     afterLoad : function(){
32339          this.removeClass("x-tree-node-loading");
32340     },
32341
32342     onTextChange : function(node, text, oldText){
32343         if(this.rendered){
32344             this.textNode.innerHTML = text;
32345         }
32346     },
32347
32348     onDisableChange : function(node, state){
32349         this.disabled = state;
32350         if(state){
32351             this.addClass("x-tree-node-disabled");
32352         }else{
32353             this.removeClass("x-tree-node-disabled");
32354         }
32355     },
32356
32357     onSelectedChange : function(state){
32358         if(state){
32359             this.focus();
32360             this.addClass("x-tree-selected");
32361         }else{
32362             //this.blur();
32363             this.removeClass("x-tree-selected");
32364         }
32365     },
32366
32367     onMove : function(tree, node, oldParent, newParent, index, refNode){
32368         this.childIndent = null;
32369         if(this.rendered){
32370             var targetNode = newParent.ui.getContainer();
32371             if(!targetNode){//target not rendered
32372                 this.holder = document.createElement("div");
32373                 this.holder.appendChild(this.wrap);
32374                 return;
32375             }
32376             var insertBefore = refNode ? refNode.ui.getEl() : null;
32377             if(insertBefore){
32378                 targetNode.insertBefore(this.wrap, insertBefore);
32379             }else{
32380                 targetNode.appendChild(this.wrap);
32381             }
32382             this.node.renderIndent(true);
32383         }
32384     },
32385
32386     addClass : function(cls){
32387         if(this.elNode){
32388             Roo.fly(this.elNode).addClass(cls);
32389         }
32390     },
32391
32392     removeClass : function(cls){
32393         if(this.elNode){
32394             Roo.fly(this.elNode).removeClass(cls);
32395         }
32396     },
32397
32398     remove : function(){
32399         if(this.rendered){
32400             this.holder = document.createElement("div");
32401             this.holder.appendChild(this.wrap);
32402         }
32403     },
32404
32405     fireEvent : function(){
32406         return this.node.fireEvent.apply(this.node, arguments);
32407     },
32408
32409     initEvents : function(){
32410         this.node.on("move", this.onMove, this);
32411         var E = Roo.EventManager;
32412         var a = this.anchor;
32413
32414         var el = Roo.fly(a, '_treeui');
32415
32416         if(Roo.isOpera){ // opera render bug ignores the CSS
32417             el.setStyle("text-decoration", "none");
32418         }
32419
32420         el.on("click", this.onClick, this);
32421         el.on("dblclick", this.onDblClick, this);
32422
32423         if(this.checkbox){
32424             Roo.EventManager.on(this.checkbox,
32425                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32426         }
32427
32428         el.on("contextmenu", this.onContextMenu, this);
32429
32430         var icon = Roo.fly(this.iconNode);
32431         icon.on("click", this.onClick, this);
32432         icon.on("dblclick", this.onDblClick, this);
32433         icon.on("contextmenu", this.onContextMenu, this);
32434         E.on(this.ecNode, "click", this.ecClick, this, true);
32435
32436         if(this.node.disabled){
32437             this.addClass("x-tree-node-disabled");
32438         }
32439         if(this.node.hidden){
32440             this.addClass("x-tree-node-disabled");
32441         }
32442         var ot = this.node.getOwnerTree();
32443         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32444         if(dd && (!this.node.isRoot || ot.rootVisible)){
32445             Roo.dd.Registry.register(this.elNode, {
32446                 node: this.node,
32447                 handles: this.getDDHandles(),
32448                 isHandle: false
32449             });
32450         }
32451     },
32452
32453     getDDHandles : function(){
32454         return [this.iconNode, this.textNode];
32455     },
32456
32457     hide : function(){
32458         if(this.rendered){
32459             this.wrap.style.display = "none";
32460         }
32461     },
32462
32463     show : function(){
32464         if(this.rendered){
32465             this.wrap.style.display = "";
32466         }
32467     },
32468
32469     onContextMenu : function(e){
32470         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32471             e.preventDefault();
32472             this.focus();
32473             this.fireEvent("contextmenu", this.node, e);
32474         }
32475     },
32476
32477     onClick : function(e){
32478         if(this.dropping){
32479             e.stopEvent();
32480             return;
32481         }
32482         if(this.fireEvent("beforeclick", this.node, e) !== false){
32483             if(!this.disabled && this.node.attributes.href){
32484                 this.fireEvent("click", this.node, e);
32485                 return;
32486             }
32487             e.preventDefault();
32488             if(this.disabled){
32489                 return;
32490             }
32491
32492             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32493                 this.node.toggle();
32494             }
32495
32496             this.fireEvent("click", this.node, e);
32497         }else{
32498             e.stopEvent();
32499         }
32500     },
32501
32502     onDblClick : function(e){
32503         e.preventDefault();
32504         if(this.disabled){
32505             return;
32506         }
32507         if(this.checkbox){
32508             this.toggleCheck();
32509         }
32510         if(!this.animating && this.node.hasChildNodes()){
32511             this.node.toggle();
32512         }
32513         this.fireEvent("dblclick", this.node, e);
32514     },
32515
32516     onCheckChange : function(){
32517         var checked = this.checkbox.checked;
32518         this.node.attributes.checked = checked;
32519         this.fireEvent('checkchange', this.node, checked);
32520     },
32521
32522     ecClick : function(e){
32523         if(!this.animating && this.node.hasChildNodes()){
32524             this.node.toggle();
32525         }
32526     },
32527
32528     startDrop : function(){
32529         this.dropping = true;
32530     },
32531
32532     // delayed drop so the click event doesn't get fired on a drop
32533     endDrop : function(){
32534        setTimeout(function(){
32535            this.dropping = false;
32536        }.createDelegate(this), 50);
32537     },
32538
32539     expand : function(){
32540         this.updateExpandIcon();
32541         this.ctNode.style.display = "";
32542     },
32543
32544     focus : function(){
32545         if(!this.node.preventHScroll){
32546             try{this.anchor.focus();
32547             }catch(e){}
32548         }else if(!Roo.isIE){
32549             try{
32550                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32551                 var l = noscroll.scrollLeft;
32552                 this.anchor.focus();
32553                 noscroll.scrollLeft = l;
32554             }catch(e){}
32555         }
32556     },
32557
32558     toggleCheck : function(value){
32559         var cb = this.checkbox;
32560         if(cb){
32561             cb.checked = (value === undefined ? !cb.checked : value);
32562         }
32563     },
32564
32565     blur : function(){
32566         try{
32567             this.anchor.blur();
32568         }catch(e){}
32569     },
32570
32571     animExpand : function(callback){
32572         var ct = Roo.get(this.ctNode);
32573         ct.stopFx();
32574         if(!this.node.hasChildNodes()){
32575             this.updateExpandIcon();
32576             this.ctNode.style.display = "";
32577             Roo.callback(callback);
32578             return;
32579         }
32580         this.animating = true;
32581         this.updateExpandIcon();
32582
32583         ct.slideIn('t', {
32584            callback : function(){
32585                this.animating = false;
32586                Roo.callback(callback);
32587             },
32588             scope: this,
32589             duration: this.node.ownerTree.duration || .25
32590         });
32591     },
32592
32593     highlight : function(){
32594         var tree = this.node.getOwnerTree();
32595         Roo.fly(this.wrap).highlight(
32596             tree.hlColor || "C3DAF9",
32597             {endColor: tree.hlBaseColor}
32598         );
32599     },
32600
32601     collapse : function(){
32602         this.updateExpandIcon();
32603         this.ctNode.style.display = "none";
32604     },
32605
32606     animCollapse : function(callback){
32607         var ct = Roo.get(this.ctNode);
32608         ct.enableDisplayMode('block');
32609         ct.stopFx();
32610
32611         this.animating = true;
32612         this.updateExpandIcon();
32613
32614         ct.slideOut('t', {
32615             callback : function(){
32616                this.animating = false;
32617                Roo.callback(callback);
32618             },
32619             scope: this,
32620             duration: this.node.ownerTree.duration || .25
32621         });
32622     },
32623
32624     getContainer : function(){
32625         return this.ctNode;
32626     },
32627
32628     getEl : function(){
32629         return this.wrap;
32630     },
32631
32632     appendDDGhost : function(ghostNode){
32633         ghostNode.appendChild(this.elNode.cloneNode(true));
32634     },
32635
32636     getDDRepairXY : function(){
32637         return Roo.lib.Dom.getXY(this.iconNode);
32638     },
32639
32640     onRender : function(){
32641         this.render();
32642     },
32643
32644     render : function(bulkRender){
32645         var n = this.node, a = n.attributes;
32646         var targetNode = n.parentNode ?
32647               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32648
32649         if(!this.rendered){
32650             this.rendered = true;
32651
32652             this.renderElements(n, a, targetNode, bulkRender);
32653
32654             if(a.qtip){
32655                if(this.textNode.setAttributeNS){
32656                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32657                    if(a.qtipTitle){
32658                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32659                    }
32660                }else{
32661                    this.textNode.setAttribute("ext:qtip", a.qtip);
32662                    if(a.qtipTitle){
32663                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32664                    }
32665                }
32666             }else if(a.qtipCfg){
32667                 a.qtipCfg.target = Roo.id(this.textNode);
32668                 Roo.QuickTips.register(a.qtipCfg);
32669             }
32670             this.initEvents();
32671             if(!this.node.expanded){
32672                 this.updateExpandIcon();
32673             }
32674         }else{
32675             if(bulkRender === true) {
32676                 targetNode.appendChild(this.wrap);
32677             }
32678         }
32679     },
32680
32681     renderElements : function(n, a, targetNode, bulkRender)
32682     {
32683         // add some indent caching, this helps performance when rendering a large tree
32684         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32685         var t = n.getOwnerTree();
32686         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32687         if (typeof(n.attributes.html) != 'undefined') {
32688             txt = n.attributes.html;
32689         }
32690         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32691         var cb = typeof a.checked == 'boolean';
32692         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32693         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32694             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32695             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32696             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32697             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32698             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32699              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32700                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32701             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32702             "</li>"];
32703
32704         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32705             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32706                                 n.nextSibling.ui.getEl(), buf.join(""));
32707         }else{
32708             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32709         }
32710
32711         this.elNode = this.wrap.childNodes[0];
32712         this.ctNode = this.wrap.childNodes[1];
32713         var cs = this.elNode.childNodes;
32714         this.indentNode = cs[0];
32715         this.ecNode = cs[1];
32716         this.iconNode = cs[2];
32717         var index = 3;
32718         if(cb){
32719             this.checkbox = cs[3];
32720             index++;
32721         }
32722         this.anchor = cs[index];
32723         this.textNode = cs[index].firstChild;
32724     },
32725
32726     getAnchor : function(){
32727         return this.anchor;
32728     },
32729
32730     getTextEl : function(){
32731         return this.textNode;
32732     },
32733
32734     getIconEl : function(){
32735         return this.iconNode;
32736     },
32737
32738     isChecked : function(){
32739         return this.checkbox ? this.checkbox.checked : false;
32740     },
32741
32742     updateExpandIcon : function(){
32743         if(this.rendered){
32744             var n = this.node, c1, c2;
32745             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32746             var hasChild = n.hasChildNodes();
32747             if(hasChild){
32748                 if(n.expanded){
32749                     cls += "-minus";
32750                     c1 = "x-tree-node-collapsed";
32751                     c2 = "x-tree-node-expanded";
32752                 }else{
32753                     cls += "-plus";
32754                     c1 = "x-tree-node-expanded";
32755                     c2 = "x-tree-node-collapsed";
32756                 }
32757                 if(this.wasLeaf){
32758                     this.removeClass("x-tree-node-leaf");
32759                     this.wasLeaf = false;
32760                 }
32761                 if(this.c1 != c1 || this.c2 != c2){
32762                     Roo.fly(this.elNode).replaceClass(c1, c2);
32763                     this.c1 = c1; this.c2 = c2;
32764                 }
32765             }else{
32766                 // this changes non-leafs into leafs if they have no children.
32767                 // it's not very rational behaviour..
32768                 
32769                 if(!this.wasLeaf && this.node.leaf){
32770                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32771                     delete this.c1;
32772                     delete this.c2;
32773                     this.wasLeaf = true;
32774                 }
32775             }
32776             var ecc = "x-tree-ec-icon "+cls;
32777             if(this.ecc != ecc){
32778                 this.ecNode.className = ecc;
32779                 this.ecc = ecc;
32780             }
32781         }
32782     },
32783
32784     getChildIndent : function(){
32785         if(!this.childIndent){
32786             var buf = [];
32787             var p = this.node;
32788             while(p){
32789                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32790                     if(!p.isLast()) {
32791                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32792                     } else {
32793                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32794                     }
32795                 }
32796                 p = p.parentNode;
32797             }
32798             this.childIndent = buf.join("");
32799         }
32800         return this.childIndent;
32801     },
32802
32803     renderIndent : function(){
32804         if(this.rendered){
32805             var indent = "";
32806             var p = this.node.parentNode;
32807             if(p){
32808                 indent = p.ui.getChildIndent();
32809             }
32810             if(this.indentMarkup != indent){ // don't rerender if not required
32811                 this.indentNode.innerHTML = indent;
32812                 this.indentMarkup = indent;
32813             }
32814             this.updateExpandIcon();
32815         }
32816     }
32817 };
32818
32819 Roo.tree.RootTreeNodeUI = function(){
32820     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32821 };
32822 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32823     render : function(){
32824         if(!this.rendered){
32825             var targetNode = this.node.ownerTree.innerCt.dom;
32826             this.node.expanded = true;
32827             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32828             this.wrap = this.ctNode = targetNode.firstChild;
32829         }
32830     },
32831     collapse : function(){
32832     },
32833     expand : function(){
32834     }
32835 });/*
32836  * Based on:
32837  * Ext JS Library 1.1.1
32838  * Copyright(c) 2006-2007, Ext JS, LLC.
32839  *
32840  * Originally Released Under LGPL - original licence link has changed is not relivant.
32841  *
32842  * Fork - LGPL
32843  * <script type="text/javascript">
32844  */
32845 /**
32846  * @class Roo.tree.TreeLoader
32847  * @extends Roo.util.Observable
32848  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32849  * nodes from a specified URL. The response must be a javascript Array definition
32850  * who's elements are node definition objects. eg:
32851  * <pre><code>
32852    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32853     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32854 </code></pre>
32855  * <br><br>
32856  * A server request is sent, and child nodes are loaded only when a node is expanded.
32857  * The loading node's id is passed to the server under the parameter name "node" to
32858  * enable the server to produce the correct child nodes.
32859  * <br><br>
32860  * To pass extra parameters, an event handler may be attached to the "beforeload"
32861  * event, and the parameters specified in the TreeLoader's baseParams property:
32862  * <pre><code>
32863     myTreeLoader.on("beforeload", function(treeLoader, node) {
32864         this.baseParams.category = node.attributes.category;
32865     }, this);
32866 </code></pre><
32867  * This would pass an HTTP parameter called "category" to the server containing
32868  * the value of the Node's "category" attribute.
32869  * @constructor
32870  * Creates a new Treeloader.
32871  * @param {Object} config A config object containing config properties.
32872  */
32873 Roo.tree.TreeLoader = function(config){
32874     this.baseParams = {};
32875     this.requestMethod = "POST";
32876     Roo.apply(this, config);
32877
32878     this.addEvents({
32879     
32880         /**
32881          * @event beforeload
32882          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32883          * @param {Object} This TreeLoader object.
32884          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32885          * @param {Object} callback The callback function specified in the {@link #load} call.
32886          */
32887         beforeload : true,
32888         /**
32889          * @event load
32890          * Fires when the node has been successfuly loaded.
32891          * @param {Object} This TreeLoader object.
32892          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32893          * @param {Object} response The response object containing the data from the server.
32894          */
32895         load : true,
32896         /**
32897          * @event loadexception
32898          * Fires if the network request failed.
32899          * @param {Object} This TreeLoader object.
32900          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32901          * @param {Object} response The response object containing the data from the server.
32902          */
32903         loadexception : true,
32904         /**
32905          * @event create
32906          * Fires before a node is created, enabling you to return custom Node types 
32907          * @param {Object} This TreeLoader object.
32908          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32909          */
32910         create : true
32911     });
32912
32913     Roo.tree.TreeLoader.superclass.constructor.call(this);
32914 };
32915
32916 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32917     /**
32918     * @cfg {String} dataUrl The URL from which to request a Json string which
32919     * specifies an array of node definition object representing the child nodes
32920     * to be loaded.
32921     */
32922     /**
32923     * @cfg {Object} baseParams (optional) An object containing properties which
32924     * specify HTTP parameters to be passed to each request for child nodes.
32925     */
32926     /**
32927     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32928     * created by this loader. If the attributes sent by the server have an attribute in this object,
32929     * they take priority.
32930     */
32931     /**
32932     * @cfg {Object} uiProviders (optional) An object containing properties which
32933     * 
32934     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32935     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32936     * <i>uiProvider</i> attribute of a returned child node is a string rather
32937     * than a reference to a TreeNodeUI implementation, this that string value
32938     * is used as a property name in the uiProviders object. You can define the provider named
32939     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32940     */
32941     uiProviders : {},
32942
32943     /**
32944     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32945     * child nodes before loading.
32946     */
32947     clearOnLoad : true,
32948
32949     /**
32950     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32951     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32952     * Grid query { data : [ .....] }
32953     */
32954     
32955     root : false,
32956      /**
32957     * @cfg {String} queryParam (optional) 
32958     * Name of the query as it will be passed on the querystring (defaults to 'node')
32959     * eg. the request will be ?node=[id]
32960     */
32961     
32962     
32963     queryParam: false,
32964     
32965     /**
32966      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32967      * This is called automatically when a node is expanded, but may be used to reload
32968      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32969      * @param {Roo.tree.TreeNode} node
32970      * @param {Function} callback
32971      */
32972     load : function(node, callback){
32973         if(this.clearOnLoad){
32974             while(node.firstChild){
32975                 node.removeChild(node.firstChild);
32976             }
32977         }
32978         if(node.attributes.children){ // preloaded json children
32979             var cs = node.attributes.children;
32980             for(var i = 0, len = cs.length; i < len; i++){
32981                 node.appendChild(this.createNode(cs[i]));
32982             }
32983             if(typeof callback == "function"){
32984                 callback();
32985             }
32986         }else if(this.dataUrl){
32987             this.requestData(node, callback);
32988         }
32989     },
32990
32991     getParams: function(node){
32992         var buf = [], bp = this.baseParams;
32993         for(var key in bp){
32994             if(typeof bp[key] != "function"){
32995                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32996             }
32997         }
32998         var n = this.queryParam === false ? 'node' : this.queryParam;
32999         buf.push(n + "=", encodeURIComponent(node.id));
33000         return buf.join("");
33001     },
33002
33003     requestData : function(node, callback){
33004         if(this.fireEvent("beforeload", this, node, callback) !== false){
33005             this.transId = Roo.Ajax.request({
33006                 method:this.requestMethod,
33007                 url: this.dataUrl||this.url,
33008                 success: this.handleResponse,
33009                 failure: this.handleFailure,
33010                 scope: this,
33011                 argument: {callback: callback, node: node},
33012                 params: this.getParams(node)
33013             });
33014         }else{
33015             // if the load is cancelled, make sure we notify
33016             // the node that we are done
33017             if(typeof callback == "function"){
33018                 callback();
33019             }
33020         }
33021     },
33022
33023     isLoading : function(){
33024         return this.transId ? true : false;
33025     },
33026
33027     abort : function(){
33028         if(this.isLoading()){
33029             Roo.Ajax.abort(this.transId);
33030         }
33031     },
33032
33033     // private
33034     createNode : function(attr)
33035     {
33036         // apply baseAttrs, nice idea Corey!
33037         if(this.baseAttrs){
33038             Roo.applyIf(attr, this.baseAttrs);
33039         }
33040         if(this.applyLoader !== false){
33041             attr.loader = this;
33042         }
33043         // uiProvider = depreciated..
33044         
33045         if(typeof(attr.uiProvider) == 'string'){
33046            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33047                 /**  eval:var:attr */ eval(attr.uiProvider);
33048         }
33049         if(typeof(this.uiProviders['default']) != 'undefined') {
33050             attr.uiProvider = this.uiProviders['default'];
33051         }
33052         
33053         this.fireEvent('create', this, attr);
33054         
33055         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33056         return(attr.leaf ?
33057                         new Roo.tree.TreeNode(attr) :
33058                         new Roo.tree.AsyncTreeNode(attr));
33059     },
33060
33061     processResponse : function(response, node, callback)
33062     {
33063         var json = response.responseText;
33064         try {
33065             
33066             var o = Roo.decode(json);
33067             
33068             if (!o.success) {
33069                 // it's a failure condition.
33070                 var a = response.argument;
33071                 this.fireEvent("loadexception", this, a.node, response);
33072                 Roo.log("Load failed - should have a handler really");
33073                 return;
33074             }
33075             
33076             if (this.root !== false) {
33077                 o = o[this.root];
33078             }
33079             
33080             for(var i = 0, len = o.length; i < len; i++){
33081                 var n = this.createNode(o[i]);
33082                 if(n){
33083                     node.appendChild(n);
33084                 }
33085             }
33086             if(typeof callback == "function"){
33087                 callback(this, node);
33088             }
33089         }catch(e){
33090             this.handleFailure(response);
33091         }
33092     },
33093
33094     handleResponse : function(response){
33095         this.transId = false;
33096         var a = response.argument;
33097         this.processResponse(response, a.node, a.callback);
33098         this.fireEvent("load", this, a.node, response);
33099     },
33100
33101     handleFailure : function(response)
33102     {
33103         // should handle failure better..
33104         this.transId = false;
33105         var a = response.argument;
33106         this.fireEvent("loadexception", this, a.node, response);
33107         if(typeof a.callback == "function"){
33108             a.callback(this, a.node);
33109         }
33110     }
33111 });/*
33112  * Based on:
33113  * Ext JS Library 1.1.1
33114  * Copyright(c) 2006-2007, Ext JS, LLC.
33115  *
33116  * Originally Released Under LGPL - original licence link has changed is not relivant.
33117  *
33118  * Fork - LGPL
33119  * <script type="text/javascript">
33120  */
33121
33122 /**
33123 * @class Roo.tree.TreeFilter
33124 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33125 * @param {TreePanel} tree
33126 * @param {Object} config (optional)
33127  */
33128 Roo.tree.TreeFilter = function(tree, config){
33129     this.tree = tree;
33130     this.filtered = {};
33131     Roo.apply(this, config);
33132 };
33133
33134 Roo.tree.TreeFilter.prototype = {
33135     clearBlank:false,
33136     reverse:false,
33137     autoClear:false,
33138     remove:false,
33139
33140      /**
33141      * Filter the data by a specific attribute.
33142      * @param {String/RegExp} value Either string that the attribute value
33143      * should start with or a RegExp to test against the attribute
33144      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33145      * @param {TreeNode} startNode (optional) The node to start the filter at.
33146      */
33147     filter : function(value, attr, startNode){
33148         attr = attr || "text";
33149         var f;
33150         if(typeof value == "string"){
33151             var vlen = value.length;
33152             // auto clear empty filter
33153             if(vlen == 0 && this.clearBlank){
33154                 this.clear();
33155                 return;
33156             }
33157             value = value.toLowerCase();
33158             f = function(n){
33159                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33160             };
33161         }else if(value.exec){ // regex?
33162             f = function(n){
33163                 return value.test(n.attributes[attr]);
33164             };
33165         }else{
33166             throw 'Illegal filter type, must be string or regex';
33167         }
33168         this.filterBy(f, null, startNode);
33169         },
33170
33171     /**
33172      * Filter by a function. The passed function will be called with each
33173      * node in the tree (or from the startNode). If the function returns true, the node is kept
33174      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33175      * @param {Function} fn The filter function
33176      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33177      */
33178     filterBy : function(fn, scope, startNode){
33179         startNode = startNode || this.tree.root;
33180         if(this.autoClear){
33181             this.clear();
33182         }
33183         var af = this.filtered, rv = this.reverse;
33184         var f = function(n){
33185             if(n == startNode){
33186                 return true;
33187             }
33188             if(af[n.id]){
33189                 return false;
33190             }
33191             var m = fn.call(scope || n, n);
33192             if(!m || rv){
33193                 af[n.id] = n;
33194                 n.ui.hide();
33195                 return false;
33196             }
33197             return true;
33198         };
33199         startNode.cascade(f);
33200         if(this.remove){
33201            for(var id in af){
33202                if(typeof id != "function"){
33203                    var n = af[id];
33204                    if(n && n.parentNode){
33205                        n.parentNode.removeChild(n);
33206                    }
33207                }
33208            }
33209         }
33210     },
33211
33212     /**
33213      * Clears the current filter. Note: with the "remove" option
33214      * set a filter cannot be cleared.
33215      */
33216     clear : function(){
33217         var t = this.tree;
33218         var af = this.filtered;
33219         for(var id in af){
33220             if(typeof id != "function"){
33221                 var n = af[id];
33222                 if(n){
33223                     n.ui.show();
33224                 }
33225             }
33226         }
33227         this.filtered = {};
33228     }
33229 };
33230 /*
33231  * Based on:
33232  * Ext JS Library 1.1.1
33233  * Copyright(c) 2006-2007, Ext JS, LLC.
33234  *
33235  * Originally Released Under LGPL - original licence link has changed is not relivant.
33236  *
33237  * Fork - LGPL
33238  * <script type="text/javascript">
33239  */
33240  
33241
33242 /**
33243  * @class Roo.tree.TreeSorter
33244  * Provides sorting of nodes in a TreePanel
33245  * 
33246  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33247  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33248  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33249  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33250  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33251  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33252  * @constructor
33253  * @param {TreePanel} tree
33254  * @param {Object} config
33255  */
33256 Roo.tree.TreeSorter = function(tree, config){
33257     Roo.apply(this, config);
33258     tree.on("beforechildrenrendered", this.doSort, this);
33259     tree.on("append", this.updateSort, this);
33260     tree.on("insert", this.updateSort, this);
33261     
33262     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33263     var p = this.property || "text";
33264     var sortType = this.sortType;
33265     var fs = this.folderSort;
33266     var cs = this.caseSensitive === true;
33267     var leafAttr = this.leafAttr || 'leaf';
33268
33269     this.sortFn = function(n1, n2){
33270         if(fs){
33271             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33272                 return 1;
33273             }
33274             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33275                 return -1;
33276             }
33277         }
33278         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33279         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33280         if(v1 < v2){
33281                         return dsc ? +1 : -1;
33282                 }else if(v1 > v2){
33283                         return dsc ? -1 : +1;
33284         }else{
33285                 return 0;
33286         }
33287     };
33288 };
33289
33290 Roo.tree.TreeSorter.prototype = {
33291     doSort : function(node){
33292         node.sort(this.sortFn);
33293     },
33294     
33295     compareNodes : function(n1, n2){
33296         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33297     },
33298     
33299     updateSort : function(tree, node){
33300         if(node.childrenRendered){
33301             this.doSort.defer(1, this, [node]);
33302         }
33303     }
33304 };/*
33305  * Based on:
33306  * Ext JS Library 1.1.1
33307  * Copyright(c) 2006-2007, Ext JS, LLC.
33308  *
33309  * Originally Released Under LGPL - original licence link has changed is not relivant.
33310  *
33311  * Fork - LGPL
33312  * <script type="text/javascript">
33313  */
33314
33315 if(Roo.dd.DropZone){
33316     
33317 Roo.tree.TreeDropZone = function(tree, config){
33318     this.allowParentInsert = false;
33319     this.allowContainerDrop = false;
33320     this.appendOnly = false;
33321     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33322     this.tree = tree;
33323     this.lastInsertClass = "x-tree-no-status";
33324     this.dragOverData = {};
33325 };
33326
33327 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33328     ddGroup : "TreeDD",
33329     
33330     expandDelay : 1000,
33331     
33332     expandNode : function(node){
33333         if(node.hasChildNodes() && !node.isExpanded()){
33334             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33335         }
33336     },
33337     
33338     queueExpand : function(node){
33339         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33340     },
33341     
33342     cancelExpand : function(){
33343         if(this.expandProcId){
33344             clearTimeout(this.expandProcId);
33345             this.expandProcId = false;
33346         }
33347     },
33348     
33349     isValidDropPoint : function(n, pt, dd, e, data){
33350         if(!n || !data){ return false; }
33351         var targetNode = n.node;
33352         var dropNode = data.node;
33353         // default drop rules
33354         if(!(targetNode && targetNode.isTarget && pt)){
33355             return false;
33356         }
33357         if(pt == "append" && targetNode.allowChildren === false){
33358             return false;
33359         }
33360         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33361             return false;
33362         }
33363         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33364             return false;
33365         }
33366         // reuse the object
33367         var overEvent = this.dragOverData;
33368         overEvent.tree = this.tree;
33369         overEvent.target = targetNode;
33370         overEvent.data = data;
33371         overEvent.point = pt;
33372         overEvent.source = dd;
33373         overEvent.rawEvent = e;
33374         overEvent.dropNode = dropNode;
33375         overEvent.cancel = false;  
33376         var result = this.tree.fireEvent("nodedragover", overEvent);
33377         return overEvent.cancel === false && result !== false;
33378     },
33379     
33380     getDropPoint : function(e, n, dd){
33381         var tn = n.node;
33382         if(tn.isRoot){
33383             return tn.allowChildren !== false ? "append" : false; // always append for root
33384         }
33385         var dragEl = n.ddel;
33386         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33387         var y = Roo.lib.Event.getPageY(e);
33388         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33389         
33390         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33391         var noAppend = tn.allowChildren === false;
33392         if(this.appendOnly || tn.parentNode.allowChildren === false){
33393             return noAppend ? false : "append";
33394         }
33395         var noBelow = false;
33396         if(!this.allowParentInsert){
33397             noBelow = tn.hasChildNodes() && tn.isExpanded();
33398         }
33399         var q = (b - t) / (noAppend ? 2 : 3);
33400         if(y >= t && y < (t + q)){
33401             return "above";
33402         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33403             return "below";
33404         }else{
33405             return "append";
33406         }
33407     },
33408     
33409     onNodeEnter : function(n, dd, e, data){
33410         this.cancelExpand();
33411     },
33412     
33413     onNodeOver : function(n, dd, e, data){
33414         var pt = this.getDropPoint(e, n, dd);
33415         var node = n.node;
33416         
33417         // auto node expand check
33418         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33419             this.queueExpand(node);
33420         }else if(pt != "append"){
33421             this.cancelExpand();
33422         }
33423         
33424         // set the insert point style on the target node
33425         var returnCls = this.dropNotAllowed;
33426         if(this.isValidDropPoint(n, pt, dd, e, data)){
33427            if(pt){
33428                var el = n.ddel;
33429                var cls;
33430                if(pt == "above"){
33431                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33432                    cls = "x-tree-drag-insert-above";
33433                }else if(pt == "below"){
33434                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33435                    cls = "x-tree-drag-insert-below";
33436                }else{
33437                    returnCls = "x-tree-drop-ok-append";
33438                    cls = "x-tree-drag-append";
33439                }
33440                if(this.lastInsertClass != cls){
33441                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33442                    this.lastInsertClass = cls;
33443                }
33444            }
33445        }
33446        return returnCls;
33447     },
33448     
33449     onNodeOut : function(n, dd, e, data){
33450         this.cancelExpand();
33451         this.removeDropIndicators(n);
33452     },
33453     
33454     onNodeDrop : function(n, dd, e, data){
33455         var point = this.getDropPoint(e, n, dd);
33456         var targetNode = n.node;
33457         targetNode.ui.startDrop();
33458         if(!this.isValidDropPoint(n, point, dd, e, data)){
33459             targetNode.ui.endDrop();
33460             return false;
33461         }
33462         // first try to find the drop node
33463         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33464         var dropEvent = {
33465             tree : this.tree,
33466             target: targetNode,
33467             data: data,
33468             point: point,
33469             source: dd,
33470             rawEvent: e,
33471             dropNode: dropNode,
33472             cancel: !dropNode   
33473         };
33474         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33475         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33476             targetNode.ui.endDrop();
33477             return false;
33478         }
33479         // allow target changing
33480         targetNode = dropEvent.target;
33481         if(point == "append" && !targetNode.isExpanded()){
33482             targetNode.expand(false, null, function(){
33483                 this.completeDrop(dropEvent);
33484             }.createDelegate(this));
33485         }else{
33486             this.completeDrop(dropEvent);
33487         }
33488         return true;
33489     },
33490     
33491     completeDrop : function(de){
33492         var ns = de.dropNode, p = de.point, t = de.target;
33493         if(!(ns instanceof Array)){
33494             ns = [ns];
33495         }
33496         var n;
33497         for(var i = 0, len = ns.length; i < len; i++){
33498             n = ns[i];
33499             if(p == "above"){
33500                 t.parentNode.insertBefore(n, t);
33501             }else if(p == "below"){
33502                 t.parentNode.insertBefore(n, t.nextSibling);
33503             }else{
33504                 t.appendChild(n);
33505             }
33506         }
33507         n.ui.focus();
33508         if(this.tree.hlDrop){
33509             n.ui.highlight();
33510         }
33511         t.ui.endDrop();
33512         this.tree.fireEvent("nodedrop", de);
33513     },
33514     
33515     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33516         if(this.tree.hlDrop){
33517             dropNode.ui.focus();
33518             dropNode.ui.highlight();
33519         }
33520         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33521     },
33522     
33523     getTree : function(){
33524         return this.tree;
33525     },
33526     
33527     removeDropIndicators : function(n){
33528         if(n && n.ddel){
33529             var el = n.ddel;
33530             Roo.fly(el).removeClass([
33531                     "x-tree-drag-insert-above",
33532                     "x-tree-drag-insert-below",
33533                     "x-tree-drag-append"]);
33534             this.lastInsertClass = "_noclass";
33535         }
33536     },
33537     
33538     beforeDragDrop : function(target, e, id){
33539         this.cancelExpand();
33540         return true;
33541     },
33542     
33543     afterRepair : function(data){
33544         if(data && Roo.enableFx){
33545             data.node.ui.highlight();
33546         }
33547         this.hideProxy();
33548     }    
33549 });
33550
33551 }
33552 /*
33553  * Based on:
33554  * Ext JS Library 1.1.1
33555  * Copyright(c) 2006-2007, Ext JS, LLC.
33556  *
33557  * Originally Released Under LGPL - original licence link has changed is not relivant.
33558  *
33559  * Fork - LGPL
33560  * <script type="text/javascript">
33561  */
33562  
33563
33564 if(Roo.dd.DragZone){
33565 Roo.tree.TreeDragZone = function(tree, config){
33566     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33567     this.tree = tree;
33568 };
33569
33570 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33571     ddGroup : "TreeDD",
33572     
33573     onBeforeDrag : function(data, e){
33574         var n = data.node;
33575         return n && n.draggable && !n.disabled;
33576     },
33577     
33578     onInitDrag : function(e){
33579         var data = this.dragData;
33580         this.tree.getSelectionModel().select(data.node);
33581         this.proxy.update("");
33582         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33583         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33584     },
33585     
33586     getRepairXY : function(e, data){
33587         return data.node.ui.getDDRepairXY();
33588     },
33589     
33590     onEndDrag : function(data, e){
33591         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33592     },
33593     
33594     onValidDrop : function(dd, e, id){
33595         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33596         this.hideProxy();
33597     },
33598     
33599     beforeInvalidDrop : function(e, id){
33600         // this scrolls the original position back into view
33601         var sm = this.tree.getSelectionModel();
33602         sm.clearSelections();
33603         sm.select(this.dragData.node);
33604     }
33605 });
33606 }/*
33607  * Based on:
33608  * Ext JS Library 1.1.1
33609  * Copyright(c) 2006-2007, Ext JS, LLC.
33610  *
33611  * Originally Released Under LGPL - original licence link has changed is not relivant.
33612  *
33613  * Fork - LGPL
33614  * <script type="text/javascript">
33615  */
33616 /**
33617  * @class Roo.tree.TreeEditor
33618  * @extends Roo.Editor
33619  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33620  * as the editor field.
33621  * @constructor
33622  * @param {Object} config (used to be the tree panel.)
33623  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33624  * 
33625  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33626  * @cfg {Roo.form.TextField|Object} field The field configuration
33627  *
33628  * 
33629  */
33630 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33631     var tree = config;
33632     var field;
33633     if (oldconfig) { // old style..
33634         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33635     } else {
33636         // new style..
33637         tree = config.tree;
33638         config.field = config.field  || {};
33639         config.field.xtype = 'TextField';
33640         field = Roo.factory(config.field, Roo.form);
33641     }
33642     config = config || {};
33643     
33644     
33645     this.addEvents({
33646         /**
33647          * @event beforenodeedit
33648          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33649          * false from the handler of this event.
33650          * @param {Editor} this
33651          * @param {Roo.tree.Node} node 
33652          */
33653         "beforenodeedit" : true
33654     });
33655     
33656     //Roo.log(config);
33657     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33658
33659     this.tree = tree;
33660
33661     tree.on('beforeclick', this.beforeNodeClick, this);
33662     tree.getTreeEl().on('mousedown', this.hide, this);
33663     this.on('complete', this.updateNode, this);
33664     this.on('beforestartedit', this.fitToTree, this);
33665     this.on('startedit', this.bindScroll, this, {delay:10});
33666     this.on('specialkey', this.onSpecialKey, this);
33667 };
33668
33669 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33670     /**
33671      * @cfg {String} alignment
33672      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33673      */
33674     alignment: "l-l",
33675     // inherit
33676     autoSize: false,
33677     /**
33678      * @cfg {Boolean} hideEl
33679      * True to hide the bound element while the editor is displayed (defaults to false)
33680      */
33681     hideEl : false,
33682     /**
33683      * @cfg {String} cls
33684      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33685      */
33686     cls: "x-small-editor x-tree-editor",
33687     /**
33688      * @cfg {Boolean} shim
33689      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33690      */
33691     shim:false,
33692     // inherit
33693     shadow:"frame",
33694     /**
33695      * @cfg {Number} maxWidth
33696      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33697      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33698      * scroll and client offsets into account prior to each edit.
33699      */
33700     maxWidth: 250,
33701
33702     editDelay : 350,
33703
33704     // private
33705     fitToTree : function(ed, el){
33706         var td = this.tree.getTreeEl().dom, nd = el.dom;
33707         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33708             td.scrollLeft = nd.offsetLeft;
33709         }
33710         var w = Math.min(
33711                 this.maxWidth,
33712                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33713         this.setSize(w, '');
33714         
33715         return this.fireEvent('beforenodeedit', this, this.editNode);
33716         
33717     },
33718
33719     // private
33720     triggerEdit : function(node){
33721         this.completeEdit();
33722         this.editNode = node;
33723         this.startEdit(node.ui.textNode, node.text);
33724     },
33725
33726     // private
33727     bindScroll : function(){
33728         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33729     },
33730
33731     // private
33732     beforeNodeClick : function(node, e){
33733         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33734         this.lastClick = new Date();
33735         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33736             e.stopEvent();
33737             this.triggerEdit(node);
33738             return false;
33739         }
33740         return true;
33741     },
33742
33743     // private
33744     updateNode : function(ed, value){
33745         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33746         this.editNode.setText(value);
33747     },
33748
33749     // private
33750     onHide : function(){
33751         Roo.tree.TreeEditor.superclass.onHide.call(this);
33752         if(this.editNode){
33753             this.editNode.ui.focus();
33754         }
33755     },
33756
33757     // private
33758     onSpecialKey : function(field, e){
33759         var k = e.getKey();
33760         if(k == e.ESC){
33761             e.stopEvent();
33762             this.cancelEdit();
33763         }else if(k == e.ENTER && !e.hasModifier()){
33764             e.stopEvent();
33765             this.completeEdit();
33766         }
33767     }
33768 });//<Script type="text/javascript">
33769 /*
33770  * Based on:
33771  * Ext JS Library 1.1.1
33772  * Copyright(c) 2006-2007, Ext JS, LLC.
33773  *
33774  * Originally Released Under LGPL - original licence link has changed is not relivant.
33775  *
33776  * Fork - LGPL
33777  * <script type="text/javascript">
33778  */
33779  
33780 /**
33781  * Not documented??? - probably should be...
33782  */
33783
33784 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33785     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33786     
33787     renderElements : function(n, a, targetNode, bulkRender){
33788         //consel.log("renderElements?");
33789         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33790
33791         var t = n.getOwnerTree();
33792         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33793         
33794         var cols = t.columns;
33795         var bw = t.borderWidth;
33796         var c = cols[0];
33797         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33798          var cb = typeof a.checked == "boolean";
33799         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33800         var colcls = 'x-t-' + tid + '-c0';
33801         var buf = [
33802             '<li class="x-tree-node">',
33803             
33804                 
33805                 '<div class="x-tree-node-el ', a.cls,'">',
33806                     // extran...
33807                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33808                 
33809                 
33810                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33811                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33812                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33813                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33814                            (a.iconCls ? ' '+a.iconCls : ''),
33815                            '" unselectable="on" />',
33816                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33817                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33818                              
33819                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33820                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33821                             '<span unselectable="on" qtip="' + tx + '">',
33822                              tx,
33823                              '</span></a>' ,
33824                     '</div>',
33825                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33826                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33827                  ];
33828         for(var i = 1, len = cols.length; i < len; i++){
33829             c = cols[i];
33830             colcls = 'x-t-' + tid + '-c' +i;
33831             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33832             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33833                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33834                       "</div>");
33835          }
33836          
33837          buf.push(
33838             '</a>',
33839             '<div class="x-clear"></div></div>',
33840             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33841             "</li>");
33842         
33843         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33844             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33845                                 n.nextSibling.ui.getEl(), buf.join(""));
33846         }else{
33847             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33848         }
33849         var el = this.wrap.firstChild;
33850         this.elRow = el;
33851         this.elNode = el.firstChild;
33852         this.ranchor = el.childNodes[1];
33853         this.ctNode = this.wrap.childNodes[1];
33854         var cs = el.firstChild.childNodes;
33855         this.indentNode = cs[0];
33856         this.ecNode = cs[1];
33857         this.iconNode = cs[2];
33858         var index = 3;
33859         if(cb){
33860             this.checkbox = cs[3];
33861             index++;
33862         }
33863         this.anchor = cs[index];
33864         
33865         this.textNode = cs[index].firstChild;
33866         
33867         //el.on("click", this.onClick, this);
33868         //el.on("dblclick", this.onDblClick, this);
33869         
33870         
33871        // console.log(this);
33872     },
33873     initEvents : function(){
33874         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33875         
33876             
33877         var a = this.ranchor;
33878
33879         var el = Roo.get(a);
33880
33881         if(Roo.isOpera){ // opera render bug ignores the CSS
33882             el.setStyle("text-decoration", "none");
33883         }
33884
33885         el.on("click", this.onClick, this);
33886         el.on("dblclick", this.onDblClick, this);
33887         el.on("contextmenu", this.onContextMenu, this);
33888         
33889     },
33890     
33891     /*onSelectedChange : function(state){
33892         if(state){
33893             this.focus();
33894             this.addClass("x-tree-selected");
33895         }else{
33896             //this.blur();
33897             this.removeClass("x-tree-selected");
33898         }
33899     },*/
33900     addClass : function(cls){
33901         if(this.elRow){
33902             Roo.fly(this.elRow).addClass(cls);
33903         }
33904         
33905     },
33906     
33907     
33908     removeClass : function(cls){
33909         if(this.elRow){
33910             Roo.fly(this.elRow).removeClass(cls);
33911         }
33912     }
33913
33914     
33915     
33916 });//<Script type="text/javascript">
33917
33918 /*
33919  * Based on:
33920  * Ext JS Library 1.1.1
33921  * Copyright(c) 2006-2007, Ext JS, LLC.
33922  *
33923  * Originally Released Under LGPL - original licence link has changed is not relivant.
33924  *
33925  * Fork - LGPL
33926  * <script type="text/javascript">
33927  */
33928  
33929
33930 /**
33931  * @class Roo.tree.ColumnTree
33932  * @extends Roo.data.TreePanel
33933  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33934  * @cfg {int} borderWidth  compined right/left border allowance
33935  * @constructor
33936  * @param {String/HTMLElement/Element} el The container element
33937  * @param {Object} config
33938  */
33939 Roo.tree.ColumnTree =  function(el, config)
33940 {
33941    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33942    this.addEvents({
33943         /**
33944         * @event resize
33945         * Fire this event on a container when it resizes
33946         * @param {int} w Width
33947         * @param {int} h Height
33948         */
33949        "resize" : true
33950     });
33951     this.on('resize', this.onResize, this);
33952 };
33953
33954 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33955     //lines:false,
33956     
33957     
33958     borderWidth: Roo.isBorderBox ? 0 : 2, 
33959     headEls : false,
33960     
33961     render : function(){
33962         // add the header.....
33963        
33964         Roo.tree.ColumnTree.superclass.render.apply(this);
33965         
33966         this.el.addClass('x-column-tree');
33967         
33968         this.headers = this.el.createChild(
33969             {cls:'x-tree-headers'},this.innerCt.dom);
33970    
33971         var cols = this.columns, c;
33972         var totalWidth = 0;
33973         this.headEls = [];
33974         var  len = cols.length;
33975         for(var i = 0; i < len; i++){
33976              c = cols[i];
33977              totalWidth += c.width;
33978             this.headEls.push(this.headers.createChild({
33979                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33980                  cn: {
33981                      cls:'x-tree-hd-text',
33982                      html: c.header
33983                  },
33984                  style:'width:'+(c.width-this.borderWidth)+'px;'
33985              }));
33986         }
33987         this.headers.createChild({cls:'x-clear'});
33988         // prevent floats from wrapping when clipped
33989         this.headers.setWidth(totalWidth);
33990         //this.innerCt.setWidth(totalWidth);
33991         this.innerCt.setStyle({ overflow: 'auto' });
33992         this.onResize(this.width, this.height);
33993              
33994         
33995     },
33996     onResize : function(w,h)
33997     {
33998         this.height = h;
33999         this.width = w;
34000         // resize cols..
34001         this.innerCt.setWidth(this.width);
34002         this.innerCt.setHeight(this.height-20);
34003         
34004         // headers...
34005         var cols = this.columns, c;
34006         var totalWidth = 0;
34007         var expEl = false;
34008         var len = cols.length;
34009         for(var i = 0; i < len; i++){
34010             c = cols[i];
34011             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34012                 // it's the expander..
34013                 expEl  = this.headEls[i];
34014                 continue;
34015             }
34016             totalWidth += c.width;
34017             
34018         }
34019         if (expEl) {
34020             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34021         }
34022         this.headers.setWidth(w-20);
34023
34024         
34025         
34026         
34027     }
34028 });
34029 /*
34030  * Based on:
34031  * Ext JS Library 1.1.1
34032  * Copyright(c) 2006-2007, Ext JS, LLC.
34033  *
34034  * Originally Released Under LGPL - original licence link has changed is not relivant.
34035  *
34036  * Fork - LGPL
34037  * <script type="text/javascript">
34038  */
34039  
34040 /**
34041  * @class Roo.menu.Menu
34042  * @extends Roo.util.Observable
34043  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34044  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34045  * @constructor
34046  * Creates a new Menu
34047  * @param {Object} config Configuration options
34048  */
34049 Roo.menu.Menu = function(config){
34050     Roo.apply(this, config);
34051     this.id = this.id || Roo.id();
34052     this.addEvents({
34053         /**
34054          * @event beforeshow
34055          * Fires before this menu is displayed
34056          * @param {Roo.menu.Menu} this
34057          */
34058         beforeshow : true,
34059         /**
34060          * @event beforehide
34061          * Fires before this menu is hidden
34062          * @param {Roo.menu.Menu} this
34063          */
34064         beforehide : true,
34065         /**
34066          * @event show
34067          * Fires after this menu is displayed
34068          * @param {Roo.menu.Menu} this
34069          */
34070         show : true,
34071         /**
34072          * @event hide
34073          * Fires after this menu is hidden
34074          * @param {Roo.menu.Menu} this
34075          */
34076         hide : true,
34077         /**
34078          * @event click
34079          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34080          * @param {Roo.menu.Menu} this
34081          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34082          * @param {Roo.EventObject} e
34083          */
34084         click : true,
34085         /**
34086          * @event mouseover
34087          * Fires when the mouse is hovering over this menu
34088          * @param {Roo.menu.Menu} this
34089          * @param {Roo.EventObject} e
34090          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34091          */
34092         mouseover : true,
34093         /**
34094          * @event mouseout
34095          * Fires when the mouse exits this menu
34096          * @param {Roo.menu.Menu} this
34097          * @param {Roo.EventObject} e
34098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34099          */
34100         mouseout : true,
34101         /**
34102          * @event itemclick
34103          * Fires when a menu item contained in this menu is clicked
34104          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34105          * @param {Roo.EventObject} e
34106          */
34107         itemclick: true
34108     });
34109     if (this.registerMenu) {
34110         Roo.menu.MenuMgr.register(this);
34111     }
34112     
34113     var mis = this.items;
34114     this.items = new Roo.util.MixedCollection();
34115     if(mis){
34116         this.add.apply(this, mis);
34117     }
34118 };
34119
34120 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34121     /**
34122      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34123      */
34124     minWidth : 120,
34125     /**
34126      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34127      * for bottom-right shadow (defaults to "sides")
34128      */
34129     shadow : "sides",
34130     /**
34131      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34132      * this menu (defaults to "tl-tr?")
34133      */
34134     subMenuAlign : "tl-tr?",
34135     /**
34136      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34137      * relative to its element of origin (defaults to "tl-bl?")
34138      */
34139     defaultAlign : "tl-bl?",
34140     /**
34141      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34142      */
34143     allowOtherMenus : false,
34144     /**
34145      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34146      */
34147     registerMenu : true,
34148
34149     hidden:true,
34150
34151     // private
34152     render : function(){
34153         if(this.el){
34154             return;
34155         }
34156         var el = this.el = new Roo.Layer({
34157             cls: "x-menu",
34158             shadow:this.shadow,
34159             constrain: false,
34160             parentEl: this.parentEl || document.body,
34161             zindex:15000
34162         });
34163
34164         this.keyNav = new Roo.menu.MenuNav(this);
34165
34166         if(this.plain){
34167             el.addClass("x-menu-plain");
34168         }
34169         if(this.cls){
34170             el.addClass(this.cls);
34171         }
34172         // generic focus element
34173         this.focusEl = el.createChild({
34174             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34175         });
34176         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34177         ul.on("click", this.onClick, this);
34178         ul.on("mouseover", this.onMouseOver, this);
34179         ul.on("mouseout", this.onMouseOut, this);
34180         this.items.each(function(item){
34181             var li = document.createElement("li");
34182             li.className = "x-menu-list-item";
34183             ul.dom.appendChild(li);
34184             item.render(li, this);
34185         }, this);
34186         this.ul = ul;
34187         this.autoWidth();
34188     },
34189
34190     // private
34191     autoWidth : function(){
34192         var el = this.el, ul = this.ul;
34193         if(!el){
34194             return;
34195         }
34196         var w = this.width;
34197         if(w){
34198             el.setWidth(w);
34199         }else if(Roo.isIE){
34200             el.setWidth(this.minWidth);
34201             var t = el.dom.offsetWidth; // force recalc
34202             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34203         }
34204     },
34205
34206     // private
34207     delayAutoWidth : function(){
34208         if(this.rendered){
34209             if(!this.awTask){
34210                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34211             }
34212             this.awTask.delay(20);
34213         }
34214     },
34215
34216     // private
34217     findTargetItem : function(e){
34218         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34219         if(t && t.menuItemId){
34220             return this.items.get(t.menuItemId);
34221         }
34222     },
34223
34224     // private
34225     onClick : function(e){
34226         var t;
34227         if(t = this.findTargetItem(e)){
34228             t.onClick(e);
34229             this.fireEvent("click", this, t, e);
34230         }
34231     },
34232
34233     // private
34234     setActiveItem : function(item, autoExpand){
34235         if(item != this.activeItem){
34236             if(this.activeItem){
34237                 this.activeItem.deactivate();
34238             }
34239             this.activeItem = item;
34240             item.activate(autoExpand);
34241         }else if(autoExpand){
34242             item.expandMenu();
34243         }
34244     },
34245
34246     // private
34247     tryActivate : function(start, step){
34248         var items = this.items;
34249         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34250             var item = items.get(i);
34251             if(!item.disabled && item.canActivate){
34252                 this.setActiveItem(item, false);
34253                 return item;
34254             }
34255         }
34256         return false;
34257     },
34258
34259     // private
34260     onMouseOver : function(e){
34261         var t;
34262         if(t = this.findTargetItem(e)){
34263             if(t.canActivate && !t.disabled){
34264                 this.setActiveItem(t, true);
34265             }
34266         }
34267         this.fireEvent("mouseover", this, e, t);
34268     },
34269
34270     // private
34271     onMouseOut : function(e){
34272         var t;
34273         if(t = this.findTargetItem(e)){
34274             if(t == this.activeItem && t.shouldDeactivate(e)){
34275                 this.activeItem.deactivate();
34276                 delete this.activeItem;
34277             }
34278         }
34279         this.fireEvent("mouseout", this, e, t);
34280     },
34281
34282     /**
34283      * Read-only.  Returns true if the menu is currently displayed, else false.
34284      * @type Boolean
34285      */
34286     isVisible : function(){
34287         return this.el && !this.hidden;
34288     },
34289
34290     /**
34291      * Displays this menu relative to another element
34292      * @param {String/HTMLElement/Roo.Element} element The element to align to
34293      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34294      * the element (defaults to this.defaultAlign)
34295      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34296      */
34297     show : function(el, pos, parentMenu){
34298         this.parentMenu = parentMenu;
34299         if(!this.el){
34300             this.render();
34301         }
34302         this.fireEvent("beforeshow", this);
34303         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34304     },
34305
34306     /**
34307      * Displays this menu at a specific xy position
34308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34310      */
34311     showAt : function(xy, parentMenu, /* private: */_e){
34312         this.parentMenu = parentMenu;
34313         if(!this.el){
34314             this.render();
34315         }
34316         if(_e !== false){
34317             this.fireEvent("beforeshow", this);
34318             xy = this.el.adjustForConstraints(xy);
34319         }
34320         this.el.setXY(xy);
34321         this.el.show();
34322         this.hidden = false;
34323         this.focus();
34324         this.fireEvent("show", this);
34325     },
34326
34327     focus : function(){
34328         if(!this.hidden){
34329             this.doFocus.defer(50, this);
34330         }
34331     },
34332
34333     doFocus : function(){
34334         if(!this.hidden){
34335             this.focusEl.focus();
34336         }
34337     },
34338
34339     /**
34340      * Hides this menu and optionally all parent menus
34341      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34342      */
34343     hide : function(deep){
34344         if(this.el && this.isVisible()){
34345             this.fireEvent("beforehide", this);
34346             if(this.activeItem){
34347                 this.activeItem.deactivate();
34348                 this.activeItem = null;
34349             }
34350             this.el.hide();
34351             this.hidden = true;
34352             this.fireEvent("hide", this);
34353         }
34354         if(deep === true && this.parentMenu){
34355             this.parentMenu.hide(true);
34356         }
34357     },
34358
34359     /**
34360      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34361      * Any of the following are valid:
34362      * <ul>
34363      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34364      * <li>An HTMLElement object which will be converted to a menu item</li>
34365      * <li>A menu item config object that will be created as a new menu item</li>
34366      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34367      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34368      * </ul>
34369      * Usage:
34370      * <pre><code>
34371 // Create the menu
34372 var menu = new Roo.menu.Menu();
34373
34374 // Create a menu item to add by reference
34375 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34376
34377 // Add a bunch of items at once using different methods.
34378 // Only the last item added will be returned.
34379 var item = menu.add(
34380     menuItem,                // add existing item by ref
34381     'Dynamic Item',          // new TextItem
34382     '-',                     // new separator
34383     { text: 'Config Item' }  // new item by config
34384 );
34385 </code></pre>
34386      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34387      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34388      */
34389     add : function(){
34390         var a = arguments, l = a.length, item;
34391         for(var i = 0; i < l; i++){
34392             var el = a[i];
34393             if ((typeof(el) == "object") && el.xtype && el.xns) {
34394                 el = Roo.factory(el, Roo.menu);
34395             }
34396             
34397             if(el.render){ // some kind of Item
34398                 item = this.addItem(el);
34399             }else if(typeof el == "string"){ // string
34400                 if(el == "separator" || el == "-"){
34401                     item = this.addSeparator();
34402                 }else{
34403                     item = this.addText(el);
34404                 }
34405             }else if(el.tagName || el.el){ // element
34406                 item = this.addElement(el);
34407             }else if(typeof el == "object"){ // must be menu item config?
34408                 item = this.addMenuItem(el);
34409             }
34410         }
34411         return item;
34412     },
34413
34414     /**
34415      * Returns this menu's underlying {@link Roo.Element} object
34416      * @return {Roo.Element} The element
34417      */
34418     getEl : function(){
34419         if(!this.el){
34420             this.render();
34421         }
34422         return this.el;
34423     },
34424
34425     /**
34426      * Adds a separator bar to the menu
34427      * @return {Roo.menu.Item} The menu item that was added
34428      */
34429     addSeparator : function(){
34430         return this.addItem(new Roo.menu.Separator());
34431     },
34432
34433     /**
34434      * Adds an {@link Roo.Element} object to the menu
34435      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34436      * @return {Roo.menu.Item} The menu item that was added
34437      */
34438     addElement : function(el){
34439         return this.addItem(new Roo.menu.BaseItem(el));
34440     },
34441
34442     /**
34443      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34444      * @param {Roo.menu.Item} item The menu item to add
34445      * @return {Roo.menu.Item} The menu item that was added
34446      */
34447     addItem : function(item){
34448         this.items.add(item);
34449         if(this.ul){
34450             var li = document.createElement("li");
34451             li.className = "x-menu-list-item";
34452             this.ul.dom.appendChild(li);
34453             item.render(li, this);
34454             this.delayAutoWidth();
34455         }
34456         return item;
34457     },
34458
34459     /**
34460      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34461      * @param {Object} config A MenuItem config object
34462      * @return {Roo.menu.Item} The menu item that was added
34463      */
34464     addMenuItem : function(config){
34465         if(!(config instanceof Roo.menu.Item)){
34466             if(typeof config.checked == "boolean"){ // must be check menu item config?
34467                 config = new Roo.menu.CheckItem(config);
34468             }else{
34469                 config = new Roo.menu.Item(config);
34470             }
34471         }
34472         return this.addItem(config);
34473     },
34474
34475     /**
34476      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34477      * @param {String} text The text to display in the menu item
34478      * @return {Roo.menu.Item} The menu item that was added
34479      */
34480     addText : function(text){
34481         return this.addItem(new Roo.menu.TextItem({ text : text }));
34482     },
34483
34484     /**
34485      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34486      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34487      * @param {Roo.menu.Item} item The menu item to add
34488      * @return {Roo.menu.Item} The menu item that was added
34489      */
34490     insert : function(index, item){
34491         this.items.insert(index, item);
34492         if(this.ul){
34493             var li = document.createElement("li");
34494             li.className = "x-menu-list-item";
34495             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34496             item.render(li, this);
34497             this.delayAutoWidth();
34498         }
34499         return item;
34500     },
34501
34502     /**
34503      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34504      * @param {Roo.menu.Item} item The menu item to remove
34505      */
34506     remove : function(item){
34507         this.items.removeKey(item.id);
34508         item.destroy();
34509     },
34510
34511     /**
34512      * Removes and destroys all items in the menu
34513      */
34514     removeAll : function(){
34515         var f;
34516         while(f = this.items.first()){
34517             this.remove(f);
34518         }
34519     }
34520 });
34521
34522 // MenuNav is a private utility class used internally by the Menu
34523 Roo.menu.MenuNav = function(menu){
34524     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34525     this.scope = this.menu = menu;
34526 };
34527
34528 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34529     doRelay : function(e, h){
34530         var k = e.getKey();
34531         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34532             this.menu.tryActivate(0, 1);
34533             return false;
34534         }
34535         return h.call(this.scope || this, e, this.menu);
34536     },
34537
34538     up : function(e, m){
34539         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34540             m.tryActivate(m.items.length-1, -1);
34541         }
34542     },
34543
34544     down : function(e, m){
34545         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34546             m.tryActivate(0, 1);
34547         }
34548     },
34549
34550     right : function(e, m){
34551         if(m.activeItem){
34552             m.activeItem.expandMenu(true);
34553         }
34554     },
34555
34556     left : function(e, m){
34557         m.hide();
34558         if(m.parentMenu && m.parentMenu.activeItem){
34559             m.parentMenu.activeItem.activate();
34560         }
34561     },
34562
34563     enter : function(e, m){
34564         if(m.activeItem){
34565             e.stopPropagation();
34566             m.activeItem.onClick(e);
34567             m.fireEvent("click", this, m.activeItem);
34568             return true;
34569         }
34570     }
34571 });/*
34572  * Based on:
34573  * Ext JS Library 1.1.1
34574  * Copyright(c) 2006-2007, Ext JS, LLC.
34575  *
34576  * Originally Released Under LGPL - original licence link has changed is not relivant.
34577  *
34578  * Fork - LGPL
34579  * <script type="text/javascript">
34580  */
34581  
34582 /**
34583  * @class Roo.menu.MenuMgr
34584  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34585  * @singleton
34586  */
34587 Roo.menu.MenuMgr = function(){
34588    var menus, active, groups = {}, attached = false, lastShow = new Date();
34589
34590    // private - called when first menu is created
34591    function init(){
34592        menus = {};
34593        active = new Roo.util.MixedCollection();
34594        Roo.get(document).addKeyListener(27, function(){
34595            if(active.length > 0){
34596                hideAll();
34597            }
34598        });
34599    }
34600
34601    // private
34602    function hideAll(){
34603        if(active && active.length > 0){
34604            var c = active.clone();
34605            c.each(function(m){
34606                m.hide();
34607            });
34608        }
34609    }
34610
34611    // private
34612    function onHide(m){
34613        active.remove(m);
34614        if(active.length < 1){
34615            Roo.get(document).un("mousedown", onMouseDown);
34616            attached = false;
34617        }
34618    }
34619
34620    // private
34621    function onShow(m){
34622        var last = active.last();
34623        lastShow = new Date();
34624        active.add(m);
34625        if(!attached){
34626            Roo.get(document).on("mousedown", onMouseDown);
34627            attached = true;
34628        }
34629        if(m.parentMenu){
34630           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34631           m.parentMenu.activeChild = m;
34632        }else if(last && last.isVisible()){
34633           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34634        }
34635    }
34636
34637    // private
34638    function onBeforeHide(m){
34639        if(m.activeChild){
34640            m.activeChild.hide();
34641        }
34642        if(m.autoHideTimer){
34643            clearTimeout(m.autoHideTimer);
34644            delete m.autoHideTimer;
34645        }
34646    }
34647
34648    // private
34649    function onBeforeShow(m){
34650        var pm = m.parentMenu;
34651        if(!pm && !m.allowOtherMenus){
34652            hideAll();
34653        }else if(pm && pm.activeChild && active != m){
34654            pm.activeChild.hide();
34655        }
34656    }
34657
34658    // private
34659    function onMouseDown(e){
34660        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34661            hideAll();
34662        }
34663    }
34664
34665    // private
34666    function onBeforeCheck(mi, state){
34667        if(state){
34668            var g = groups[mi.group];
34669            for(var i = 0, l = g.length; i < l; i++){
34670                if(g[i] != mi){
34671                    g[i].setChecked(false);
34672                }
34673            }
34674        }
34675    }
34676
34677    return {
34678
34679        /**
34680         * Hides all menus that are currently visible
34681         */
34682        hideAll : function(){
34683             hideAll();  
34684        },
34685
34686        // private
34687        register : function(menu){
34688            if(!menus){
34689                init();
34690            }
34691            menus[menu.id] = menu;
34692            menu.on("beforehide", onBeforeHide);
34693            menu.on("hide", onHide);
34694            menu.on("beforeshow", onBeforeShow);
34695            menu.on("show", onShow);
34696            var g = menu.group;
34697            if(g && menu.events["checkchange"]){
34698                if(!groups[g]){
34699                    groups[g] = [];
34700                }
34701                groups[g].push(menu);
34702                menu.on("checkchange", onCheck);
34703            }
34704        },
34705
34706         /**
34707          * Returns a {@link Roo.menu.Menu} object
34708          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34709          * be used to generate and return a new Menu instance.
34710          */
34711        get : function(menu){
34712            if(typeof menu == "string"){ // menu id
34713                return menus[menu];
34714            }else if(menu.events){  // menu instance
34715                return menu;
34716            }else if(typeof menu.length == 'number'){ // array of menu items?
34717                return new Roo.menu.Menu({items:menu});
34718            }else{ // otherwise, must be a config
34719                return new Roo.menu.Menu(menu);
34720            }
34721        },
34722
34723        // private
34724        unregister : function(menu){
34725            delete menus[menu.id];
34726            menu.un("beforehide", onBeforeHide);
34727            menu.un("hide", onHide);
34728            menu.un("beforeshow", onBeforeShow);
34729            menu.un("show", onShow);
34730            var g = menu.group;
34731            if(g && menu.events["checkchange"]){
34732                groups[g].remove(menu);
34733                menu.un("checkchange", onCheck);
34734            }
34735        },
34736
34737        // private
34738        registerCheckable : function(menuItem){
34739            var g = menuItem.group;
34740            if(g){
34741                if(!groups[g]){
34742                    groups[g] = [];
34743                }
34744                groups[g].push(menuItem);
34745                menuItem.on("beforecheckchange", onBeforeCheck);
34746            }
34747        },
34748
34749        // private
34750        unregisterCheckable : function(menuItem){
34751            var g = menuItem.group;
34752            if(g){
34753                groups[g].remove(menuItem);
34754                menuItem.un("beforecheckchange", onBeforeCheck);
34755            }
34756        }
34757    };
34758 }();/*
34759  * Based on:
34760  * Ext JS Library 1.1.1
34761  * Copyright(c) 2006-2007, Ext JS, LLC.
34762  *
34763  * Originally Released Under LGPL - original licence link has changed is not relivant.
34764  *
34765  * Fork - LGPL
34766  * <script type="text/javascript">
34767  */
34768  
34769
34770 /**
34771  * @class Roo.menu.BaseItem
34772  * @extends Roo.Component
34773  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34774  * management and base configuration options shared by all menu components.
34775  * @constructor
34776  * Creates a new BaseItem
34777  * @param {Object} config Configuration options
34778  */
34779 Roo.menu.BaseItem = function(config){
34780     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34781
34782     this.addEvents({
34783         /**
34784          * @event click
34785          * Fires when this item is clicked
34786          * @param {Roo.menu.BaseItem} this
34787          * @param {Roo.EventObject} e
34788          */
34789         click: true,
34790         /**
34791          * @event activate
34792          * Fires when this item is activated
34793          * @param {Roo.menu.BaseItem} this
34794          */
34795         activate : true,
34796         /**
34797          * @event deactivate
34798          * Fires when this item is deactivated
34799          * @param {Roo.menu.BaseItem} this
34800          */
34801         deactivate : true
34802     });
34803
34804     if(this.handler){
34805         this.on("click", this.handler, this.scope, true);
34806     }
34807 };
34808
34809 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34810     /**
34811      * @cfg {Function} handler
34812      * A function that will handle the click event of this menu item (defaults to undefined)
34813      */
34814     /**
34815      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34816      */
34817     canActivate : false,
34818     /**
34819      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34820      */
34821     activeClass : "x-menu-item-active",
34822     /**
34823      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34824      */
34825     hideOnClick : true,
34826     /**
34827      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34828      */
34829     hideDelay : 100,
34830
34831     // private
34832     ctype: "Roo.menu.BaseItem",
34833
34834     // private
34835     actionMode : "container",
34836
34837     // private
34838     render : function(container, parentMenu){
34839         this.parentMenu = parentMenu;
34840         Roo.menu.BaseItem.superclass.render.call(this, container);
34841         this.container.menuItemId = this.id;
34842     },
34843
34844     // private
34845     onRender : function(container, position){
34846         this.el = Roo.get(this.el);
34847         container.dom.appendChild(this.el.dom);
34848     },
34849
34850     // private
34851     onClick : function(e){
34852         if(!this.disabled && this.fireEvent("click", this, e) !== false
34853                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34854             this.handleClick(e);
34855         }else{
34856             e.stopEvent();
34857         }
34858     },
34859
34860     // private
34861     activate : function(){
34862         if(this.disabled){
34863             return false;
34864         }
34865         var li = this.container;
34866         li.addClass(this.activeClass);
34867         this.region = li.getRegion().adjust(2, 2, -2, -2);
34868         this.fireEvent("activate", this);
34869         return true;
34870     },
34871
34872     // private
34873     deactivate : function(){
34874         this.container.removeClass(this.activeClass);
34875         this.fireEvent("deactivate", this);
34876     },
34877
34878     // private
34879     shouldDeactivate : function(e){
34880         return !this.region || !this.region.contains(e.getPoint());
34881     },
34882
34883     // private
34884     handleClick : function(e){
34885         if(this.hideOnClick){
34886             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34887         }
34888     },
34889
34890     // private
34891     expandMenu : function(autoActivate){
34892         // do nothing
34893     },
34894
34895     // private
34896     hideMenu : function(){
34897         // do nothing
34898     }
34899 });/*
34900  * Based on:
34901  * Ext JS Library 1.1.1
34902  * Copyright(c) 2006-2007, Ext JS, LLC.
34903  *
34904  * Originally Released Under LGPL - original licence link has changed is not relivant.
34905  *
34906  * Fork - LGPL
34907  * <script type="text/javascript">
34908  */
34909  
34910 /**
34911  * @class Roo.menu.Adapter
34912  * @extends Roo.menu.BaseItem
34913  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34914  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34915  * @constructor
34916  * Creates a new Adapter
34917  * @param {Object} config Configuration options
34918  */
34919 Roo.menu.Adapter = function(component, config){
34920     Roo.menu.Adapter.superclass.constructor.call(this, config);
34921     this.component = component;
34922 };
34923 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34924     // private
34925     canActivate : true,
34926
34927     // private
34928     onRender : function(container, position){
34929         this.component.render(container);
34930         this.el = this.component.getEl();
34931     },
34932
34933     // private
34934     activate : function(){
34935         if(this.disabled){
34936             return false;
34937         }
34938         this.component.focus();
34939         this.fireEvent("activate", this);
34940         return true;
34941     },
34942
34943     // private
34944     deactivate : function(){
34945         this.fireEvent("deactivate", this);
34946     },
34947
34948     // private
34949     disable : function(){
34950         this.component.disable();
34951         Roo.menu.Adapter.superclass.disable.call(this);
34952     },
34953
34954     // private
34955     enable : function(){
34956         this.component.enable();
34957         Roo.menu.Adapter.superclass.enable.call(this);
34958     }
34959 });/*
34960  * Based on:
34961  * Ext JS Library 1.1.1
34962  * Copyright(c) 2006-2007, Ext JS, LLC.
34963  *
34964  * Originally Released Under LGPL - original licence link has changed is not relivant.
34965  *
34966  * Fork - LGPL
34967  * <script type="text/javascript">
34968  */
34969
34970 /**
34971  * @class Roo.menu.TextItem
34972  * @extends Roo.menu.BaseItem
34973  * Adds a static text string to a menu, usually used as either a heading or group separator.
34974  * Note: old style constructor with text is still supported.
34975  * 
34976  * @constructor
34977  * Creates a new TextItem
34978  * @param {Object} cfg Configuration
34979  */
34980 Roo.menu.TextItem = function(cfg){
34981     if (typeof(cfg) == 'string') {
34982         this.text = cfg;
34983     } else {
34984         Roo.apply(this,cfg);
34985     }
34986     
34987     Roo.menu.TextItem.superclass.constructor.call(this);
34988 };
34989
34990 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34991     /**
34992      * @cfg {Boolean} text Text to show on item.
34993      */
34994     text : '',
34995     
34996     /**
34997      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34998      */
34999     hideOnClick : false,
35000     /**
35001      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35002      */
35003     itemCls : "x-menu-text",
35004
35005     // private
35006     onRender : function(){
35007         var s = document.createElement("span");
35008         s.className = this.itemCls;
35009         s.innerHTML = this.text;
35010         this.el = s;
35011         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35012     }
35013 });/*
35014  * Based on:
35015  * Ext JS Library 1.1.1
35016  * Copyright(c) 2006-2007, Ext JS, LLC.
35017  *
35018  * Originally Released Under LGPL - original licence link has changed is not relivant.
35019  *
35020  * Fork - LGPL
35021  * <script type="text/javascript">
35022  */
35023
35024 /**
35025  * @class Roo.menu.Separator
35026  * @extends Roo.menu.BaseItem
35027  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35028  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35029  * @constructor
35030  * @param {Object} config Configuration options
35031  */
35032 Roo.menu.Separator = function(config){
35033     Roo.menu.Separator.superclass.constructor.call(this, config);
35034 };
35035
35036 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35037     /**
35038      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35039      */
35040     itemCls : "x-menu-sep",
35041     /**
35042      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35043      */
35044     hideOnClick : false,
35045
35046     // private
35047     onRender : function(li){
35048         var s = document.createElement("span");
35049         s.className = this.itemCls;
35050         s.innerHTML = "&#160;";
35051         this.el = s;
35052         li.addClass("x-menu-sep-li");
35053         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35054     }
35055 });/*
35056  * Based on:
35057  * Ext JS Library 1.1.1
35058  * Copyright(c) 2006-2007, Ext JS, LLC.
35059  *
35060  * Originally Released Under LGPL - original licence link has changed is not relivant.
35061  *
35062  * Fork - LGPL
35063  * <script type="text/javascript">
35064  */
35065 /**
35066  * @class Roo.menu.Item
35067  * @extends Roo.menu.BaseItem
35068  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35069  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35070  * activation and click handling.
35071  * @constructor
35072  * Creates a new Item
35073  * @param {Object} config Configuration options
35074  */
35075 Roo.menu.Item = function(config){
35076     Roo.menu.Item.superclass.constructor.call(this, config);
35077     if(this.menu){
35078         this.menu = Roo.menu.MenuMgr.get(this.menu);
35079     }
35080 };
35081 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35082     
35083     /**
35084      * @cfg {String} text
35085      * The text to show on the menu item.
35086      */
35087     text: '',
35088      /**
35089      * @cfg {String} HTML to render in menu
35090      * The text to show on the menu item (HTML version).
35091      */
35092     html: '',
35093     /**
35094      * @cfg {String} icon
35095      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35096      */
35097     icon: undefined,
35098     /**
35099      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35100      */
35101     itemCls : "x-menu-item",
35102     /**
35103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35104      */
35105     canActivate : true,
35106     /**
35107      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35108      */
35109     showDelay: 200,
35110     // doc'd in BaseItem
35111     hideDelay: 200,
35112
35113     // private
35114     ctype: "Roo.menu.Item",
35115     
35116     // private
35117     onRender : function(container, position){
35118         var el = document.createElement("a");
35119         el.hideFocus = true;
35120         el.unselectable = "on";
35121         el.href = this.href || "#";
35122         if(this.hrefTarget){
35123             el.target = this.hrefTarget;
35124         }
35125         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35126         
35127         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35128         
35129         el.innerHTML = String.format(
35130                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35131                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35132         this.el = el;
35133         Roo.menu.Item.superclass.onRender.call(this, container, position);
35134     },
35135
35136     /**
35137      * Sets the text to display in this menu item
35138      * @param {String} text The text to display
35139      * @param {Boolean} isHTML true to indicate text is pure html.
35140      */
35141     setText : function(text, isHTML){
35142         if (isHTML) {
35143             this.html = text;
35144         } else {
35145             this.text = text;
35146             this.html = '';
35147         }
35148         if(this.rendered){
35149             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35150      
35151             this.el.update(String.format(
35152                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35153                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35154             this.parentMenu.autoWidth();
35155         }
35156     },
35157
35158     // private
35159     handleClick : function(e){
35160         if(!this.href){ // if no link defined, stop the event automatically
35161             e.stopEvent();
35162         }
35163         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35164     },
35165
35166     // private
35167     activate : function(autoExpand){
35168         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35169             this.focus();
35170             if(autoExpand){
35171                 this.expandMenu();
35172             }
35173         }
35174         return true;
35175     },
35176
35177     // private
35178     shouldDeactivate : function(e){
35179         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35180             if(this.menu && this.menu.isVisible()){
35181                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35182             }
35183             return true;
35184         }
35185         return false;
35186     },
35187
35188     // private
35189     deactivate : function(){
35190         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35191         this.hideMenu();
35192     },
35193
35194     // private
35195     expandMenu : function(autoActivate){
35196         if(!this.disabled && this.menu){
35197             clearTimeout(this.hideTimer);
35198             delete this.hideTimer;
35199             if(!this.menu.isVisible() && !this.showTimer){
35200                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35201             }else if (this.menu.isVisible() && autoActivate){
35202                 this.menu.tryActivate(0, 1);
35203             }
35204         }
35205     },
35206
35207     // private
35208     deferExpand : function(autoActivate){
35209         delete this.showTimer;
35210         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35211         if(autoActivate){
35212             this.menu.tryActivate(0, 1);
35213         }
35214     },
35215
35216     // private
35217     hideMenu : function(){
35218         clearTimeout(this.showTimer);
35219         delete this.showTimer;
35220         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35221             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35222         }
35223     },
35224
35225     // private
35226     deferHide : function(){
35227         delete this.hideTimer;
35228         this.menu.hide();
35229     }
35230 });/*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241 /**
35242  * @class Roo.menu.CheckItem
35243  * @extends Roo.menu.Item
35244  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35245  * @constructor
35246  * Creates a new CheckItem
35247  * @param {Object} config Configuration options
35248  */
35249 Roo.menu.CheckItem = function(config){
35250     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35251     this.addEvents({
35252         /**
35253          * @event beforecheckchange
35254          * Fires before the checked value is set, providing an opportunity to cancel if needed
35255          * @param {Roo.menu.CheckItem} this
35256          * @param {Boolean} checked The new checked value that will be set
35257          */
35258         "beforecheckchange" : true,
35259         /**
35260          * @event checkchange
35261          * Fires after the checked value has been set
35262          * @param {Roo.menu.CheckItem} this
35263          * @param {Boolean} checked The checked value that was set
35264          */
35265         "checkchange" : true
35266     });
35267     if(this.checkHandler){
35268         this.on('checkchange', this.checkHandler, this.scope);
35269     }
35270 };
35271 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35272     /**
35273      * @cfg {String} group
35274      * All check items with the same group name will automatically be grouped into a single-select
35275      * radio button group (defaults to '')
35276      */
35277     /**
35278      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35279      */
35280     itemCls : "x-menu-item x-menu-check-item",
35281     /**
35282      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35283      */
35284     groupClass : "x-menu-group-item",
35285
35286     /**
35287      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35288      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35289      * initialized with checked = true will be rendered as checked.
35290      */
35291     checked: false,
35292
35293     // private
35294     ctype: "Roo.menu.CheckItem",
35295
35296     // private
35297     onRender : function(c){
35298         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35299         if(this.group){
35300             this.el.addClass(this.groupClass);
35301         }
35302         Roo.menu.MenuMgr.registerCheckable(this);
35303         if(this.checked){
35304             this.checked = false;
35305             this.setChecked(true, true);
35306         }
35307     },
35308
35309     // private
35310     destroy : function(){
35311         if(this.rendered){
35312             Roo.menu.MenuMgr.unregisterCheckable(this);
35313         }
35314         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35315     },
35316
35317     /**
35318      * Set the checked state of this item
35319      * @param {Boolean} checked The new checked value
35320      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35321      */
35322     setChecked : function(state, suppressEvent){
35323         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35324             if(this.container){
35325                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35326             }
35327             this.checked = state;
35328             if(suppressEvent !== true){
35329                 this.fireEvent("checkchange", this, state);
35330             }
35331         }
35332     },
35333
35334     // private
35335     handleClick : function(e){
35336        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35337            this.setChecked(!this.checked);
35338        }
35339        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35340     }
35341 });/*
35342  * Based on:
35343  * Ext JS Library 1.1.1
35344  * Copyright(c) 2006-2007, Ext JS, LLC.
35345  *
35346  * Originally Released Under LGPL - original licence link has changed is not relivant.
35347  *
35348  * Fork - LGPL
35349  * <script type="text/javascript">
35350  */
35351  
35352 /**
35353  * @class Roo.menu.DateItem
35354  * @extends Roo.menu.Adapter
35355  * A menu item that wraps the {@link Roo.DatPicker} component.
35356  * @constructor
35357  * Creates a new DateItem
35358  * @param {Object} config Configuration options
35359  */
35360 Roo.menu.DateItem = function(config){
35361     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35362     /** The Roo.DatePicker object @type Roo.DatePicker */
35363     this.picker = this.component;
35364     this.addEvents({select: true});
35365     
35366     this.picker.on("render", function(picker){
35367         picker.getEl().swallowEvent("click");
35368         picker.container.addClass("x-menu-date-item");
35369     });
35370
35371     this.picker.on("select", this.onSelect, this);
35372 };
35373
35374 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35375     // private
35376     onSelect : function(picker, date){
35377         this.fireEvent("select", this, date, picker);
35378         Roo.menu.DateItem.superclass.handleClick.call(this);
35379     }
35380 });/*
35381  * Based on:
35382  * Ext JS Library 1.1.1
35383  * Copyright(c) 2006-2007, Ext JS, LLC.
35384  *
35385  * Originally Released Under LGPL - original licence link has changed is not relivant.
35386  *
35387  * Fork - LGPL
35388  * <script type="text/javascript">
35389  */
35390  
35391 /**
35392  * @class Roo.menu.ColorItem
35393  * @extends Roo.menu.Adapter
35394  * A menu item that wraps the {@link Roo.ColorPalette} component.
35395  * @constructor
35396  * Creates a new ColorItem
35397  * @param {Object} config Configuration options
35398  */
35399 Roo.menu.ColorItem = function(config){
35400     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35401     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35402     this.palette = this.component;
35403     this.relayEvents(this.palette, ["select"]);
35404     if(this.selectHandler){
35405         this.on('select', this.selectHandler, this.scope);
35406     }
35407 };
35408 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35409  * Based on:
35410  * Ext JS Library 1.1.1
35411  * Copyright(c) 2006-2007, Ext JS, LLC.
35412  *
35413  * Originally Released Under LGPL - original licence link has changed is not relivant.
35414  *
35415  * Fork - LGPL
35416  * <script type="text/javascript">
35417  */
35418  
35419
35420 /**
35421  * @class Roo.menu.DateMenu
35422  * @extends Roo.menu.Menu
35423  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35424  * @constructor
35425  * Creates a new DateMenu
35426  * @param {Object} config Configuration options
35427  */
35428 Roo.menu.DateMenu = function(config){
35429     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35430     this.plain = true;
35431     var di = new Roo.menu.DateItem(config);
35432     this.add(di);
35433     /**
35434      * The {@link Roo.DatePicker} instance for this DateMenu
35435      * @type DatePicker
35436      */
35437     this.picker = di.picker;
35438     /**
35439      * @event select
35440      * @param {DatePicker} picker
35441      * @param {Date} date
35442      */
35443     this.relayEvents(di, ["select"]);
35444
35445     this.on('beforeshow', function(){
35446         if(this.picker){
35447             this.picker.hideMonthPicker(true);
35448         }
35449     }, this);
35450 };
35451 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35452     cls:'x-date-menu'
35453 });/*
35454  * Based on:
35455  * Ext JS Library 1.1.1
35456  * Copyright(c) 2006-2007, Ext JS, LLC.
35457  *
35458  * Originally Released Under LGPL - original licence link has changed is not relivant.
35459  *
35460  * Fork - LGPL
35461  * <script type="text/javascript">
35462  */
35463  
35464
35465 /**
35466  * @class Roo.menu.ColorMenu
35467  * @extends Roo.menu.Menu
35468  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35469  * @constructor
35470  * Creates a new ColorMenu
35471  * @param {Object} config Configuration options
35472  */
35473 Roo.menu.ColorMenu = function(config){
35474     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35475     this.plain = true;
35476     var ci = new Roo.menu.ColorItem(config);
35477     this.add(ci);
35478     /**
35479      * The {@link Roo.ColorPalette} instance for this ColorMenu
35480      * @type ColorPalette
35481      */
35482     this.palette = ci.palette;
35483     /**
35484      * @event select
35485      * @param {ColorPalette} palette
35486      * @param {String} color
35487      */
35488     this.relayEvents(ci, ["select"]);
35489 };
35490 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35491  * Based on:
35492  * Ext JS Library 1.1.1
35493  * Copyright(c) 2006-2007, Ext JS, LLC.
35494  *
35495  * Originally Released Under LGPL - original licence link has changed is not relivant.
35496  *
35497  * Fork - LGPL
35498  * <script type="text/javascript">
35499  */
35500  
35501 /**
35502  * @class Roo.form.Field
35503  * @extends Roo.BoxComponent
35504  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35505  * @constructor
35506  * Creates a new Field
35507  * @param {Object} config Configuration options
35508  */
35509 Roo.form.Field = function(config){
35510     Roo.form.Field.superclass.constructor.call(this, config);
35511 };
35512
35513 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35514     /**
35515      * @cfg {String} fieldLabel Label to use when rendering a form.
35516      */
35517        /**
35518      * @cfg {String} qtip Mouse over tip
35519      */
35520      
35521     /**
35522      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35523      */
35524     invalidClass : "x-form-invalid",
35525     /**
35526      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35527      */
35528     invalidText : "The value in this field is invalid",
35529     /**
35530      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35531      */
35532     focusClass : "x-form-focus",
35533     /**
35534      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35535       automatic validation (defaults to "keyup").
35536      */
35537     validationEvent : "keyup",
35538     /**
35539      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35540      */
35541     validateOnBlur : true,
35542     /**
35543      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35544      */
35545     validationDelay : 250,
35546     /**
35547      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35548      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35549      */
35550     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35551     /**
35552      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35553      */
35554     fieldClass : "x-form-field",
35555     /**
35556      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35557      *<pre>
35558 Value         Description
35559 -----------   ----------------------------------------------------------------------
35560 qtip          Display a quick tip when the user hovers over the field
35561 title         Display a default browser title attribute popup
35562 under         Add a block div beneath the field containing the error text
35563 side          Add an error icon to the right of the field with a popup on hover
35564 [element id]  Add the error text directly to the innerHTML of the specified element
35565 </pre>
35566      */
35567     msgTarget : 'qtip',
35568     /**
35569      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35570      */
35571     msgFx : 'normal',
35572
35573     /**
35574      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35575      */
35576     readOnly : false,
35577
35578     /**
35579      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35580      */
35581     disabled : false,
35582
35583     /**
35584      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35585      */
35586     inputType : undefined,
35587     
35588     /**
35589      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35590          */
35591         tabIndex : undefined,
35592         
35593     // private
35594     isFormField : true,
35595
35596     // private
35597     hasFocus : false,
35598     /**
35599      * @property {Roo.Element} fieldEl
35600      * Element Containing the rendered Field (with label etc.)
35601      */
35602     /**
35603      * @cfg {Mixed} value A value to initialize this field with.
35604      */
35605     value : undefined,
35606
35607     /**
35608      * @cfg {String} name The field's HTML name attribute.
35609      */
35610     /**
35611      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35612      */
35613
35614         // private ??
35615         initComponent : function(){
35616         Roo.form.Field.superclass.initComponent.call(this);
35617         this.addEvents({
35618             /**
35619              * @event focus
35620              * Fires when this field receives input focus.
35621              * @param {Roo.form.Field} this
35622              */
35623             focus : true,
35624             /**
35625              * @event blur
35626              * Fires when this field loses input focus.
35627              * @param {Roo.form.Field} this
35628              */
35629             blur : true,
35630             /**
35631              * @event specialkey
35632              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35633              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35634              * @param {Roo.form.Field} this
35635              * @param {Roo.EventObject} e The event object
35636              */
35637             specialkey : true,
35638             /**
35639              * @event change
35640              * Fires just before the field blurs if the field value has changed.
35641              * @param {Roo.form.Field} this
35642              * @param {Mixed} newValue The new value
35643              * @param {Mixed} oldValue The original value
35644              */
35645             change : true,
35646             /**
35647              * @event invalid
35648              * Fires after the field has been marked as invalid.
35649              * @param {Roo.form.Field} this
35650              * @param {String} msg The validation message
35651              */
35652             invalid : true,
35653             /**
35654              * @event valid
35655              * Fires after the field has been validated with no errors.
35656              * @param {Roo.form.Field} this
35657              */
35658             valid : true,
35659              /**
35660              * @event keyup
35661              * Fires after the key up
35662              * @param {Roo.form.Field} this
35663              * @param {Roo.EventObject}  e The event Object
35664              */
35665             keyup : true
35666         });
35667     },
35668
35669     /**
35670      * Returns the name attribute of the field if available
35671      * @return {String} name The field name
35672      */
35673     getName: function(){
35674          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35675     },
35676
35677     // private
35678     onRender : function(ct, position){
35679         Roo.form.Field.superclass.onRender.call(this, ct, position);
35680         if(!this.el){
35681             var cfg = this.getAutoCreate();
35682             if(!cfg.name){
35683                 cfg.name = this.name || this.id;
35684             }
35685             if(this.inputType){
35686                 cfg.type = this.inputType;
35687             }
35688             this.el = ct.createChild(cfg, position);
35689         }
35690         var type = this.el.dom.type;
35691         if(type){
35692             if(type == 'password'){
35693                 type = 'text';
35694             }
35695             this.el.addClass('x-form-'+type);
35696         }
35697         if(this.readOnly){
35698             this.el.dom.readOnly = true;
35699         }
35700         if(this.tabIndex !== undefined){
35701             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35702         }
35703
35704         this.el.addClass([this.fieldClass, this.cls]);
35705         this.initValue();
35706     },
35707
35708     /**
35709      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35710      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35711      * @return {Roo.form.Field} this
35712      */
35713     applyTo : function(target){
35714         this.allowDomMove = false;
35715         this.el = Roo.get(target);
35716         this.render(this.el.dom.parentNode);
35717         return this;
35718     },
35719
35720     // private
35721     initValue : function(){
35722         if(this.value !== undefined){
35723             this.setValue(this.value);
35724         }else if(this.el.dom.value.length > 0){
35725             this.setValue(this.el.dom.value);
35726         }
35727     },
35728
35729     /**
35730      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35731      */
35732     isDirty : function() {
35733         if(this.disabled) {
35734             return false;
35735         }
35736         return String(this.getValue()) !== String(this.originalValue);
35737     },
35738
35739     // private
35740     afterRender : function(){
35741         Roo.form.Field.superclass.afterRender.call(this);
35742         this.initEvents();
35743     },
35744
35745     // private
35746     fireKey : function(e){
35747         //Roo.log('field ' + e.getKey());
35748         if(e.isNavKeyPress()){
35749             this.fireEvent("specialkey", this, e);
35750         }
35751     },
35752
35753     /**
35754      * Resets the current field value to the originally loaded value and clears any validation messages
35755      */
35756     reset : function(){
35757         this.setValue(this.originalValue);
35758         this.clearInvalid();
35759     },
35760
35761     // private
35762     initEvents : function(){
35763         // safari killled keypress - so keydown is now used..
35764         this.el.on("keydown" , this.fireKey,  this);
35765         this.el.on("focus", this.onFocus,  this);
35766         this.el.on("blur", this.onBlur,  this);
35767         this.el.relayEvent('keyup', this);
35768
35769         // reference to original value for reset
35770         this.originalValue = this.getValue();
35771     },
35772
35773     // private
35774     onFocus : function(){
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.addClass(this.focusClass);
35777         }
35778         if(!this.hasFocus){
35779             this.hasFocus = true;
35780             this.startValue = this.getValue();
35781             this.fireEvent("focus", this);
35782         }
35783     },
35784
35785     beforeBlur : Roo.emptyFn,
35786
35787     // private
35788     onBlur : function(){
35789         this.beforeBlur();
35790         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35791             this.el.removeClass(this.focusClass);
35792         }
35793         this.hasFocus = false;
35794         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35795             this.validate();
35796         }
35797         var v = this.getValue();
35798         if(String(v) !== String(this.startValue)){
35799             this.fireEvent('change', this, v, this.startValue);
35800         }
35801         this.fireEvent("blur", this);
35802     },
35803
35804     /**
35805      * Returns whether or not the field value is currently valid
35806      * @param {Boolean} preventMark True to disable marking the field invalid
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     isValid : function(preventMark){
35810         if(this.disabled){
35811             return true;
35812         }
35813         var restore = this.preventMark;
35814         this.preventMark = preventMark === true;
35815         var v = this.validateValue(this.processValue(this.getRawValue()));
35816         this.preventMark = restore;
35817         return v;
35818     },
35819
35820     /**
35821      * Validates the field value
35822      * @return {Boolean} True if the value is valid, else false
35823      */
35824     validate : function(){
35825         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35826             this.clearInvalid();
35827             return true;
35828         }
35829         return false;
35830     },
35831
35832     processValue : function(value){
35833         return value;
35834     },
35835
35836     // private
35837     // Subclasses should provide the validation implementation by overriding this
35838     validateValue : function(value){
35839         return true;
35840     },
35841
35842     /**
35843      * Mark this field as invalid
35844      * @param {String} msg The validation message
35845      */
35846     markInvalid : function(msg){
35847         if(!this.rendered || this.preventMark){ // not rendered
35848             return;
35849         }
35850         this.el.addClass(this.invalidClass);
35851         msg = msg || this.invalidText;
35852         switch(this.msgTarget){
35853             case 'qtip':
35854                 this.el.dom.qtip = msg;
35855                 this.el.dom.qclass = 'x-form-invalid-tip';
35856                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35857                     Roo.QuickTips.enable();
35858                 }
35859                 break;
35860             case 'title':
35861                 this.el.dom.title = msg;
35862                 break;
35863             case 'under':
35864                 if(!this.errorEl){
35865                     var elp = this.el.findParent('.x-form-element', 5, true);
35866                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35867                     this.errorEl.setWidth(elp.getWidth(true)-20);
35868                 }
35869                 this.errorEl.update(msg);
35870                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35871                 break;
35872             case 'side':
35873                 if(!this.errorIcon){
35874                     var elp = this.el.findParent('.x-form-element', 5, true);
35875                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35876                 }
35877                 this.alignErrorIcon();
35878                 this.errorIcon.dom.qtip = msg;
35879                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35880                 this.errorIcon.show();
35881                 this.on('resize', this.alignErrorIcon, this);
35882                 break;
35883             default:
35884                 var t = Roo.getDom(this.msgTarget);
35885                 t.innerHTML = msg;
35886                 t.style.display = this.msgDisplay;
35887                 break;
35888         }
35889         this.fireEvent('invalid', this, msg);
35890     },
35891
35892     // private
35893     alignErrorIcon : function(){
35894         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35895     },
35896
35897     /**
35898      * Clear any invalid styles/messages for this field
35899      */
35900     clearInvalid : function(){
35901         if(!this.rendered || this.preventMark){ // not rendered
35902             return;
35903         }
35904         this.el.removeClass(this.invalidClass);
35905         switch(this.msgTarget){
35906             case 'qtip':
35907                 this.el.dom.qtip = '';
35908                 break;
35909             case 'title':
35910                 this.el.dom.title = '';
35911                 break;
35912             case 'under':
35913                 if(this.errorEl){
35914                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35915                 }
35916                 break;
35917             case 'side':
35918                 if(this.errorIcon){
35919                     this.errorIcon.dom.qtip = '';
35920                     this.errorIcon.hide();
35921                     this.un('resize', this.alignErrorIcon, this);
35922                 }
35923                 break;
35924             default:
35925                 var t = Roo.getDom(this.msgTarget);
35926                 t.innerHTML = '';
35927                 t.style.display = 'none';
35928                 break;
35929         }
35930         this.fireEvent('valid', this);
35931     },
35932
35933     /**
35934      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35935      * @return {Mixed} value The field value
35936      */
35937     getRawValue : function(){
35938         var v = this.el.getValue();
35939         if(v === this.emptyText){
35940             v = '';
35941         }
35942         return v;
35943     },
35944
35945     /**
35946      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35947      * @return {Mixed} value The field value
35948      */
35949     getValue : function(){
35950         var v = this.el.getValue();
35951         if(v === this.emptyText || v === undefined){
35952             v = '';
35953         }
35954         return v;
35955     },
35956
35957     /**
35958      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35959      * @param {Mixed} value The value to set
35960      */
35961     setRawValue : function(v){
35962         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35963     },
35964
35965     /**
35966      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35967      * @param {Mixed} value The value to set
35968      */
35969     setValue : function(v){
35970         this.value = v;
35971         if(this.rendered){
35972             this.el.dom.value = (v === null || v === undefined ? '' : v);
35973              this.validate();
35974         }
35975     },
35976
35977     adjustSize : function(w, h){
35978         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35979         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35980         return s;
35981     },
35982
35983     adjustWidth : function(tag, w){
35984         tag = tag.toLowerCase();
35985         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35986             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35987                 if(tag == 'input'){
35988                     return w + 2;
35989                 }
35990                 if(tag = 'textarea'){
35991                     return w-2;
35992                 }
35993             }else if(Roo.isOpera){
35994                 if(tag == 'input'){
35995                     return w + 2;
35996                 }
35997                 if(tag = 'textarea'){
35998                     return w-2;
35999                 }
36000             }
36001         }
36002         return w;
36003     }
36004 });
36005
36006
36007 // anything other than normal should be considered experimental
36008 Roo.form.Field.msgFx = {
36009     normal : {
36010         show: function(msgEl, f){
36011             msgEl.setDisplayed('block');
36012         },
36013
36014         hide : function(msgEl, f){
36015             msgEl.setDisplayed(false).update('');
36016         }
36017     },
36018
36019     slide : {
36020         show: function(msgEl, f){
36021             msgEl.slideIn('t', {stopFx:true});
36022         },
36023
36024         hide : function(msgEl, f){
36025             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36026         }
36027     },
36028
36029     slideRight : {
36030         show: function(msgEl, f){
36031             msgEl.fixDisplay();
36032             msgEl.alignTo(f.el, 'tl-tr');
36033             msgEl.slideIn('l', {stopFx:true});
36034         },
36035
36036         hide : function(msgEl, f){
36037             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36038         }
36039     }
36040 };/*
36041  * Based on:
36042  * Ext JS Library 1.1.1
36043  * Copyright(c) 2006-2007, Ext JS, LLC.
36044  *
36045  * Originally Released Under LGPL - original licence link has changed is not relivant.
36046  *
36047  * Fork - LGPL
36048  * <script type="text/javascript">
36049  */
36050  
36051
36052 /**
36053  * @class Roo.form.TextField
36054  * @extends Roo.form.Field
36055  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36056  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36057  * @constructor
36058  * Creates a new TextField
36059  * @param {Object} config Configuration options
36060  */
36061 Roo.form.TextField = function(config){
36062     Roo.form.TextField.superclass.constructor.call(this, config);
36063     this.addEvents({
36064         /**
36065          * @event autosize
36066          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36067          * according to the default logic, but this event provides a hook for the developer to apply additional
36068          * logic at runtime to resize the field if needed.
36069              * @param {Roo.form.Field} this This text field
36070              * @param {Number} width The new field width
36071              */
36072         autosize : true
36073     });
36074 };
36075
36076 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36077     /**
36078      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36079      */
36080     grow : false,
36081     /**
36082      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36083      */
36084     growMin : 30,
36085     /**
36086      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36087      */
36088     growMax : 800,
36089     /**
36090      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36091      */
36092     vtype : null,
36093     /**
36094      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36095      */
36096     maskRe : null,
36097     /**
36098      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36099      */
36100     disableKeyFilter : false,
36101     /**
36102      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36103      */
36104     allowBlank : true,
36105     /**
36106      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36107      */
36108     minLength : 0,
36109     /**
36110      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36111      */
36112     maxLength : Number.MAX_VALUE,
36113     /**
36114      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36115      */
36116     minLengthText : "The minimum length for this field is {0}",
36117     /**
36118      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36119      */
36120     maxLengthText : "The maximum length for this field is {0}",
36121     /**
36122      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36123      */
36124     selectOnFocus : false,
36125     /**
36126      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36127      */
36128     blankText : "This field is required",
36129     /**
36130      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36131      * If available, this function will be called only after the basic validators all return true, and will be passed the
36132      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36133      */
36134     validator : null,
36135     /**
36136      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36137      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36138      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36139      */
36140     regex : null,
36141     /**
36142      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36143      */
36144     regexText : "",
36145     /**
36146      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36147      */
36148     emptyText : null,
36149     /**
36150      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36151      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36152      */
36153     emptyClass : 'x-form-empty-field',
36154
36155     // private
36156     initEvents : function(){
36157         Roo.form.TextField.superclass.initEvents.call(this);
36158         if(this.validationEvent == 'keyup'){
36159             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36160             this.el.on('keyup', this.filterValidation, this);
36161         }
36162         else if(this.validationEvent !== false){
36163             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36164         }
36165         if(this.selectOnFocus || this.emptyText){
36166             this.on("focus", this.preFocus, this);
36167             if(this.emptyText){
36168                 this.on('blur', this.postBlur, this);
36169                 this.applyEmptyText();
36170             }
36171         }
36172         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36173             this.el.on("keypress", this.filterKeys, this);
36174         }
36175         if(this.grow){
36176             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36177             this.el.on("click", this.autoSize,  this);
36178         }
36179     },
36180
36181     processValue : function(value){
36182         if(this.stripCharsRe){
36183             var newValue = value.replace(this.stripCharsRe, '');
36184             if(newValue !== value){
36185                 this.setRawValue(newValue);
36186                 return newValue;
36187             }
36188         }
36189         return value;
36190     },
36191
36192     filterValidation : function(e){
36193         if(!e.isNavKeyPress()){
36194             this.validationTask.delay(this.validationDelay);
36195         }
36196     },
36197
36198     // private
36199     onKeyUp : function(e){
36200         if(!e.isNavKeyPress()){
36201             this.autoSize();
36202         }
36203     },
36204
36205     /**
36206      * Resets the current field value to the originally-loaded value and clears any validation messages.
36207      * Also adds emptyText and emptyClass if the original value was blank.
36208      */
36209     reset : function(){
36210         Roo.form.TextField.superclass.reset.call(this);
36211         this.applyEmptyText();
36212     },
36213
36214     applyEmptyText : function(){
36215         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36216             this.setRawValue(this.emptyText);
36217             this.el.addClass(this.emptyClass);
36218         }
36219     },
36220
36221     // private
36222     preFocus : function(){
36223         if(this.emptyText){
36224             if(this.el.dom.value == this.emptyText){
36225                 this.setRawValue('');
36226             }
36227             this.el.removeClass(this.emptyClass);
36228         }
36229         if(this.selectOnFocus){
36230             this.el.dom.select();
36231         }
36232     },
36233
36234     // private
36235     postBlur : function(){
36236         this.applyEmptyText();
36237     },
36238
36239     // private
36240     filterKeys : function(e){
36241         var k = e.getKey();
36242         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36243             return;
36244         }
36245         var c = e.getCharCode(), cc = String.fromCharCode(c);
36246         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36247             return;
36248         }
36249         if(!this.maskRe.test(cc)){
36250             e.stopEvent();
36251         }
36252     },
36253
36254     setValue : function(v){
36255         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36256             this.el.removeClass(this.emptyClass);
36257         }
36258         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36259         this.applyEmptyText();
36260         this.autoSize();
36261     },
36262
36263     /**
36264      * Validates a value according to the field's validation rules and marks the field as invalid
36265      * if the validation fails
36266      * @param {Mixed} value The value to validate
36267      * @return {Boolean} True if the value is valid, else false
36268      */
36269     validateValue : function(value){
36270         if(value.length < 1 || value === this.emptyText){ // if it's blank
36271              if(this.allowBlank){
36272                 this.clearInvalid();
36273                 return true;
36274              }else{
36275                 this.markInvalid(this.blankText);
36276                 return false;
36277              }
36278         }
36279         if(value.length < this.minLength){
36280             this.markInvalid(String.format(this.minLengthText, this.minLength));
36281             return false;
36282         }
36283         if(value.length > this.maxLength){
36284             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36285             return false;
36286         }
36287         if(this.vtype){
36288             var vt = Roo.form.VTypes;
36289             if(!vt[this.vtype](value, this)){
36290                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36291                 return false;
36292             }
36293         }
36294         if(typeof this.validator == "function"){
36295             var msg = this.validator(value);
36296             if(msg !== true){
36297                 this.markInvalid(msg);
36298                 return false;
36299             }
36300         }
36301         if(this.regex && !this.regex.test(value)){
36302             this.markInvalid(this.regexText);
36303             return false;
36304         }
36305         return true;
36306     },
36307
36308     /**
36309      * Selects text in this field
36310      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36311      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36312      */
36313     selectText : function(start, end){
36314         var v = this.getRawValue();
36315         if(v.length > 0){
36316             start = start === undefined ? 0 : start;
36317             end = end === undefined ? v.length : end;
36318             var d = this.el.dom;
36319             if(d.setSelectionRange){
36320                 d.setSelectionRange(start, end);
36321             }else if(d.createTextRange){
36322                 var range = d.createTextRange();
36323                 range.moveStart("character", start);
36324                 range.moveEnd("character", v.length-end);
36325                 range.select();
36326             }
36327         }
36328     },
36329
36330     /**
36331      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36332      * This only takes effect if grow = true, and fires the autosize event.
36333      */
36334     autoSize : function(){
36335         if(!this.grow || !this.rendered){
36336             return;
36337         }
36338         if(!this.metrics){
36339             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36340         }
36341         var el = this.el;
36342         var v = el.dom.value;
36343         var d = document.createElement('div');
36344         d.appendChild(document.createTextNode(v));
36345         v = d.innerHTML;
36346         d = null;
36347         v += "&#160;";
36348         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36349         this.el.setWidth(w);
36350         this.fireEvent("autosize", this, w);
36351     }
36352 });/*
36353  * Based on:
36354  * Ext JS Library 1.1.1
36355  * Copyright(c) 2006-2007, Ext JS, LLC.
36356  *
36357  * Originally Released Under LGPL - original licence link has changed is not relivant.
36358  *
36359  * Fork - LGPL
36360  * <script type="text/javascript">
36361  */
36362  
36363 /**
36364  * @class Roo.form.Hidden
36365  * @extends Roo.form.TextField
36366  * Simple Hidden element used on forms 
36367  * 
36368  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36369  * 
36370  * @constructor
36371  * Creates a new Hidden form element.
36372  * @param {Object} config Configuration options
36373  */
36374
36375
36376
36377 // easy hidden field...
36378 Roo.form.Hidden = function(config){
36379     Roo.form.Hidden.superclass.constructor.call(this, config);
36380 };
36381   
36382 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36383     fieldLabel:      '',
36384     inputType:      'hidden',
36385     width:          50,
36386     allowBlank:     true,
36387     labelSeparator: '',
36388     hidden:         true,
36389     itemCls :       'x-form-item-display-none'
36390
36391
36392 });
36393
36394
36395 /*
36396  * Based on:
36397  * Ext JS Library 1.1.1
36398  * Copyright(c) 2006-2007, Ext JS, LLC.
36399  *
36400  * Originally Released Under LGPL - original licence link has changed is not relivant.
36401  *
36402  * Fork - LGPL
36403  * <script type="text/javascript">
36404  */
36405  
36406 /**
36407  * @class Roo.form.TriggerField
36408  * @extends Roo.form.TextField
36409  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36410  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36411  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36412  * for which you can provide a custom implementation.  For example:
36413  * <pre><code>
36414 var trigger = new Roo.form.TriggerField();
36415 trigger.onTriggerClick = myTriggerFn;
36416 trigger.applyTo('my-field');
36417 </code></pre>
36418  *
36419  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36420  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36421  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36422  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36423  * @constructor
36424  * Create a new TriggerField.
36425  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36426  * to the base TextField)
36427  */
36428 Roo.form.TriggerField = function(config){
36429     this.mimicing = false;
36430     Roo.form.TriggerField.superclass.constructor.call(this, config);
36431 };
36432
36433 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36434     /**
36435      * @cfg {String} triggerClass A CSS class to apply to the trigger
36436      */
36437     /**
36438      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36439      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36440      */
36441     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36442     /**
36443      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36444      */
36445     hideTrigger:false,
36446
36447     /** @cfg {Boolean} grow @hide */
36448     /** @cfg {Number} growMin @hide */
36449     /** @cfg {Number} growMax @hide */
36450
36451     /**
36452      * @hide 
36453      * @method
36454      */
36455     autoSize: Roo.emptyFn,
36456     // private
36457     monitorTab : true,
36458     // private
36459     deferHeight : true,
36460
36461     
36462     actionMode : 'wrap',
36463     // private
36464     onResize : function(w, h){
36465         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36466         if(typeof w == 'number'){
36467             var x = w - this.trigger.getWidth();
36468             this.el.setWidth(this.adjustWidth('input', x));
36469             this.trigger.setStyle('left', x+'px');
36470         }
36471     },
36472
36473     // private
36474     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36475
36476     // private
36477     getResizeEl : function(){
36478         return this.wrap;
36479     },
36480
36481     // private
36482     getPositionEl : function(){
36483         return this.wrap;
36484     },
36485
36486     // private
36487     alignErrorIcon : function(){
36488         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36489     },
36490
36491     // private
36492     onRender : function(ct, position){
36493         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36494         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36495         this.trigger = this.wrap.createChild(this.triggerConfig ||
36496                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36497         if(this.hideTrigger){
36498             this.trigger.setDisplayed(false);
36499         }
36500         this.initTrigger();
36501         if(!this.width){
36502             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36503         }
36504     },
36505
36506     // private
36507     initTrigger : function(){
36508         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36509         this.trigger.addClassOnOver('x-form-trigger-over');
36510         this.trigger.addClassOnClick('x-form-trigger-click');
36511     },
36512
36513     // private
36514     onDestroy : function(){
36515         if(this.trigger){
36516             this.trigger.removeAllListeners();
36517             this.trigger.remove();
36518         }
36519         if(this.wrap){
36520             this.wrap.remove();
36521         }
36522         Roo.form.TriggerField.superclass.onDestroy.call(this);
36523     },
36524
36525     // private
36526     onFocus : function(){
36527         Roo.form.TriggerField.superclass.onFocus.call(this);
36528         if(!this.mimicing){
36529             this.wrap.addClass('x-trigger-wrap-focus');
36530             this.mimicing = true;
36531             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36532             if(this.monitorTab){
36533                 this.el.on("keydown", this.checkTab, this);
36534             }
36535         }
36536     },
36537
36538     // private
36539     checkTab : function(e){
36540         if(e.getKey() == e.TAB){
36541             this.triggerBlur();
36542         }
36543     },
36544
36545     // private
36546     onBlur : function(){
36547         // do nothing
36548     },
36549
36550     // private
36551     mimicBlur : function(e, t){
36552         if(!this.wrap.contains(t) && this.validateBlur()){
36553             this.triggerBlur();
36554         }
36555     },
36556
36557     // private
36558     triggerBlur : function(){
36559         this.mimicing = false;
36560         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36561         if(this.monitorTab){
36562             this.el.un("keydown", this.checkTab, this);
36563         }
36564         this.wrap.removeClass('x-trigger-wrap-focus');
36565         Roo.form.TriggerField.superclass.onBlur.call(this);
36566     },
36567
36568     // private
36569     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36570     validateBlur : function(e, t){
36571         return true;
36572     },
36573
36574     // private
36575     onDisable : function(){
36576         Roo.form.TriggerField.superclass.onDisable.call(this);
36577         if(this.wrap){
36578             this.wrap.addClass('x-item-disabled');
36579         }
36580     },
36581
36582     // private
36583     onEnable : function(){
36584         Roo.form.TriggerField.superclass.onEnable.call(this);
36585         if(this.wrap){
36586             this.wrap.removeClass('x-item-disabled');
36587         }
36588     },
36589
36590     // private
36591     onShow : function(){
36592         var ae = this.getActionEl();
36593         
36594         if(ae){
36595             ae.dom.style.display = '';
36596             ae.dom.style.visibility = 'visible';
36597         }
36598     },
36599
36600     // private
36601     
36602     onHide : function(){
36603         var ae = this.getActionEl();
36604         ae.dom.style.display = 'none';
36605     },
36606
36607     /**
36608      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36609      * by an implementing function.
36610      * @method
36611      * @param {EventObject} e
36612      */
36613     onTriggerClick : Roo.emptyFn
36614 });
36615
36616 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36617 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36618 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36619 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36620     initComponent : function(){
36621         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36622
36623         this.triggerConfig = {
36624             tag:'span', cls:'x-form-twin-triggers', cn:[
36625             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36626             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36627         ]};
36628     },
36629
36630     getTrigger : function(index){
36631         return this.triggers[index];
36632     },
36633
36634     initTrigger : function(){
36635         var ts = this.trigger.select('.x-form-trigger', true);
36636         this.wrap.setStyle('overflow', 'hidden');
36637         var triggerField = this;
36638         ts.each(function(t, all, index){
36639             t.hide = function(){
36640                 var w = triggerField.wrap.getWidth();
36641                 this.dom.style.display = 'none';
36642                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36643             };
36644             t.show = function(){
36645                 var w = triggerField.wrap.getWidth();
36646                 this.dom.style.display = '';
36647                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36648             };
36649             var triggerIndex = 'Trigger'+(index+1);
36650
36651             if(this['hide'+triggerIndex]){
36652                 t.dom.style.display = 'none';
36653             }
36654             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36655             t.addClassOnOver('x-form-trigger-over');
36656             t.addClassOnClick('x-form-trigger-click');
36657         }, this);
36658         this.triggers = ts.elements;
36659     },
36660
36661     onTrigger1Click : Roo.emptyFn,
36662     onTrigger2Click : Roo.emptyFn
36663 });/*
36664  * Based on:
36665  * Ext JS Library 1.1.1
36666  * Copyright(c) 2006-2007, Ext JS, LLC.
36667  *
36668  * Originally Released Under LGPL - original licence link has changed is not relivant.
36669  *
36670  * Fork - LGPL
36671  * <script type="text/javascript">
36672  */
36673  
36674 /**
36675  * @class Roo.form.TextArea
36676  * @extends Roo.form.TextField
36677  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36678  * support for auto-sizing.
36679  * @constructor
36680  * Creates a new TextArea
36681  * @param {Object} config Configuration options
36682  */
36683 Roo.form.TextArea = function(config){
36684     Roo.form.TextArea.superclass.constructor.call(this, config);
36685     // these are provided exchanges for backwards compat
36686     // minHeight/maxHeight were replaced by growMin/growMax to be
36687     // compatible with TextField growing config values
36688     if(this.minHeight !== undefined){
36689         this.growMin = this.minHeight;
36690     }
36691     if(this.maxHeight !== undefined){
36692         this.growMax = this.maxHeight;
36693     }
36694 };
36695
36696 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36697     /**
36698      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36699      */
36700     growMin : 60,
36701     /**
36702      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36703      */
36704     growMax: 1000,
36705     /**
36706      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36707      * in the field (equivalent to setting overflow: hidden, defaults to false)
36708      */
36709     preventScrollbars: false,
36710     /**
36711      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36712      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36713      */
36714
36715     // private
36716     onRender : function(ct, position){
36717         if(!this.el){
36718             this.defaultAutoCreate = {
36719                 tag: "textarea",
36720                 style:"width:300px;height:60px;",
36721                 autocomplete: "off"
36722             };
36723         }
36724         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36725         if(this.grow){
36726             this.textSizeEl = Roo.DomHelper.append(document.body, {
36727                 tag: "pre", cls: "x-form-grow-sizer"
36728             });
36729             if(this.preventScrollbars){
36730                 this.el.setStyle("overflow", "hidden");
36731             }
36732             this.el.setHeight(this.growMin);
36733         }
36734     },
36735
36736     onDestroy : function(){
36737         if(this.textSizeEl){
36738             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36739         }
36740         Roo.form.TextArea.superclass.onDestroy.call(this);
36741     },
36742
36743     // private
36744     onKeyUp : function(e){
36745         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36746             this.autoSize();
36747         }
36748     },
36749
36750     /**
36751      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36752      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36753      */
36754     autoSize : function(){
36755         if(!this.grow || !this.textSizeEl){
36756             return;
36757         }
36758         var el = this.el;
36759         var v = el.dom.value;
36760         var ts = this.textSizeEl;
36761
36762         ts.innerHTML = '';
36763         ts.appendChild(document.createTextNode(v));
36764         v = ts.innerHTML;
36765
36766         Roo.fly(ts).setWidth(this.el.getWidth());
36767         if(v.length < 1){
36768             v = "&#160;&#160;";
36769         }else{
36770             if(Roo.isIE){
36771                 v = v.replace(/\n/g, '<p>&#160;</p>');
36772             }
36773             v += "&#160;\n&#160;";
36774         }
36775         ts.innerHTML = v;
36776         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36777         if(h != this.lastHeight){
36778             this.lastHeight = h;
36779             this.el.setHeight(h);
36780             this.fireEvent("autosize", this, h);
36781         }
36782     }
36783 });/*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793  
36794
36795 /**
36796  * @class Roo.form.NumberField
36797  * @extends Roo.form.TextField
36798  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36799  * @constructor
36800  * Creates a new NumberField
36801  * @param {Object} config Configuration options
36802  */
36803 Roo.form.NumberField = function(config){
36804     Roo.form.NumberField.superclass.constructor.call(this, config);
36805 };
36806
36807 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36808     /**
36809      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36810      */
36811     fieldClass: "x-form-field x-form-num-field",
36812     /**
36813      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36814      */
36815     allowDecimals : true,
36816     /**
36817      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36818      */
36819     decimalSeparator : ".",
36820     /**
36821      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36822      */
36823     decimalPrecision : 2,
36824     /**
36825      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36826      */
36827     allowNegative : true,
36828     /**
36829      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36830      */
36831     minValue : Number.NEGATIVE_INFINITY,
36832     /**
36833      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36834      */
36835     maxValue : Number.MAX_VALUE,
36836     /**
36837      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36838      */
36839     minText : "The minimum value for this field is {0}",
36840     /**
36841      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36842      */
36843     maxText : "The maximum value for this field is {0}",
36844     /**
36845      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36846      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36847      */
36848     nanText : "{0} is not a valid number",
36849
36850     // private
36851     initEvents : function(){
36852         Roo.form.NumberField.superclass.initEvents.call(this);
36853         var allowed = "0123456789";
36854         if(this.allowDecimals){
36855             allowed += this.decimalSeparator;
36856         }
36857         if(this.allowNegative){
36858             allowed += "-";
36859         }
36860         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36861         var keyPress = function(e){
36862             var k = e.getKey();
36863             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36864                 return;
36865             }
36866             var c = e.getCharCode();
36867             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36868                 e.stopEvent();
36869             }
36870         };
36871         this.el.on("keypress", keyPress, this);
36872     },
36873
36874     // private
36875     validateValue : function(value){
36876         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36877             return false;
36878         }
36879         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36880              return true;
36881         }
36882         var num = this.parseValue(value);
36883         if(isNaN(num)){
36884             this.markInvalid(String.format(this.nanText, value));
36885             return false;
36886         }
36887         if(num < this.minValue){
36888             this.markInvalid(String.format(this.minText, this.minValue));
36889             return false;
36890         }
36891         if(num > this.maxValue){
36892             this.markInvalid(String.format(this.maxText, this.maxValue));
36893             return false;
36894         }
36895         return true;
36896     },
36897
36898     getValue : function(){
36899         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36900     },
36901
36902     // private
36903     parseValue : function(value){
36904         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36905         return isNaN(value) ? '' : value;
36906     },
36907
36908     // private
36909     fixPrecision : function(value){
36910         var nan = isNaN(value);
36911         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36912             return nan ? '' : value;
36913         }
36914         return parseFloat(value).toFixed(this.decimalPrecision);
36915     },
36916
36917     setValue : function(v){
36918         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36919     },
36920
36921     // private
36922     decimalPrecisionFcn : function(v){
36923         return Math.floor(v);
36924     },
36925
36926     beforeBlur : function(){
36927         var v = this.parseValue(this.getRawValue());
36928         if(v){
36929             this.setValue(this.fixPrecision(v));
36930         }
36931     }
36932 });/*
36933  * Based on:
36934  * Ext JS Library 1.1.1
36935  * Copyright(c) 2006-2007, Ext JS, LLC.
36936  *
36937  * Originally Released Under LGPL - original licence link has changed is not relivant.
36938  *
36939  * Fork - LGPL
36940  * <script type="text/javascript">
36941  */
36942  
36943 /**
36944  * @class Roo.form.DateField
36945  * @extends Roo.form.TriggerField
36946  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36947 * @constructor
36948 * Create a new DateField
36949 * @param {Object} config
36950  */
36951 Roo.form.DateField = function(config){
36952     Roo.form.DateField.superclass.constructor.call(this, config);
36953     
36954       this.addEvents({
36955          
36956         /**
36957          * @event select
36958          * Fires when a date is selected
36959              * @param {Roo.form.DateField} combo This combo box
36960              * @param {Date} date The date selected
36961              */
36962         'select' : true
36963          
36964     });
36965     
36966     
36967     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36968     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36969     this.ddMatch = null;
36970     if(this.disabledDates){
36971         var dd = this.disabledDates;
36972         var re = "(?:";
36973         for(var i = 0; i < dd.length; i++){
36974             re += dd[i];
36975             if(i != dd.length-1) re += "|";
36976         }
36977         this.ddMatch = new RegExp(re + ")");
36978     }
36979 };
36980
36981 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36982     /**
36983      * @cfg {String} format
36984      * The default date format string which can be overriden for localization support.  The format must be
36985      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36986      */
36987     format : "m/d/y",
36988     /**
36989      * @cfg {String} altFormats
36990      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36991      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36992      */
36993     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36994     /**
36995      * @cfg {Array} disabledDays
36996      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36997      */
36998     disabledDays : null,
36999     /**
37000      * @cfg {String} disabledDaysText
37001      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37002      */
37003     disabledDaysText : "Disabled",
37004     /**
37005      * @cfg {Array} disabledDates
37006      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37007      * expression so they are very powerful. Some examples:
37008      * <ul>
37009      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37010      * <li>["03/08", "09/16"] would disable those days for every year</li>
37011      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37012      * <li>["03/../2006"] would disable every day in March 2006</li>
37013      * <li>["^03"] would disable every day in every March</li>
37014      * </ul>
37015      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37016      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37017      */
37018     disabledDates : null,
37019     /**
37020      * @cfg {String} disabledDatesText
37021      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37022      */
37023     disabledDatesText : "Disabled",
37024     /**
37025      * @cfg {Date/String} minValue
37026      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37027      * valid format (defaults to null).
37028      */
37029     minValue : null,
37030     /**
37031      * @cfg {Date/String} maxValue
37032      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37033      * valid format (defaults to null).
37034      */
37035     maxValue : null,
37036     /**
37037      * @cfg {String} minText
37038      * The error text to display when the date in the cell is before minValue (defaults to
37039      * 'The date in this field must be after {minValue}').
37040      */
37041     minText : "The date in this field must be equal to or after {0}",
37042     /**
37043      * @cfg {String} maxText
37044      * The error text to display when the date in the cell is after maxValue (defaults to
37045      * 'The date in this field must be before {maxValue}').
37046      */
37047     maxText : "The date in this field must be equal to or before {0}",
37048     /**
37049      * @cfg {String} invalidText
37050      * The error text to display when the date in the field is invalid (defaults to
37051      * '{value} is not a valid date - it must be in the format {format}').
37052      */
37053     invalidText : "{0} is not a valid date - it must be in the format {1}",
37054     /**
37055      * @cfg {String} triggerClass
37056      * An additional CSS class used to style the trigger button.  The trigger will always get the
37057      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37058      * which displays a calendar icon).
37059      */
37060     triggerClass : 'x-form-date-trigger',
37061     
37062
37063     /**
37064      * @cfg {bool} useIso
37065      * if enabled, then the date field will use a hidden field to store the 
37066      * real value as iso formated date. default (false)
37067      */ 
37068     useIso : false,
37069     /**
37070      * @cfg {String/Object} autoCreate
37071      * A DomHelper element spec, or true for a default element spec (defaults to
37072      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37073      */ 
37074     // private
37075     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37076     
37077     // private
37078     hiddenField: false,
37079     
37080     onRender : function(ct, position)
37081     {
37082         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37083         if (this.useIso) {
37084             this.el.dom.removeAttribute('name'); 
37085             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37086                     'before', true);
37087             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37088             // prevent input submission
37089             this.hiddenName = this.name;
37090         }
37091             
37092             
37093     },
37094     
37095     // private
37096     validateValue : function(value)
37097     {
37098         value = this.formatDate(value);
37099         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37100             return false;
37101         }
37102         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37103              return true;
37104         }
37105         var svalue = value;
37106         value = this.parseDate(value);
37107         if(!value){
37108             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37109             return false;
37110         }
37111         var time = value.getTime();
37112         if(this.minValue && time < this.minValue.getTime()){
37113             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37114             return false;
37115         }
37116         if(this.maxValue && time > this.maxValue.getTime()){
37117             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37118             return false;
37119         }
37120         if(this.disabledDays){
37121             var day = value.getDay();
37122             for(var i = 0; i < this.disabledDays.length; i++) {
37123                 if(day === this.disabledDays[i]){
37124                     this.markInvalid(this.disabledDaysText);
37125                     return false;
37126                 }
37127             }
37128         }
37129         var fvalue = this.formatDate(value);
37130         if(this.ddMatch && this.ddMatch.test(fvalue)){
37131             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37132             return false;
37133         }
37134         return true;
37135     },
37136
37137     // private
37138     // Provides logic to override the default TriggerField.validateBlur which just returns true
37139     validateBlur : function(){
37140         return !this.menu || !this.menu.isVisible();
37141     },
37142
37143     /**
37144      * Returns the current date value of the date field.
37145      * @return {Date} The date value
37146      */
37147     getValue : function(){
37148         
37149         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37150     },
37151
37152     /**
37153      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37154      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37155      * (the default format used is "m/d/y").
37156      * <br />Usage:
37157      * <pre><code>
37158 //All of these calls set the same date value (May 4, 2006)
37159
37160 //Pass a date object:
37161 var dt = new Date('5/4/06');
37162 dateField.setValue(dt);
37163
37164 //Pass a date string (default format):
37165 dateField.setValue('5/4/06');
37166
37167 //Pass a date string (custom format):
37168 dateField.format = 'Y-m-d';
37169 dateField.setValue('2006-5-4');
37170 </code></pre>
37171      * @param {String/Date} date The date or valid date string
37172      */
37173     setValue : function(date){
37174         if (this.hiddenField) {
37175             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37176         }
37177         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37178     },
37179
37180     // private
37181     parseDate : function(value){
37182         if(!value || value instanceof Date){
37183             return value;
37184         }
37185         var v = Date.parseDate(value, this.format);
37186         if(!v && this.altFormats){
37187             if(!this.altFormatsArray){
37188                 this.altFormatsArray = this.altFormats.split("|");
37189             }
37190             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37191                 v = Date.parseDate(value, this.altFormatsArray[i]);
37192             }
37193         }
37194         return v;
37195     },
37196
37197     // private
37198     formatDate : function(date, fmt){
37199         return (!date || !(date instanceof Date)) ?
37200                date : date.dateFormat(fmt || this.format);
37201     },
37202
37203     // private
37204     menuListeners : {
37205         select: function(m, d){
37206             this.setValue(d);
37207             this.fireEvent('select', this, d);
37208         },
37209         show : function(){ // retain focus styling
37210             this.onFocus();
37211         },
37212         hide : function(){
37213             this.focus.defer(10, this);
37214             var ml = this.menuListeners;
37215             this.menu.un("select", ml.select,  this);
37216             this.menu.un("show", ml.show,  this);
37217             this.menu.un("hide", ml.hide,  this);
37218         }
37219     },
37220
37221     // private
37222     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37223     onTriggerClick : function(){
37224         if(this.disabled){
37225             return;
37226         }
37227         if(this.menu == null){
37228             this.menu = new Roo.menu.DateMenu();
37229         }
37230         Roo.apply(this.menu.picker,  {
37231             showClear: this.allowBlank,
37232             minDate : this.minValue,
37233             maxDate : this.maxValue,
37234             disabledDatesRE : this.ddMatch,
37235             disabledDatesText : this.disabledDatesText,
37236             disabledDays : this.disabledDays,
37237             disabledDaysText : this.disabledDaysText,
37238             format : this.format,
37239             minText : String.format(this.minText, this.formatDate(this.minValue)),
37240             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37241         });
37242         this.menu.on(Roo.apply({}, this.menuListeners, {
37243             scope:this
37244         }));
37245         this.menu.picker.setValue(this.getValue() || new Date());
37246         this.menu.show(this.el, "tl-bl?");
37247     },
37248
37249     beforeBlur : function(){
37250         var v = this.parseDate(this.getRawValue());
37251         if(v){
37252             this.setValue(v);
37253         }
37254     }
37255
37256     /** @cfg {Boolean} grow @hide */
37257     /** @cfg {Number} growMin @hide */
37258     /** @cfg {Number} growMax @hide */
37259     /**
37260      * @hide
37261      * @method autoSize
37262      */
37263 });/*
37264  * Based on:
37265  * Ext JS Library 1.1.1
37266  * Copyright(c) 2006-2007, Ext JS, LLC.
37267  *
37268  * Originally Released Under LGPL - original licence link has changed is not relivant.
37269  *
37270  * Fork - LGPL
37271  * <script type="text/javascript">
37272  */
37273  
37274
37275 /**
37276  * @class Roo.form.ComboBox
37277  * @extends Roo.form.TriggerField
37278  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37279  * @constructor
37280  * Create a new ComboBox.
37281  * @param {Object} config Configuration options
37282  */
37283 Roo.form.ComboBox = function(config){
37284     Roo.form.ComboBox.superclass.constructor.call(this, config);
37285     this.addEvents({
37286         /**
37287          * @event expand
37288          * Fires when the dropdown list is expanded
37289              * @param {Roo.form.ComboBox} combo This combo box
37290              */
37291         'expand' : true,
37292         /**
37293          * @event collapse
37294          * Fires when the dropdown list is collapsed
37295              * @param {Roo.form.ComboBox} combo This combo box
37296              */
37297         'collapse' : true,
37298         /**
37299          * @event beforeselect
37300          * Fires before a list item is selected. Return false to cancel the selection.
37301              * @param {Roo.form.ComboBox} combo This combo box
37302              * @param {Roo.data.Record} record The data record returned from the underlying store
37303              * @param {Number} index The index of the selected item in the dropdown list
37304              */
37305         'beforeselect' : true,
37306         /**
37307          * @event select
37308          * Fires when a list item is selected
37309              * @param {Roo.form.ComboBox} combo This combo box
37310              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37311              * @param {Number} index The index of the selected item in the dropdown list
37312              */
37313         'select' : true,
37314         /**
37315          * @event beforequery
37316          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37317          * The event object passed has these properties:
37318              * @param {Roo.form.ComboBox} combo This combo box
37319              * @param {String} query The query
37320              * @param {Boolean} forceAll true to force "all" query
37321              * @param {Boolean} cancel true to cancel the query
37322              * @param {Object} e The query event object
37323              */
37324         'beforequery': true,
37325          /**
37326          * @event add
37327          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37328              * @param {Roo.form.ComboBox} combo This combo box
37329              */
37330         'add' : true,
37331         /**
37332          * @event edit
37333          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37334              * @param {Roo.form.ComboBox} combo This combo box
37335              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37336              */
37337         'edit' : true
37338         
37339         
37340     });
37341     if(this.transform){
37342         this.allowDomMove = false;
37343         var s = Roo.getDom(this.transform);
37344         if(!this.hiddenName){
37345             this.hiddenName = s.name;
37346         }
37347         if(!this.store){
37348             this.mode = 'local';
37349             var d = [], opts = s.options;
37350             for(var i = 0, len = opts.length;i < len; i++){
37351                 var o = opts[i];
37352                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37353                 if(o.selected) {
37354                     this.value = value;
37355                 }
37356                 d.push([value, o.text]);
37357             }
37358             this.store = new Roo.data.SimpleStore({
37359                 'id': 0,
37360                 fields: ['value', 'text'],
37361                 data : d
37362             });
37363             this.valueField = 'value';
37364             this.displayField = 'text';
37365         }
37366         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37367         if(!this.lazyRender){
37368             this.target = true;
37369             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37370             s.parentNode.removeChild(s); // remove it
37371             this.render(this.el.parentNode);
37372         }else{
37373             s.parentNode.removeChild(s); // remove it
37374         }
37375
37376     }
37377     if (this.store) {
37378         this.store = Roo.factory(this.store, Roo.data);
37379     }
37380     
37381     this.selectedIndex = -1;
37382     if(this.mode == 'local'){
37383         if(config.queryDelay === undefined){
37384             this.queryDelay = 10;
37385         }
37386         if(config.minChars === undefined){
37387             this.minChars = 0;
37388         }
37389     }
37390 };
37391
37392 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37393     /**
37394      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37395      */
37396     /**
37397      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37398      * rendering into an Roo.Editor, defaults to false)
37399      */
37400     /**
37401      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37402      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37403      */
37404     /**
37405      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37406      */
37407     /**
37408      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37409      * the dropdown list (defaults to undefined, with no header element)
37410      */
37411
37412      /**
37413      * @cfg {String/Roo.Template} tpl The template to use to render the output
37414      */
37415      
37416     // private
37417     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37418     /**
37419      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37420      */
37421     listWidth: undefined,
37422     /**
37423      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37424      * mode = 'remote' or 'text' if mode = 'local')
37425      */
37426     displayField: undefined,
37427     /**
37428      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37429      * mode = 'remote' or 'value' if mode = 'local'). 
37430      * Note: use of a valueField requires the user make a selection
37431      * in order for a value to be mapped.
37432      */
37433     valueField: undefined,
37434     
37435     
37436     /**
37437      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37438      * field's data value (defaults to the underlying DOM element's name)
37439      */
37440     hiddenName: undefined,
37441     /**
37442      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37443      */
37444     listClass: '',
37445     /**
37446      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37447      */
37448     selectedClass: 'x-combo-selected',
37449     /**
37450      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37451      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37452      * which displays a downward arrow icon).
37453      */
37454     triggerClass : 'x-form-arrow-trigger',
37455     /**
37456      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37457      */
37458     shadow:'sides',
37459     /**
37460      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37461      * anchor positions (defaults to 'tl-bl')
37462      */
37463     listAlign: 'tl-bl?',
37464     /**
37465      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37466      */
37467     maxHeight: 300,
37468     /**
37469      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37470      * query specified by the allQuery config option (defaults to 'query')
37471      */
37472     triggerAction: 'query',
37473     /**
37474      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37475      * (defaults to 4, does not apply if editable = false)
37476      */
37477     minChars : 4,
37478     /**
37479      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37480      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37481      */
37482     typeAhead: false,
37483     /**
37484      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37485      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37486      */
37487     queryDelay: 500,
37488     /**
37489      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37490      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37491      */
37492     pageSize: 0,
37493     /**
37494      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37495      * when editable = true (defaults to false)
37496      */
37497     selectOnFocus:false,
37498     /**
37499      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37500      */
37501     queryParam: 'query',
37502     /**
37503      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37504      * when mode = 'remote' (defaults to 'Loading...')
37505      */
37506     loadingText: 'Loading...',
37507     /**
37508      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37509      */
37510     resizable: false,
37511     /**
37512      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37513      */
37514     handleHeight : 8,
37515     /**
37516      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37517      * traditional select (defaults to true)
37518      */
37519     editable: true,
37520     /**
37521      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37522      */
37523     allQuery: '',
37524     /**
37525      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37526      */
37527     mode: 'remote',
37528     /**
37529      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37530      * listWidth has a higher value)
37531      */
37532     minListWidth : 70,
37533     /**
37534      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37535      * allow the user to set arbitrary text into the field (defaults to false)
37536      */
37537     forceSelection:false,
37538     /**
37539      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37540      * if typeAhead = true (defaults to 250)
37541      */
37542     typeAheadDelay : 250,
37543     /**
37544      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37545      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37546      */
37547     valueNotFoundText : undefined,
37548     /**
37549      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37550      */
37551     blockFocus : false,
37552     
37553     /**
37554      * @cfg {Boolean} disableClear Disable showing of clear button.
37555      */
37556     disableClear : false,
37557     /**
37558      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37559      */
37560     alwaysQuery : false,
37561     
37562     //private
37563     addicon : false,
37564     editicon: false,
37565     
37566     // element that contains real text value.. (when hidden is used..)
37567      
37568     // private
37569     onRender : function(ct, position){
37570         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37571         if(this.hiddenName){
37572             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37573                     'before', true);
37574             this.hiddenField.value =
37575                 this.hiddenValue !== undefined ? this.hiddenValue :
37576                 this.value !== undefined ? this.value : '';
37577
37578             // prevent input submission
37579             this.el.dom.removeAttribute('name');
37580              
37581              
37582         }
37583         if(Roo.isGecko){
37584             this.el.dom.setAttribute('autocomplete', 'off');
37585         }
37586
37587         var cls = 'x-combo-list';
37588
37589         this.list = new Roo.Layer({
37590             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37591         });
37592
37593         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37594         this.list.setWidth(lw);
37595         this.list.swallowEvent('mousewheel');
37596         this.assetHeight = 0;
37597
37598         if(this.title){
37599             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37600             this.assetHeight += this.header.getHeight();
37601         }
37602
37603         this.innerList = this.list.createChild({cls:cls+'-inner'});
37604         this.innerList.on('mouseover', this.onViewOver, this);
37605         this.innerList.on('mousemove', this.onViewMove, this);
37606         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37607         
37608         if(this.allowBlank && !this.pageSize && !this.disableClear){
37609             this.footer = this.list.createChild({cls:cls+'-ft'});
37610             this.pageTb = new Roo.Toolbar(this.footer);
37611            
37612         }
37613         if(this.pageSize){
37614             this.footer = this.list.createChild({cls:cls+'-ft'});
37615             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37616                     {pageSize: this.pageSize});
37617             
37618         }
37619         
37620         if (this.pageTb && this.allowBlank && !this.disableClear) {
37621             var _this = this;
37622             this.pageTb.add(new Roo.Toolbar.Fill(), {
37623                 cls: 'x-btn-icon x-btn-clear',
37624                 text: '&#160;',
37625                 handler: function()
37626                 {
37627                     _this.collapse();
37628                     _this.clearValue();
37629                     _this.onSelect(false, -1);
37630                 }
37631             });
37632         }
37633         if (this.footer) {
37634             this.assetHeight += this.footer.getHeight();
37635         }
37636         
37637
37638         if(!this.tpl){
37639             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37640         }
37641
37642         this.view = new Roo.View(this.innerList, this.tpl, {
37643             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37644         });
37645
37646         this.view.on('click', this.onViewClick, this);
37647
37648         this.store.on('beforeload', this.onBeforeLoad, this);
37649         this.store.on('load', this.onLoad, this);
37650         this.store.on('loadexception', this.onLoadException, this);
37651
37652         if(this.resizable){
37653             this.resizer = new Roo.Resizable(this.list,  {
37654                pinned:true, handles:'se'
37655             });
37656             this.resizer.on('resize', function(r, w, h){
37657                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37658                 this.listWidth = w;
37659                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37660                 this.restrictHeight();
37661             }, this);
37662             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37663         }
37664         if(!this.editable){
37665             this.editable = true;
37666             this.setEditable(false);
37667         }  
37668         
37669         
37670         if (typeof(this.events.add.listeners) != 'undefined') {
37671             
37672             this.addicon = this.wrap.createChild(
37673                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37674        
37675             this.addicon.on('click', function(e) {
37676                 this.fireEvent('add', this);
37677             }, this);
37678         }
37679         if (typeof(this.events.edit.listeners) != 'undefined') {
37680             
37681             this.editicon = this.wrap.createChild(
37682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37683             if (this.addicon) {
37684                 this.editicon.setStyle('margin-left', '40px');
37685             }
37686             this.editicon.on('click', function(e) {
37687                 
37688                 // we fire even  if inothing is selected..
37689                 this.fireEvent('edit', this, this.lastData );
37690                 
37691             }, this);
37692         }
37693         
37694         
37695         
37696     },
37697
37698     // private
37699     initEvents : function(){
37700         Roo.form.ComboBox.superclass.initEvents.call(this);
37701
37702         this.keyNav = new Roo.KeyNav(this.el, {
37703             "up" : function(e){
37704                 this.inKeyMode = true;
37705                 this.selectPrev();
37706             },
37707
37708             "down" : function(e){
37709                 if(!this.isExpanded()){
37710                     this.onTriggerClick();
37711                 }else{
37712                     this.inKeyMode = true;
37713                     this.selectNext();
37714                 }
37715             },
37716
37717             "enter" : function(e){
37718                 this.onViewClick();
37719                 //return true;
37720             },
37721
37722             "esc" : function(e){
37723                 this.collapse();
37724             },
37725
37726             "tab" : function(e){
37727                 this.onViewClick(false);
37728                 this.fireEvent("specialkey", this, e);
37729                 return true;
37730             },
37731
37732             scope : this,
37733
37734             doRelay : function(foo, bar, hname){
37735                 if(hname == 'down' || this.scope.isExpanded()){
37736                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37737                 }
37738                 return true;
37739             },
37740
37741             forceKeyDown: true
37742         });
37743         this.queryDelay = Math.max(this.queryDelay || 10,
37744                 this.mode == 'local' ? 10 : 250);
37745         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37746         if(this.typeAhead){
37747             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37748         }
37749         if(this.editable !== false){
37750             this.el.on("keyup", this.onKeyUp, this);
37751         }
37752         if(this.forceSelection){
37753             this.on('blur', this.doForce, this);
37754         }
37755     },
37756
37757     onDestroy : function(){
37758         if(this.view){
37759             this.view.setStore(null);
37760             this.view.el.removeAllListeners();
37761             this.view.el.remove();
37762             this.view.purgeListeners();
37763         }
37764         if(this.list){
37765             this.list.destroy();
37766         }
37767         if(this.store){
37768             this.store.un('beforeload', this.onBeforeLoad, this);
37769             this.store.un('load', this.onLoad, this);
37770             this.store.un('loadexception', this.onLoadException, this);
37771         }
37772         Roo.form.ComboBox.superclass.onDestroy.call(this);
37773     },
37774
37775     // private
37776     fireKey : function(e){
37777         if(e.isNavKeyPress() && !this.list.isVisible()){
37778             this.fireEvent("specialkey", this, e);
37779         }
37780     },
37781
37782     // private
37783     onResize: function(w, h){
37784         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37785         
37786         if(typeof w != 'number'){
37787             // we do not handle it!?!?
37788             return;
37789         }
37790         var tw = this.trigger.getWidth();
37791         tw += this.addicon ? this.addicon.getWidth() : 0;
37792         tw += this.editicon ? this.editicon.getWidth() : 0;
37793         var x = w - tw;
37794         this.el.setWidth( this.adjustWidth('input', x));
37795             
37796         this.trigger.setStyle('left', x+'px');
37797         
37798         if(this.list && this.listWidth === undefined){
37799             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37800             this.list.setWidth(lw);
37801             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37802         }
37803         
37804     
37805         
37806     },
37807
37808     /**
37809      * Allow or prevent the user from directly editing the field text.  If false is passed,
37810      * the user will only be able to select from the items defined in the dropdown list.  This method
37811      * is the runtime equivalent of setting the 'editable' config option at config time.
37812      * @param {Boolean} value True to allow the user to directly edit the field text
37813      */
37814     setEditable : function(value){
37815         if(value == this.editable){
37816             return;
37817         }
37818         this.editable = value;
37819         if(!value){
37820             this.el.dom.setAttribute('readOnly', true);
37821             this.el.on('mousedown', this.onTriggerClick,  this);
37822             this.el.addClass('x-combo-noedit');
37823         }else{
37824             this.el.dom.setAttribute('readOnly', false);
37825             this.el.un('mousedown', this.onTriggerClick,  this);
37826             this.el.removeClass('x-combo-noedit');
37827         }
37828     },
37829
37830     // private
37831     onBeforeLoad : function(){
37832         if(!this.hasFocus){
37833             return;
37834         }
37835         this.innerList.update(this.loadingText ?
37836                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37837         this.restrictHeight();
37838         this.selectedIndex = -1;
37839     },
37840
37841     // private
37842     onLoad : function(){
37843         if(!this.hasFocus){
37844             return;
37845         }
37846         if(this.store.getCount() > 0){
37847             this.expand();
37848             this.restrictHeight();
37849             if(this.lastQuery == this.allQuery){
37850                 if(this.editable){
37851                     this.el.dom.select();
37852                 }
37853                 if(!this.selectByValue(this.value, true)){
37854                     this.select(0, true);
37855                 }
37856             }else{
37857                 this.selectNext();
37858                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37859                     this.taTask.delay(this.typeAheadDelay);
37860                 }
37861             }
37862         }else{
37863             this.onEmptyResults();
37864         }
37865         //this.el.focus();
37866     },
37867     // private
37868     onLoadException : function()
37869     {
37870         this.collapse();
37871         Roo.log(this.store.reader.jsonData);
37872         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37873             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37874         }
37875         
37876         
37877     },
37878     // private
37879     onTypeAhead : function(){
37880         if(this.store.getCount() > 0){
37881             var r = this.store.getAt(0);
37882             var newValue = r.data[this.displayField];
37883             var len = newValue.length;
37884             var selStart = this.getRawValue().length;
37885             if(selStart != len){
37886                 this.setRawValue(newValue);
37887                 this.selectText(selStart, newValue.length);
37888             }
37889         }
37890     },
37891
37892     // private
37893     onSelect : function(record, index){
37894         if(this.fireEvent('beforeselect', this, record, index) !== false){
37895             this.setFromData(index > -1 ? record.data : false);
37896             this.collapse();
37897             this.fireEvent('select', this, record, index);
37898         }
37899     },
37900
37901     /**
37902      * Returns the currently selected field value or empty string if no value is set.
37903      * @return {String} value The selected value
37904      */
37905     getValue : function(){
37906         if(this.valueField){
37907             return typeof this.value != 'undefined' ? this.value : '';
37908         }else{
37909             return Roo.form.ComboBox.superclass.getValue.call(this);
37910         }
37911     },
37912
37913     /**
37914      * Clears any text/value currently set in the field
37915      */
37916     clearValue : function(){
37917         if(this.hiddenField){
37918             this.hiddenField.value = '';
37919         }
37920         this.value = '';
37921         this.setRawValue('');
37922         this.lastSelectionText = '';
37923         this.applyEmptyText();
37924     },
37925
37926     /**
37927      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37928      * will be displayed in the field.  If the value does not match the data value of an existing item,
37929      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37930      * Otherwise the field will be blank (although the value will still be set).
37931      * @param {String} value The value to match
37932      */
37933     setValue : function(v){
37934         var text = v;
37935         if(this.valueField){
37936             var r = this.findRecord(this.valueField, v);
37937             if(r){
37938                 text = r.data[this.displayField];
37939             }else if(this.valueNotFoundText !== undefined){
37940                 text = this.valueNotFoundText;
37941             }
37942         }
37943         this.lastSelectionText = text;
37944         if(this.hiddenField){
37945             this.hiddenField.value = v;
37946         }
37947         Roo.form.ComboBox.superclass.setValue.call(this, text);
37948         this.value = v;
37949     },
37950     /**
37951      * @property {Object} the last set data for the element
37952      */
37953     
37954     lastData : false,
37955     /**
37956      * Sets the value of the field based on a object which is related to the record format for the store.
37957      * @param {Object} value the value to set as. or false on reset?
37958      */
37959     setFromData : function(o){
37960         var dv = ''; // display value
37961         var vv = ''; // value value..
37962         this.lastData = o;
37963         if (this.displayField) {
37964             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37965         } else {
37966             // this is an error condition!!!
37967             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37968         }
37969         
37970         if(this.valueField){
37971             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37972         }
37973         if(this.hiddenField){
37974             this.hiddenField.value = vv;
37975             
37976             this.lastSelectionText = dv;
37977             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37978             this.value = vv;
37979             return;
37980         }
37981         // no hidden field.. - we store the value in 'value', but still display
37982         // display field!!!!
37983         this.lastSelectionText = dv;
37984         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37985         this.value = vv;
37986         
37987         
37988     },
37989     // private
37990     reset : function(){
37991         // overridden so that last data is reset..
37992         this.setValue(this.originalValue);
37993         this.clearInvalid();
37994         this.lastData = false;
37995     },
37996     // private
37997     findRecord : function(prop, value){
37998         var record;
37999         if(this.store.getCount() > 0){
38000             this.store.each(function(r){
38001                 if(r.data[prop] == value){
38002                     record = r;
38003                     return false;
38004                 }
38005                 return true;
38006             });
38007         }
38008         return record;
38009     },
38010     
38011     getName: function()
38012     {
38013         // returns hidden if it's set..
38014         if (!this.rendered) {return ''};
38015         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38016         
38017     },
38018     // private
38019     onViewMove : function(e, t){
38020         this.inKeyMode = false;
38021     },
38022
38023     // private
38024     onViewOver : function(e, t){
38025         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38026             return;
38027         }
38028         var item = this.view.findItemFromChild(t);
38029         if(item){
38030             var index = this.view.indexOf(item);
38031             this.select(index, false);
38032         }
38033     },
38034
38035     // private
38036     onViewClick : function(doFocus)
38037     {
38038         var index = this.view.getSelectedIndexes()[0];
38039         var r = this.store.getAt(index);
38040         if(r){
38041             this.onSelect(r, index);
38042         }
38043         if(doFocus !== false && !this.blockFocus){
38044             this.el.focus();
38045         }
38046     },
38047
38048     // private
38049     restrictHeight : function(){
38050         this.innerList.dom.style.height = '';
38051         var inner = this.innerList.dom;
38052         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38053         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38054         this.list.beginUpdate();
38055         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38056         this.list.alignTo(this.el, this.listAlign);
38057         this.list.endUpdate();
38058     },
38059
38060     // private
38061     onEmptyResults : function(){
38062         this.collapse();
38063     },
38064
38065     /**
38066      * Returns true if the dropdown list is expanded, else false.
38067      */
38068     isExpanded : function(){
38069         return this.list.isVisible();
38070     },
38071
38072     /**
38073      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38074      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38075      * @param {String} value The data value of the item to select
38076      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38077      * selected item if it is not currently in view (defaults to true)
38078      * @return {Boolean} True if the value matched an item in the list, else false
38079      */
38080     selectByValue : function(v, scrollIntoView){
38081         if(v !== undefined && v !== null){
38082             var r = this.findRecord(this.valueField || this.displayField, v);
38083             if(r){
38084                 this.select(this.store.indexOf(r), scrollIntoView);
38085                 return true;
38086             }
38087         }
38088         return false;
38089     },
38090
38091     /**
38092      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38093      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38094      * @param {Number} index The zero-based index of the list item to select
38095      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38096      * selected item if it is not currently in view (defaults to true)
38097      */
38098     select : function(index, scrollIntoView){
38099         this.selectedIndex = index;
38100         this.view.select(index);
38101         if(scrollIntoView !== false){
38102             var el = this.view.getNode(index);
38103             if(el){
38104                 this.innerList.scrollChildIntoView(el, false);
38105             }
38106         }
38107     },
38108
38109     // private
38110     selectNext : function(){
38111         var ct = this.store.getCount();
38112         if(ct > 0){
38113             if(this.selectedIndex == -1){
38114                 this.select(0);
38115             }else if(this.selectedIndex < ct-1){
38116                 this.select(this.selectedIndex+1);
38117             }
38118         }
38119     },
38120
38121     // private
38122     selectPrev : function(){
38123         var ct = this.store.getCount();
38124         if(ct > 0){
38125             if(this.selectedIndex == -1){
38126                 this.select(0);
38127             }else if(this.selectedIndex != 0){
38128                 this.select(this.selectedIndex-1);
38129             }
38130         }
38131     },
38132
38133     // private
38134     onKeyUp : function(e){
38135         if(this.editable !== false && !e.isSpecialKey()){
38136             this.lastKey = e.getKey();
38137             this.dqTask.delay(this.queryDelay);
38138         }
38139     },
38140
38141     // private
38142     validateBlur : function(){
38143         return !this.list || !this.list.isVisible();   
38144     },
38145
38146     // private
38147     initQuery : function(){
38148         this.doQuery(this.getRawValue());
38149     },
38150
38151     // private
38152     doForce : function(){
38153         if(this.el.dom.value.length > 0){
38154             this.el.dom.value =
38155                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38156             this.applyEmptyText();
38157         }
38158     },
38159
38160     /**
38161      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38162      * query allowing the query action to be canceled if needed.
38163      * @param {String} query The SQL query to execute
38164      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38165      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38166      * saved in the current store (defaults to false)
38167      */
38168     doQuery : function(q, forceAll){
38169         if(q === undefined || q === null){
38170             q = '';
38171         }
38172         var qe = {
38173             query: q,
38174             forceAll: forceAll,
38175             combo: this,
38176             cancel:false
38177         };
38178         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38179             return false;
38180         }
38181         q = qe.query;
38182         forceAll = qe.forceAll;
38183         if(forceAll === true || (q.length >= this.minChars)){
38184             if(this.lastQuery != q || this.alwaysQuery){
38185                 this.lastQuery = q;
38186                 if(this.mode == 'local'){
38187                     this.selectedIndex = -1;
38188                     if(forceAll){
38189                         this.store.clearFilter();
38190                     }else{
38191                         this.store.filter(this.displayField, q);
38192                     }
38193                     this.onLoad();
38194                 }else{
38195                     this.store.baseParams[this.queryParam] = q;
38196                     this.store.load({
38197                         params: this.getParams(q)
38198                     });
38199                     this.expand();
38200                 }
38201             }else{
38202                 this.selectedIndex = -1;
38203                 this.onLoad();   
38204             }
38205         }
38206     },
38207
38208     // private
38209     getParams : function(q){
38210         var p = {};
38211         //p[this.queryParam] = q;
38212         if(this.pageSize){
38213             p.start = 0;
38214             p.limit = this.pageSize;
38215         }
38216         return p;
38217     },
38218
38219     /**
38220      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38221      */
38222     collapse : function(){
38223         if(!this.isExpanded()){
38224             return;
38225         }
38226         this.list.hide();
38227         Roo.get(document).un('mousedown', this.collapseIf, this);
38228         Roo.get(document).un('mousewheel', this.collapseIf, this);
38229         if (!this.editable) {
38230             Roo.get(document).un('keydown', this.listKeyPress, this);
38231         }
38232         this.fireEvent('collapse', this);
38233     },
38234
38235     // private
38236     collapseIf : function(e){
38237         if(!e.within(this.wrap) && !e.within(this.list)){
38238             this.collapse();
38239         }
38240     },
38241
38242     /**
38243      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38244      */
38245     expand : function(){
38246         if(this.isExpanded() || !this.hasFocus){
38247             return;
38248         }
38249         this.list.alignTo(this.el, this.listAlign);
38250         this.list.show();
38251         Roo.get(document).on('mousedown', this.collapseIf, this);
38252         Roo.get(document).on('mousewheel', this.collapseIf, this);
38253         if (!this.editable) {
38254             Roo.get(document).on('keydown', this.listKeyPress, this);
38255         }
38256         
38257         this.fireEvent('expand', this);
38258     },
38259
38260     // private
38261     // Implements the default empty TriggerField.onTriggerClick function
38262     onTriggerClick : function(){
38263         if(this.disabled){
38264             return;
38265         }
38266         if(this.isExpanded()){
38267             this.collapse();
38268             if (!this.blockFocus) {
38269                 this.el.focus();
38270             }
38271             
38272         }else {
38273             this.hasFocus = true;
38274             if(this.triggerAction == 'all') {
38275                 this.doQuery(this.allQuery, true);
38276             } else {
38277                 this.doQuery(this.getRawValue());
38278             }
38279             if (!this.blockFocus) {
38280                 this.el.focus();
38281             }
38282         }
38283     },
38284     listKeyPress : function(e)
38285     {
38286         //Roo.log('listkeypress');
38287         // scroll to first matching element based on key pres..
38288         if (e.isSpecialKey()) {
38289             return false;
38290         }
38291         var k = String.fromCharCode(e.getKey()).toUpperCase();
38292         //Roo.log(k);
38293         var match  = false;
38294         var csel = this.view.getSelectedNodes();
38295         var cselitem = false;
38296         if (csel.length) {
38297             var ix = this.view.indexOf(csel[0]);
38298             cselitem  = this.store.getAt(ix);
38299             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38300                 cselitem = false;
38301             }
38302             
38303         }
38304         
38305         this.store.each(function(v) { 
38306             if (cselitem) {
38307                 // start at existing selection.
38308                 if (cselitem.id == v.id) {
38309                     cselitem = false;
38310                 }
38311                 return;
38312             }
38313                 
38314             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38315                 match = this.store.indexOf(v);
38316                 return false;
38317             }
38318         }, this);
38319         
38320         if (match === false) {
38321             return true; // no more action?
38322         }
38323         // scroll to?
38324         this.view.select(match);
38325         var sn = Roo.get(this.view.getSelectedNodes()[0])
38326         sn.scrollIntoView(sn.dom.parentNode, false);
38327     }
38328
38329     /** 
38330     * @cfg {Boolean} grow 
38331     * @hide 
38332     */
38333     /** 
38334     * @cfg {Number} growMin 
38335     * @hide 
38336     */
38337     /** 
38338     * @cfg {Number} growMax 
38339     * @hide 
38340     */
38341     /**
38342      * @hide
38343      * @method autoSize
38344      */
38345 });/*
38346  * Based on:
38347  * Ext JS Library 1.1.1
38348  * Copyright(c) 2006-2007, Ext JS, LLC.
38349  *
38350  * Originally Released Under LGPL - original licence link has changed is not relivant.
38351  *
38352  * Fork - LGPL
38353  * <script type="text/javascript">
38354  */
38355 /**
38356  * @class Roo.form.Checkbox
38357  * @extends Roo.form.Field
38358  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38359  * @constructor
38360  * Creates a new Checkbox
38361  * @param {Object} config Configuration options
38362  */
38363 Roo.form.Checkbox = function(config){
38364     Roo.form.Checkbox.superclass.constructor.call(this, config);
38365     this.addEvents({
38366         /**
38367          * @event check
38368          * Fires when the checkbox is checked or unchecked.
38369              * @param {Roo.form.Checkbox} this This checkbox
38370              * @param {Boolean} checked The new checked value
38371              */
38372         check : true
38373     });
38374 };
38375
38376 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38377     /**
38378      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38379      */
38380     focusClass : undefined,
38381     /**
38382      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38383      */
38384     fieldClass: "x-form-field",
38385     /**
38386      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38387      */
38388     checked: false,
38389     /**
38390      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38391      * {tag: "input", type: "checkbox", autocomplete: "off"})
38392      */
38393     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38394     /**
38395      * @cfg {String} boxLabel The text that appears beside the checkbox
38396      */
38397     boxLabel : "",
38398     /**
38399      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38400      */  
38401     inputValue : '1',
38402     /**
38403      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38404      */
38405      valueOff: '0', // value when not checked..
38406
38407     actionMode : 'viewEl', 
38408     //
38409     // private
38410     itemCls : 'x-menu-check-item x-form-item',
38411     groupClass : 'x-menu-group-item',
38412     inputType : 'hidden',
38413     
38414     
38415     inSetChecked: false, // check that we are not calling self...
38416     
38417     inputElement: false, // real input element?
38418     basedOn: false, // ????
38419     
38420     isFormField: true, // not sure where this is needed!!!!
38421
38422     onResize : function(){
38423         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38424         if(!this.boxLabel){
38425             this.el.alignTo(this.wrap, 'c-c');
38426         }
38427     },
38428
38429     initEvents : function(){
38430         Roo.form.Checkbox.superclass.initEvents.call(this);
38431         this.el.on("click", this.onClick,  this);
38432         this.el.on("change", this.onClick,  this);
38433     },
38434
38435
38436     getResizeEl : function(){
38437         return this.wrap;
38438     },
38439
38440     getPositionEl : function(){
38441         return this.wrap;
38442     },
38443
38444     // private
38445     onRender : function(ct, position){
38446         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38447         /*
38448         if(this.inputValue !== undefined){
38449             this.el.dom.value = this.inputValue;
38450         }
38451         */
38452         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38453         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38454         var viewEl = this.wrap.createChild({ 
38455             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38456         this.viewEl = viewEl;   
38457         this.wrap.on('click', this.onClick,  this); 
38458         
38459         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38460         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38461         
38462         
38463         
38464         if(this.boxLabel){
38465             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38466         //    viewEl.on('click', this.onClick,  this); 
38467         }
38468         //if(this.checked){
38469             this.setChecked(this.checked);
38470         //}else{
38471             //this.checked = this.el.dom;
38472         //}
38473
38474     },
38475
38476     // private
38477     initValue : Roo.emptyFn,
38478
38479     /**
38480      * Returns the checked state of the checkbox.
38481      * @return {Boolean} True if checked, else false
38482      */
38483     getValue : function(){
38484         if(this.el){
38485             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38486         }
38487         return this.valueOff;
38488         
38489     },
38490
38491         // private
38492     onClick : function(){ 
38493         this.setChecked(!this.checked);
38494
38495         //if(this.el.dom.checked != this.checked){
38496         //    this.setValue(this.el.dom.checked);
38497        // }
38498     },
38499
38500     /**
38501      * Sets the checked state of the checkbox.
38502      * On is always based on a string comparison between inputValue and the param.
38503      * @param {Boolean/String} value - the value to set 
38504      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38505      */
38506     setValue : function(v,suppressEvent){
38507         
38508         
38509         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38510         //if(this.el && this.el.dom){
38511         //    this.el.dom.checked = this.checked;
38512         //    this.el.dom.defaultChecked = this.checked;
38513         //}
38514         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38515         //this.fireEvent("check", this, this.checked);
38516     },
38517     // private..
38518     setChecked : function(state,suppressEvent)
38519     {
38520         if (this.inSetChecked) {
38521             this.checked = state;
38522             return;
38523         }
38524         
38525     
38526         if(this.wrap){
38527             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38528         }
38529         this.checked = state;
38530         if(suppressEvent !== true){
38531             this.fireEvent('check', this, state);
38532         }
38533         this.inSetChecked = true;
38534         this.el.dom.value = state ? this.inputValue : this.valueOff;
38535         this.inSetChecked = false;
38536         
38537     },
38538     // handle setting of hidden value by some other method!!?!?
38539     setFromHidden: function()
38540     {
38541         if(!this.el){
38542             return;
38543         }
38544         //console.log("SET FROM HIDDEN");
38545         //alert('setFrom hidden');
38546         this.setValue(this.el.dom.value);
38547     },
38548     
38549     onDestroy : function()
38550     {
38551         if(this.viewEl){
38552             Roo.get(this.viewEl).remove();
38553         }
38554          
38555         Roo.form.Checkbox.superclass.onDestroy.call(this);
38556     }
38557
38558 });/*
38559  * Based on:
38560  * Ext JS Library 1.1.1
38561  * Copyright(c) 2006-2007, Ext JS, LLC.
38562  *
38563  * Originally Released Under LGPL - original licence link has changed is not relivant.
38564  *
38565  * Fork - LGPL
38566  * <script type="text/javascript">
38567  */
38568  
38569 /**
38570  * @class Roo.form.Radio
38571  * @extends Roo.form.Checkbox
38572  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38573  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38574  * @constructor
38575  * Creates a new Radio
38576  * @param {Object} config Configuration options
38577  */
38578 Roo.form.Radio = function(){
38579     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38580 };
38581 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38582     inputType: 'radio',
38583
38584     /**
38585      * If this radio is part of a group, it will return the selected value
38586      * @return {String}
38587      */
38588     getGroupValue : function(){
38589         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38590     }
38591 });//<script type="text/javascript">
38592
38593 /*
38594  * Ext JS Library 1.1.1
38595  * Copyright(c) 2006-2007, Ext JS, LLC.
38596  * licensing@extjs.com
38597  * 
38598  * http://www.extjs.com/license
38599  */
38600  
38601  /*
38602   * 
38603   * Known bugs:
38604   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38605   * - IE ? - no idea how much works there.
38606   * 
38607   * 
38608   * 
38609   */
38610  
38611
38612 /**
38613  * @class Ext.form.HtmlEditor
38614  * @extends Ext.form.Field
38615  * Provides a lightweight HTML Editor component.
38616  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38617  * 
38618  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38619  * supported by this editor.</b><br/><br/>
38620  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38621  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38622  */
38623 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38624       /**
38625      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38626      */
38627     toolbars : false,
38628     /**
38629      * @cfg {String} createLinkText The default text for the create link prompt
38630      */
38631     createLinkText : 'Please enter the URL for the link:',
38632     /**
38633      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38634      */
38635     defaultLinkValue : 'http:/'+'/',
38636    
38637      /**
38638      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38639      *                        Roo.resizable.
38640      */
38641     resizable : false,
38642      /**
38643      * @cfg {Number} height (in pixels)
38644      */   
38645     height: 300,
38646    /**
38647      * @cfg {Number} width (in pixels)
38648      */   
38649     width: 500,
38650     
38651     /**
38652      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38653      * 
38654      */
38655     stylesheets: false,
38656     
38657     // id of frame..
38658     frameId: false,
38659     
38660     // private properties
38661     validationEvent : false,
38662     deferHeight: true,
38663     initialized : false,
38664     activated : false,
38665     sourceEditMode : false,
38666     onFocus : Roo.emptyFn,
38667     iframePad:3,
38668     hideMode:'offsets',
38669     
38670     defaultAutoCreate : { // modified by initCompnoent..
38671         tag: "textarea",
38672         style:"width:500px;height:300px;",
38673         autocomplete: "off"
38674     },
38675
38676     // private
38677     initComponent : function(){
38678         this.addEvents({
38679             /**
38680              * @event initialize
38681              * Fires when the editor is fully initialized (including the iframe)
38682              * @param {HtmlEditor} this
38683              */
38684             initialize: true,
38685             /**
38686              * @event activate
38687              * Fires when the editor is first receives the focus. Any insertion must wait
38688              * until after this event.
38689              * @param {HtmlEditor} this
38690              */
38691             activate: true,
38692              /**
38693              * @event beforesync
38694              * Fires before the textarea is updated with content from the editor iframe. Return false
38695              * to cancel the sync.
38696              * @param {HtmlEditor} this
38697              * @param {String} html
38698              */
38699             beforesync: true,
38700              /**
38701              * @event beforepush
38702              * Fires before the iframe editor is updated with content from the textarea. Return false
38703              * to cancel the push.
38704              * @param {HtmlEditor} this
38705              * @param {String} html
38706              */
38707             beforepush: true,
38708              /**
38709              * @event sync
38710              * Fires when the textarea is updated with content from the editor iframe.
38711              * @param {HtmlEditor} this
38712              * @param {String} html
38713              */
38714             sync: true,
38715              /**
38716              * @event push
38717              * Fires when the iframe editor is updated with content from the textarea.
38718              * @param {HtmlEditor} this
38719              * @param {String} html
38720              */
38721             push: true,
38722              /**
38723              * @event editmodechange
38724              * Fires when the editor switches edit modes
38725              * @param {HtmlEditor} this
38726              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38727              */
38728             editmodechange: true,
38729             /**
38730              * @event editorevent
38731              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38732              * @param {HtmlEditor} this
38733              */
38734             editorevent: true
38735         });
38736         this.defaultAutoCreate =  {
38737             tag: "textarea",
38738             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38739             autocomplete: "off"
38740         };
38741     },
38742
38743     /**
38744      * Protected method that will not generally be called directly. It
38745      * is called when the editor creates its toolbar. Override this method if you need to
38746      * add custom toolbar buttons.
38747      * @param {HtmlEditor} editor
38748      */
38749     createToolbar : function(editor){
38750         if (!editor.toolbars || !editor.toolbars.length) {
38751             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38752         }
38753         
38754         for (var i =0 ; i < editor.toolbars.length;i++) {
38755             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38756             editor.toolbars[i].init(editor);
38757         }
38758          
38759         
38760     },
38761
38762     /**
38763      * Protected method that will not generally be called directly. It
38764      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38765      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38766      */
38767     getDocMarkup : function(){
38768         // body styles..
38769         var st = '';
38770         if (this.stylesheets === false) {
38771             
38772             Roo.get(document.head).select('style').each(function(node) {
38773                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38774             });
38775             
38776             Roo.get(document.head).select('link').each(function(node) { 
38777                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38778             });
38779             
38780         } else if (!this.stylesheets.length) {
38781                 // simple..
38782                 st = '<style type="text/css">' +
38783                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38784                    '</style>';
38785         } else {
38786             Roo.each(this.stylesheets, function(s) {
38787                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38788             });
38789             
38790         }
38791         
38792         return '<html><head>' + st  +
38793             //<style type="text/css">' +
38794             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38795             //'</style>' +
38796             ' </head><body></body></html>';
38797     },
38798
38799     // private
38800     onRender : function(ct, position)
38801     {
38802         var _t = this;
38803         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38804         this.el.dom.style.border = '0 none';
38805         this.el.dom.setAttribute('tabIndex', -1);
38806         this.el.addClass('x-hidden');
38807         if(Roo.isIE){ // fix IE 1px bogus margin
38808             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38809         }
38810         this.wrap = this.el.wrap({
38811             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38812         });
38813         
38814         if (this.resizable) {
38815             this.resizeEl = new Roo.Resizable(this.wrap, {
38816                 pinned : true,
38817                 wrap: true,
38818                 dynamic : true,
38819                 minHeight : this.height,
38820                 height: this.height,
38821                 handles : this.resizable,
38822                 width: this.width,
38823                 listeners : {
38824                     resize : function(r, w, h) {
38825                         _t.onResize(w,h); // -something
38826                     }
38827                 }
38828             });
38829             
38830         }
38831
38832         this.frameId = Roo.id();
38833         
38834         this.createToolbar(this);
38835         
38836       
38837         
38838         var iframe = this.wrap.createChild({
38839             tag: 'iframe',
38840             id: this.frameId,
38841             name: this.frameId,
38842             frameBorder : 'no',
38843             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38844         }, this.el
38845         );
38846         
38847        // console.log(iframe);
38848         //this.wrap.dom.appendChild(iframe);
38849
38850         this.iframe = iframe.dom;
38851
38852          this.assignDocWin();
38853         
38854         this.doc.designMode = 'on';
38855        
38856         this.doc.open();
38857         this.doc.write(this.getDocMarkup());
38858         this.doc.close();
38859
38860         
38861         var task = { // must defer to wait for browser to be ready
38862             run : function(){
38863                 //console.log("run task?" + this.doc.readyState);
38864                 this.assignDocWin();
38865                 if(this.doc.body || this.doc.readyState == 'complete'){
38866                     try {
38867                         this.doc.designMode="on";
38868                     } catch (e) {
38869                         return;
38870                     }
38871                     Roo.TaskMgr.stop(task);
38872                     this.initEditor.defer(10, this);
38873                 }
38874             },
38875             interval : 10,
38876             duration:10000,
38877             scope: this
38878         };
38879         Roo.TaskMgr.start(task);
38880
38881         if(!this.width){
38882             this.setSize(this.wrap.getSize());
38883         }
38884         if (this.resizeEl) {
38885             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38886             // should trigger onReize..
38887         }
38888     },
38889
38890     // private
38891     onResize : function(w, h)
38892     {
38893         //Roo.log('resize: ' +w + ',' + h );
38894         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38895         if(this.el && this.iframe){
38896             if(typeof w == 'number'){
38897                 var aw = w - this.wrap.getFrameWidth('lr');
38898                 this.el.setWidth(this.adjustWidth('textarea', aw));
38899                 this.iframe.style.width = aw + 'px';
38900             }
38901             if(typeof h == 'number'){
38902                 var tbh = 0;
38903                 for (var i =0; i < this.toolbars.length;i++) {
38904                     // fixme - ask toolbars for heights?
38905                     tbh += this.toolbars[i].tb.el.getHeight();
38906                     if (this.toolbars[i].footer) {
38907                         tbh += this.toolbars[i].footer.el.getHeight();
38908                     }
38909                 }
38910                 
38911                 
38912                 
38913                 
38914                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38915                 ah -= 5; // knock a few pixes off for look..
38916                 this.el.setHeight(this.adjustWidth('textarea', ah));
38917                 this.iframe.style.height = ah + 'px';
38918                 if(this.doc){
38919                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38920                 }
38921             }
38922         }
38923     },
38924
38925     /**
38926      * Toggles the editor between standard and source edit mode.
38927      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38928      */
38929     toggleSourceEdit : function(sourceEditMode){
38930         
38931         this.sourceEditMode = sourceEditMode === true;
38932         
38933         if(this.sourceEditMode){
38934           
38935             this.syncValue();
38936             this.iframe.className = 'x-hidden';
38937             this.el.removeClass('x-hidden');
38938             this.el.dom.removeAttribute('tabIndex');
38939             this.el.focus();
38940         }else{
38941              
38942             this.pushValue();
38943             this.iframe.className = '';
38944             this.el.addClass('x-hidden');
38945             this.el.dom.setAttribute('tabIndex', -1);
38946             this.deferFocus();
38947         }
38948         this.setSize(this.wrap.getSize());
38949         this.fireEvent('editmodechange', this, this.sourceEditMode);
38950     },
38951
38952     // private used internally
38953     createLink : function(){
38954         var url = prompt(this.createLinkText, this.defaultLinkValue);
38955         if(url && url != 'http:/'+'/'){
38956             this.relayCmd('createlink', url);
38957         }
38958     },
38959
38960     // private (for BoxComponent)
38961     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38962
38963     // private (for BoxComponent)
38964     getResizeEl : function(){
38965         return this.wrap;
38966     },
38967
38968     // private (for BoxComponent)
38969     getPositionEl : function(){
38970         return this.wrap;
38971     },
38972
38973     // private
38974     initEvents : function(){
38975         this.originalValue = this.getValue();
38976     },
38977
38978     /**
38979      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38980      * @method
38981      */
38982     markInvalid : Roo.emptyFn,
38983     /**
38984      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38985      * @method
38986      */
38987     clearInvalid : Roo.emptyFn,
38988
38989     setValue : function(v){
38990         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38991         this.pushValue();
38992     },
38993
38994     /**
38995      * Protected method that will not generally be called directly. If you need/want
38996      * custom HTML cleanup, this is the method you should override.
38997      * @param {String} html The HTML to be cleaned
38998      * return {String} The cleaned HTML
38999      */
39000     cleanHtml : function(html){
39001         html = String(html);
39002         if(html.length > 5){
39003             if(Roo.isSafari){ // strip safari nonsense
39004                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39005             }
39006         }
39007         if(html == '&nbsp;'){
39008             html = '';
39009         }
39010         return html;
39011     },
39012
39013     /**
39014      * Protected method that will not generally be called directly. Syncs the contents
39015      * of the editor iframe with the textarea.
39016      */
39017     syncValue : function(){
39018         if(this.initialized){
39019             var bd = (this.doc.body || this.doc.documentElement);
39020             this.cleanUpPaste();
39021             var html = bd.innerHTML;
39022             if(Roo.isSafari){
39023                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39024                 var m = bs.match(/text-align:(.*?);/i);
39025                 if(m && m[1]){
39026                     html = '<div style="'+m[0]+'">' + html + '</div>';
39027                 }
39028             }
39029             html = this.cleanHtml(html);
39030             if(this.fireEvent('beforesync', this, html) !== false){
39031                 this.el.dom.value = html;
39032                 this.fireEvent('sync', this, html);
39033             }
39034         }
39035     },
39036
39037     /**
39038      * Protected method that will not generally be called directly. Pushes the value of the textarea
39039      * into the iframe editor.
39040      */
39041     pushValue : function(){
39042         if(this.initialized){
39043             var v = this.el.dom.value;
39044             if(v.length < 1){
39045                 v = '&#160;';
39046             }
39047             
39048             if(this.fireEvent('beforepush', this, v) !== false){
39049                 var d = (this.doc.body || this.doc.documentElement);
39050                 d.innerHTML = v;
39051                 this.cleanUpPaste();
39052                 this.el.dom.value = d.innerHTML;
39053                 this.fireEvent('push', this, v);
39054             }
39055         }
39056     },
39057
39058     // private
39059     deferFocus : function(){
39060         this.focus.defer(10, this);
39061     },
39062
39063     // doc'ed in Field
39064     focus : function(){
39065         if(this.win && !this.sourceEditMode){
39066             this.win.focus();
39067         }else{
39068             this.el.focus();
39069         }
39070     },
39071     
39072     assignDocWin: function()
39073     {
39074         var iframe = this.iframe;
39075         
39076          if(Roo.isIE){
39077             this.doc = iframe.contentWindow.document;
39078             this.win = iframe.contentWindow;
39079         } else {
39080             if (!Roo.get(this.frameId)) {
39081                 return;
39082             }
39083             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39084             this.win = Roo.get(this.frameId).dom.contentWindow;
39085         }
39086     },
39087     
39088     // private
39089     initEditor : function(){
39090         //console.log("INIT EDITOR");
39091         this.assignDocWin();
39092         
39093         
39094         
39095         this.doc.designMode="on";
39096         this.doc.open();
39097         this.doc.write(this.getDocMarkup());
39098         this.doc.close();
39099         
39100         var dbody = (this.doc.body || this.doc.documentElement);
39101         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39102         // this copies styles from the containing element into thsi one..
39103         // not sure why we need all of this..
39104         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39105         ss['background-attachment'] = 'fixed'; // w3c
39106         dbody.bgProperties = 'fixed'; // ie
39107         Roo.DomHelper.applyStyles(dbody, ss);
39108         Roo.EventManager.on(this.doc, {
39109             //'mousedown': this.onEditorEvent,
39110             'mouseup': this.onEditorEvent,
39111             'dblclick': this.onEditorEvent,
39112             'click': this.onEditorEvent,
39113             'keyup': this.onEditorEvent,
39114             buffer:100,
39115             scope: this
39116         });
39117         if(Roo.isGecko){
39118             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39119         }
39120         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39121             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39122         }
39123         this.initialized = true;
39124
39125         this.fireEvent('initialize', this);
39126         this.pushValue();
39127     },
39128
39129     // private
39130     onDestroy : function(){
39131         
39132         
39133         
39134         if(this.rendered){
39135             
39136             for (var i =0; i < this.toolbars.length;i++) {
39137                 // fixme - ask toolbars for heights?
39138                 this.toolbars[i].onDestroy();
39139             }
39140             
39141             this.wrap.dom.innerHTML = '';
39142             this.wrap.remove();
39143         }
39144     },
39145
39146     // private
39147     onFirstFocus : function(){
39148         
39149         this.assignDocWin();
39150         
39151         
39152         this.activated = true;
39153         for (var i =0; i < this.toolbars.length;i++) {
39154             this.toolbars[i].onFirstFocus();
39155         }
39156        
39157         if(Roo.isGecko){ // prevent silly gecko errors
39158             this.win.focus();
39159             var s = this.win.getSelection();
39160             if(!s.focusNode || s.focusNode.nodeType != 3){
39161                 var r = s.getRangeAt(0);
39162                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39163                 r.collapse(true);
39164                 this.deferFocus();
39165             }
39166             try{
39167                 this.execCmd('useCSS', true);
39168                 this.execCmd('styleWithCSS', false);
39169             }catch(e){}
39170         }
39171         this.fireEvent('activate', this);
39172     },
39173
39174     // private
39175     adjustFont: function(btn){
39176         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39177         //if(Roo.isSafari){ // safari
39178         //    adjust *= 2;
39179        // }
39180         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39181         if(Roo.isSafari){ // safari
39182             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39183             v =  (v < 10) ? 10 : v;
39184             v =  (v > 48) ? 48 : v;
39185             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39186             
39187         }
39188         
39189         
39190         v = Math.max(1, v+adjust);
39191         
39192         this.execCmd('FontSize', v  );
39193     },
39194
39195     onEditorEvent : function(e){
39196         this.fireEvent('editorevent', this, e);
39197       //  this.updateToolbar();
39198         this.syncValue();
39199     },
39200
39201     insertTag : function(tg)
39202     {
39203         // could be a bit smarter... -> wrap the current selected tRoo..
39204         
39205         this.execCmd("formatblock",   tg);
39206         
39207     },
39208     
39209     insertText : function(txt)
39210     {
39211         
39212         
39213         range = this.createRange();
39214         range.deleteContents();
39215                //alert(Sender.getAttribute('label'));
39216                
39217         range.insertNode(this.doc.createTextNode(txt));
39218     } ,
39219     
39220     // private
39221     relayBtnCmd : function(btn){
39222         this.relayCmd(btn.cmd);
39223     },
39224
39225     /**
39226      * Executes a Midas editor command on the editor document and performs necessary focus and
39227      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39228      * @param {String} cmd The Midas command
39229      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39230      */
39231     relayCmd : function(cmd, value){
39232         this.win.focus();
39233         this.execCmd(cmd, value);
39234         this.fireEvent('editorevent', this);
39235         //this.updateToolbar();
39236         this.deferFocus();
39237     },
39238
39239     /**
39240      * Executes a Midas editor command directly on the editor document.
39241      * For visual commands, you should use {@link #relayCmd} instead.
39242      * <b>This should only be called after the editor is initialized.</b>
39243      * @param {String} cmd The Midas command
39244      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39245      */
39246     execCmd : function(cmd, value){
39247         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39248         this.syncValue();
39249     },
39250
39251    
39252     /**
39253      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39254      * to insert tRoo.
39255      * @param {String} text
39256      */
39257     insertAtCursor : function(text){
39258         if(!this.activated){
39259             return;
39260         }
39261         if(Roo.isIE){
39262             this.win.focus();
39263             var r = this.doc.selection.createRange();
39264             if(r){
39265                 r.collapse(true);
39266                 r.pasteHTML(text);
39267                 this.syncValue();
39268                 this.deferFocus();
39269             }
39270         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39271             this.win.focus();
39272             this.execCmd('InsertHTML', text);
39273             this.deferFocus();
39274         }
39275     },
39276  // private
39277     mozKeyPress : function(e){
39278         if(e.ctrlKey){
39279             var c = e.getCharCode(), cmd;
39280           
39281             if(c > 0){
39282                 c = String.fromCharCode(c).toLowerCase();
39283                 switch(c){
39284                     case 'b':
39285                         cmd = 'bold';
39286                     break;
39287                     case 'i':
39288                         cmd = 'italic';
39289                     break;
39290                     case 'u':
39291                         cmd = 'underline';
39292                     case 'v':
39293                         this.cleanUpPaste.defer(100, this);
39294                         return;
39295                     break;
39296                 }
39297                 if(cmd){
39298                     this.win.focus();
39299                     this.execCmd(cmd);
39300                     this.deferFocus();
39301                     e.preventDefault();
39302                 }
39303                 
39304             }
39305         }
39306     },
39307
39308     // private
39309     fixKeys : function(){ // load time branching for fastest keydown performance
39310         if(Roo.isIE){
39311             return function(e){
39312                 var k = e.getKey(), r;
39313                 if(k == e.TAB){
39314                     e.stopEvent();
39315                     r = this.doc.selection.createRange();
39316                     if(r){
39317                         r.collapse(true);
39318                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39319                         this.deferFocus();
39320                     }
39321                     return;
39322                 }
39323                 
39324                 if(k == e.ENTER){
39325                     r = this.doc.selection.createRange();
39326                     if(r){
39327                         var target = r.parentElement();
39328                         if(!target || target.tagName.toLowerCase() != 'li'){
39329                             e.stopEvent();
39330                             r.pasteHTML('<br />');
39331                             r.collapse(false);
39332                             r.select();
39333                         }
39334                     }
39335                 }
39336                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39337                     this.cleanUpPaste.defer(100, this);
39338                     return;
39339                 }
39340                 
39341                 
39342             };
39343         }else if(Roo.isOpera){
39344             return function(e){
39345                 var k = e.getKey();
39346                 if(k == e.TAB){
39347                     e.stopEvent();
39348                     this.win.focus();
39349                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39350                     this.deferFocus();
39351                 }
39352                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39353                     this.cleanUpPaste.defer(100, this);
39354                     return;
39355                 }
39356                 
39357             };
39358         }else if(Roo.isSafari){
39359             return function(e){
39360                 var k = e.getKey();
39361                 
39362                 if(k == e.TAB){
39363                     e.stopEvent();
39364                     this.execCmd('InsertText','\t');
39365                     this.deferFocus();
39366                     return;
39367                 }
39368                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39369                     this.cleanUpPaste.defer(100, this);
39370                     return;
39371                 }
39372                 
39373              };
39374         }
39375     }(),
39376     
39377     getAllAncestors: function()
39378     {
39379         var p = this.getSelectedNode();
39380         var a = [];
39381         if (!p) {
39382             a.push(p); // push blank onto stack..
39383             p = this.getParentElement();
39384         }
39385         
39386         
39387         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39388             a.push(p);
39389             p = p.parentNode;
39390         }
39391         a.push(this.doc.body);
39392         return a;
39393     },
39394     lastSel : false,
39395     lastSelNode : false,
39396     
39397     
39398     getSelection : function() 
39399     {
39400         this.assignDocWin();
39401         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39402     },
39403     
39404     getSelectedNode: function() 
39405     {
39406         // this may only work on Gecko!!!
39407         
39408         // should we cache this!!!!
39409         
39410         
39411         
39412          
39413         var range = this.createRange(this.getSelection()).cloneRange();
39414         
39415         if (Roo.isIE) {
39416             var parent = range.parentElement();
39417             while (true) {
39418                 var testRange = range.duplicate();
39419                 testRange.moveToElementText(parent);
39420                 if (testRange.inRange(range)) {
39421                     break;
39422                 }
39423                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39424                     break;
39425                 }
39426                 parent = parent.parentElement;
39427             }
39428             return parent;
39429         }
39430         
39431         // is ancestor a text element.
39432         var ac =  range.commonAncestorContainer;
39433         if (ac.nodeType == 3) {
39434             ac = ac.parentNode;
39435         }
39436         
39437         var ar = ac.childNodes;
39438          
39439         var nodes = [];
39440         var other_nodes = [];
39441         var has_other_nodes = false;
39442         for (var i=0;i<ar.length;i++) {
39443             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39444                 continue;
39445             }
39446             // fullly contained node.
39447             
39448             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39449                 nodes.push(ar[i]);
39450                 continue;
39451             }
39452             
39453             // probably selected..
39454             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39455                 other_nodes.push(ar[i]);
39456                 continue;
39457             }
39458             // outer..
39459             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39460                 continue;
39461             }
39462             
39463             
39464             has_other_nodes = true;
39465         }
39466         if (!nodes.length && other_nodes.length) {
39467             nodes= other_nodes;
39468         }
39469         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39470             return false;
39471         }
39472         
39473         return nodes[0];
39474     },
39475     createRange: function(sel)
39476     {
39477         // this has strange effects when using with 
39478         // top toolbar - not sure if it's a great idea.
39479         //this.editor.contentWindow.focus();
39480         if (typeof sel != "undefined") {
39481             try {
39482                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39483             } catch(e) {
39484                 return this.doc.createRange();
39485             }
39486         } else {
39487             return this.doc.createRange();
39488         }
39489     },
39490     getParentElement: function()
39491     {
39492         
39493         this.assignDocWin();
39494         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39495         
39496         var range = this.createRange(sel);
39497          
39498         try {
39499             var p = range.commonAncestorContainer;
39500             while (p.nodeType == 3) { // text node
39501                 p = p.parentNode;
39502             }
39503             return p;
39504         } catch (e) {
39505             return null;
39506         }
39507     
39508     },
39509     /***
39510      *
39511      * Range intersection.. the hard stuff...
39512      *  '-1' = before
39513      *  '0' = hits..
39514      *  '1' = after.
39515      *         [ -- selected range --- ]
39516      *   [fail]                        [fail]
39517      *
39518      *    basically..
39519      *      if end is before start or  hits it. fail.
39520      *      if start is after end or hits it fail.
39521      *
39522      *   if either hits (but other is outside. - then it's not 
39523      *   
39524      *    
39525      **/
39526     
39527     
39528     // @see http://www.thismuchiknow.co.uk/?p=64.
39529     rangeIntersectsNode : function(range, node)
39530     {
39531         var nodeRange = node.ownerDocument.createRange();
39532         try {
39533             nodeRange.selectNode(node);
39534         } catch (e) {
39535             nodeRange.selectNodeContents(node);
39536         }
39537     
39538         var rangeStartRange = range.cloneRange();
39539         rangeStartRange.collapse(true);
39540     
39541         var rangeEndRange = range.cloneRange();
39542         rangeEndRange.collapse(false);
39543     
39544         var nodeStartRange = nodeRange.cloneRange();
39545         nodeStartRange.collapse(true);
39546     
39547         var nodeEndRange = nodeRange.cloneRange();
39548         nodeEndRange.collapse(false);
39549     
39550         return rangeStartRange.compareBoundaryPoints(
39551                  Range.START_TO_START, nodeEndRange) == -1 &&
39552                rangeEndRange.compareBoundaryPoints(
39553                  Range.START_TO_START, nodeStartRange) == 1;
39554         
39555          
39556     },
39557     rangeCompareNode : function(range, node)
39558     {
39559         var nodeRange = node.ownerDocument.createRange();
39560         try {
39561             nodeRange.selectNode(node);
39562         } catch (e) {
39563             nodeRange.selectNodeContents(node);
39564         }
39565         
39566         
39567         range.collapse(true);
39568     
39569         nodeRange.collapse(true);
39570      
39571         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39572         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39573          
39574         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39575         
39576         var nodeIsBefore   =  ss == 1;
39577         var nodeIsAfter    = ee == -1;
39578         
39579         if (nodeIsBefore && nodeIsAfter)
39580             return 0; // outer
39581         if (!nodeIsBefore && nodeIsAfter)
39582             return 1; //right trailed.
39583         
39584         if (nodeIsBefore && !nodeIsAfter)
39585             return 2;  // left trailed.
39586         // fully contined.
39587         return 3;
39588     },
39589
39590     // private? - in a new class?
39591     cleanUpPaste :  function()
39592     {
39593         // cleans up the whole document..
39594       //  console.log('cleanuppaste');
39595         this.cleanUpChildren(this.doc.body);
39596         
39597         
39598     },
39599     cleanUpChildren : function (n)
39600     {
39601         if (!n.childNodes.length) {
39602             return;
39603         }
39604         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39605            this.cleanUpChild(n.childNodes[i]);
39606         }
39607     },
39608     
39609     
39610         
39611     
39612     cleanUpChild : function (node)
39613     {
39614         //console.log(node);
39615         if (node.nodeName == "#text") {
39616             // clean up silly Windows -- stuff?
39617             return; 
39618         }
39619         if (node.nodeName == "#comment") {
39620             node.parentNode.removeChild(node);
39621             // clean up silly Windows -- stuff?
39622             return; 
39623         }
39624         
39625         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39626             // remove node.
39627             node.parentNode.removeChild(node);
39628             return;
39629             
39630         }
39631         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39632             this.cleanUpChildren(node);
39633             // inserts everything just before this node...
39634             while (node.childNodes.length) {
39635                 var cn = node.childNodes[0];
39636                 node.removeChild(cn);
39637                 node.parentNode.insertBefore(cn, node);
39638             }
39639             node.parentNode.removeChild(node);
39640             return;
39641         }
39642         
39643         if (!node.attributes || !node.attributes.length) {
39644             this.cleanUpChildren(node);
39645             return;
39646         }
39647         
39648         function cleanAttr(n,v)
39649         {
39650             
39651             if (v.match(/^\./) || v.match(/^\//)) {
39652                 return;
39653             }
39654             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39655                 return;
39656             }
39657             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39658             node.removeAttribute(n);
39659             
39660         }
39661         
39662         function cleanStyle(n,v)
39663         {
39664             if (v.match(/expression/)) { //XSS?? should we even bother..
39665                 node.removeAttribute(n);
39666                 return;
39667             }
39668             
39669             
39670             var parts = v.split(/;/);
39671             Roo.each(parts, function(p) {
39672                 p = p.replace(/\s+/g,'');
39673                 if (!p.length) {
39674                     return true;
39675                 }
39676                 var l = p.split(':').shift().replace(/\s+/g,'');
39677                 
39678                 // only allow 'c whitelisted system attributes'
39679                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39680                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39681                     node.removeAttribute(n);
39682                     return false;
39683                 }
39684                 return true;
39685             });
39686             
39687             
39688         }
39689         
39690         
39691         for (var i = node.attributes.length-1; i > -1 ; i--) {
39692             var a = node.attributes[i];
39693             //console.log(a);
39694             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39695                 node.removeAttribute(a.name);
39696                 return;
39697             }
39698             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39699                 cleanAttr(a.name,a.value); // fixme..
39700                 return;
39701             }
39702             if (a.name == 'style') {
39703                 cleanStyle(a.name,a.value);
39704             }
39705             /// clean up MS crap..
39706             if (a.name == 'class') {
39707                 if (a.value.match(/^Mso/)) {
39708                     node.className = '';
39709                 }
39710             }
39711             
39712             // style cleanup!?
39713             // class cleanup?
39714             
39715         }
39716         
39717         
39718         this.cleanUpChildren(node);
39719         
39720         
39721     }
39722     
39723     
39724     // hide stuff that is not compatible
39725     /**
39726      * @event blur
39727      * @hide
39728      */
39729     /**
39730      * @event change
39731      * @hide
39732      */
39733     /**
39734      * @event focus
39735      * @hide
39736      */
39737     /**
39738      * @event specialkey
39739      * @hide
39740      */
39741     /**
39742      * @cfg {String} fieldClass @hide
39743      */
39744     /**
39745      * @cfg {String} focusClass @hide
39746      */
39747     /**
39748      * @cfg {String} autoCreate @hide
39749      */
39750     /**
39751      * @cfg {String} inputType @hide
39752      */
39753     /**
39754      * @cfg {String} invalidClass @hide
39755      */
39756     /**
39757      * @cfg {String} invalidText @hide
39758      */
39759     /**
39760      * @cfg {String} msgFx @hide
39761      */
39762     /**
39763      * @cfg {String} validateOnBlur @hide
39764      */
39765 });
39766
39767 Roo.form.HtmlEditor.white = [
39768         'area', 'br', 'img', 'input', 'hr', 'wbr',
39769         
39770        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39771        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39772        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39773        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39774        'table',   'ul',         'xmp', 
39775        
39776        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39777       'thead',   'tr', 
39778      
39779       'dir', 'menu', 'ol', 'ul', 'dl',
39780        
39781       'embed',  'object'
39782 ];
39783
39784
39785 Roo.form.HtmlEditor.black = [
39786     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39787         'applet', // 
39788         'base',   'basefont', 'bgsound', 'blink',  'body', 
39789         'frame',  'frameset', 'head',    'html',   'ilayer', 
39790         'iframe', 'layer',  'link',     'meta',    'object',   
39791         'script', 'style' ,'title',  'xml' // clean later..
39792 ];
39793 Roo.form.HtmlEditor.clean = [
39794     'script', 'style', 'title', 'xml'
39795 ];
39796 Roo.form.HtmlEditor.remove = [
39797     'font'
39798 ];
39799 // attributes..
39800
39801 Roo.form.HtmlEditor.ablack = [
39802     'on'
39803 ];
39804     
39805 Roo.form.HtmlEditor.aclean = [ 
39806     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39807 ];
39808
39809 // protocols..
39810 Roo.form.HtmlEditor.pwhite= [
39811         'http',  'https',  'mailto'
39812 ];
39813
39814 // white listed style attributes.
39815 Roo.form.HtmlEditor.cwhite= [
39816         'text-align',
39817         'font-size'
39818 ];
39819
39820 // <script type="text/javascript">
39821 /*
39822  * Based on
39823  * Ext JS Library 1.1.1
39824  * Copyright(c) 2006-2007, Ext JS, LLC.
39825  *  
39826  
39827  */
39828
39829 /**
39830  * @class Roo.form.HtmlEditorToolbar1
39831  * Basic Toolbar
39832  * 
39833  * Usage:
39834  *
39835  new Roo.form.HtmlEditor({
39836     ....
39837     toolbars : [
39838         new Roo.form.HtmlEditorToolbar1({
39839             disable : { fonts: 1 , format: 1, ..., ... , ...],
39840             btns : [ .... ]
39841         })
39842     }
39843      
39844  * 
39845  * @cfg {Object} disable List of elements to disable..
39846  * @cfg {Array} btns List of additional buttons.
39847  * 
39848  * 
39849  * NEEDS Extra CSS? 
39850  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39851  */
39852  
39853 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39854 {
39855     
39856     Roo.apply(this, config);
39857     
39858     // default disabled, based on 'good practice'..
39859     this.disable = this.disable || {};
39860     Roo.applyIf(this.disable, {
39861         fontSize : true,
39862         colors : true,
39863         specialElements : true
39864     });
39865     
39866     
39867     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39868     // dont call parent... till later.
39869 }
39870
39871 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39872     
39873     tb: false,
39874     
39875     rendered: false,
39876     
39877     editor : false,
39878     /**
39879      * @cfg {Object} disable  List of toolbar elements to disable
39880          
39881      */
39882     disable : false,
39883       /**
39884      * @cfg {Array} fontFamilies An array of available font families
39885      */
39886     fontFamilies : [
39887         'Arial',
39888         'Courier New',
39889         'Tahoma',
39890         'Times New Roman',
39891         'Verdana'
39892     ],
39893     
39894     specialChars : [
39895            "&#169;",
39896           "&#174;",     
39897           "&#8482;",    
39898           "&#163;" ,    
39899          // "&#8212;",    
39900           "&#8230;",    
39901           "&#247;" ,    
39902         //  "&#225;" ,     ?? a acute?
39903            "&#8364;"    , //Euro
39904        //   "&#8220;"    ,
39905         //  "&#8221;"    ,
39906         //  "&#8226;"    ,
39907           "&#176;"  //   , // degrees
39908
39909          // "&#233;"     , // e ecute
39910          // "&#250;"     , // u ecute?
39911     ],
39912     
39913     specialElements : [
39914         {
39915             text: "Insert Table",
39916             xtype: 'MenuItem',
39917             xns : Roo.Menu,
39918             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39919                 
39920         },
39921         {    
39922             text: "Insert Image",
39923             xtype: 'MenuItem',
39924             xns : Roo.Menu,
39925             ihtml : '<img src="about:blank"/>'
39926             
39927         }
39928         
39929          
39930     ],
39931     
39932     
39933     inputElements : [ 
39934             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39935             "input:submit", "input:button", "select", "textarea", "label" ],
39936     formats : [
39937         ["p"] ,  
39938         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39939         ["pre"],[ "code"], 
39940         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39941     ],
39942      /**
39943      * @cfg {String} defaultFont default font to use.
39944      */
39945     defaultFont: 'tahoma',
39946    
39947     fontSelect : false,
39948     
39949     
39950     formatCombo : false,
39951     
39952     init : function(editor)
39953     {
39954         this.editor = editor;
39955         
39956         
39957         var fid = editor.frameId;
39958         var etb = this;
39959         function btn(id, toggle, handler){
39960             var xid = fid + '-'+ id ;
39961             return {
39962                 id : xid,
39963                 cmd : id,
39964                 cls : 'x-btn-icon x-edit-'+id,
39965                 enableToggle:toggle !== false,
39966                 scope: editor, // was editor...
39967                 handler:handler||editor.relayBtnCmd,
39968                 clickEvent:'mousedown',
39969                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39970                 tabIndex:-1
39971             };
39972         }
39973         
39974         
39975         
39976         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39977         this.tb = tb;
39978          // stop form submits
39979         tb.el.on('click', function(e){
39980             e.preventDefault(); // what does this do?
39981         });
39982
39983         if(!this.disable.font && !Roo.isSafari){
39984             /* why no safari for fonts
39985             editor.fontSelect = tb.el.createChild({
39986                 tag:'select',
39987                 tabIndex: -1,
39988                 cls:'x-font-select',
39989                 html: editor.createFontOptions()
39990             });
39991             editor.fontSelect.on('change', function(){
39992                 var font = editor.fontSelect.dom.value;
39993                 editor.relayCmd('fontname', font);
39994                 editor.deferFocus();
39995             }, editor);
39996             tb.add(
39997                 editor.fontSelect.dom,
39998                 '-'
39999             );
40000             */
40001         };
40002         if(!this.disable.formats){
40003             this.formatCombo = new Roo.form.ComboBox({
40004                 store: new Roo.data.SimpleStore({
40005                     id : 'tag',
40006                     fields: ['tag'],
40007                     data : this.formats // from states.js
40008                 }),
40009                 blockFocus : true,
40010                 //autoCreate : {tag: "div",  size: "20"},
40011                 displayField:'tag',
40012                 typeAhead: false,
40013                 mode: 'local',
40014                 editable : false,
40015                 triggerAction: 'all',
40016                 emptyText:'Add tag',
40017                 selectOnFocus:true,
40018                 width:135,
40019                 listeners : {
40020                     'select': function(c, r, i) {
40021                         editor.insertTag(r.get('tag'));
40022                         editor.focus();
40023                     }
40024                 }
40025
40026             });
40027             tb.addField(this.formatCombo);
40028             
40029         }
40030         
40031         if(!this.disable.format){
40032             tb.add(
40033                 btn('bold'),
40034                 btn('italic'),
40035                 btn('underline')
40036             );
40037         };
40038         if(!this.disable.fontSize){
40039             tb.add(
40040                 '-',
40041                 
40042                 
40043                 btn('increasefontsize', false, editor.adjustFont),
40044                 btn('decreasefontsize', false, editor.adjustFont)
40045             );
40046         };
40047         
40048         
40049         if(!this.disable.colors){
40050             tb.add(
40051                 '-', {
40052                     id:editor.frameId +'-forecolor',
40053                     cls:'x-btn-icon x-edit-forecolor',
40054                     clickEvent:'mousedown',
40055                     tooltip: this.buttonTips['forecolor'] || undefined,
40056                     tabIndex:-1,
40057                     menu : new Roo.menu.ColorMenu({
40058                         allowReselect: true,
40059                         focus: Roo.emptyFn,
40060                         value:'000000',
40061                         plain:true,
40062                         selectHandler: function(cp, color){
40063                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40064                             editor.deferFocus();
40065                         },
40066                         scope: editor,
40067                         clickEvent:'mousedown'
40068                     })
40069                 }, {
40070                     id:editor.frameId +'backcolor',
40071                     cls:'x-btn-icon x-edit-backcolor',
40072                     clickEvent:'mousedown',
40073                     tooltip: this.buttonTips['backcolor'] || undefined,
40074                     tabIndex:-1,
40075                     menu : new Roo.menu.ColorMenu({
40076                         focus: Roo.emptyFn,
40077                         value:'FFFFFF',
40078                         plain:true,
40079                         allowReselect: true,
40080                         selectHandler: function(cp, color){
40081                             if(Roo.isGecko){
40082                                 editor.execCmd('useCSS', false);
40083                                 editor.execCmd('hilitecolor', color);
40084                                 editor.execCmd('useCSS', true);
40085                                 editor.deferFocus();
40086                             }else{
40087                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40088                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40089                                 editor.deferFocus();
40090                             }
40091                         },
40092                         scope:editor,
40093                         clickEvent:'mousedown'
40094                     })
40095                 }
40096             );
40097         };
40098         // now add all the items...
40099         
40100
40101         if(!this.disable.alignments){
40102             tb.add(
40103                 '-',
40104                 btn('justifyleft'),
40105                 btn('justifycenter'),
40106                 btn('justifyright')
40107             );
40108         };
40109
40110         //if(!Roo.isSafari){
40111             if(!this.disable.links){
40112                 tb.add(
40113                     '-',
40114                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40115                 );
40116             };
40117
40118             if(!this.disable.lists){
40119                 tb.add(
40120                     '-',
40121                     btn('insertorderedlist'),
40122                     btn('insertunorderedlist')
40123                 );
40124             }
40125             if(!this.disable.sourceEdit){
40126                 tb.add(
40127                     '-',
40128                     btn('sourceedit', true, function(btn){
40129                         this.toggleSourceEdit(btn.pressed);
40130                     })
40131                 );
40132             }
40133         //}
40134         
40135         var smenu = { };
40136         // special menu.. - needs to be tidied up..
40137         if (!this.disable.special) {
40138             smenu = {
40139                 text: "&#169;",
40140                 cls: 'x-edit-none',
40141                 
40142                 menu : {
40143                     items : []
40144                 }
40145             };
40146             for (var i =0; i < this.specialChars.length; i++) {
40147                 smenu.menu.items.push({
40148                     
40149                     html: this.specialChars[i],
40150                     handler: function(a,b) {
40151                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40152                         
40153                     },
40154                     tabIndex:-1
40155                 });
40156             }
40157             
40158             
40159             tb.add(smenu);
40160             
40161             
40162         }
40163          
40164         if (!this.disable.specialElements) {
40165             var semenu = {
40166                 text: "Other;",
40167                 cls: 'x-edit-none',
40168                 menu : {
40169                     items : []
40170                 }
40171             };
40172             for (var i =0; i < this.specialElements.length; i++) {
40173                 semenu.menu.items.push(
40174                     Roo.apply({ 
40175                         handler: function(a,b) {
40176                             editor.insertAtCursor(this.ihtml);
40177                         }
40178                     }, this.specialElements[i])
40179                 );
40180                     
40181             }
40182             
40183             tb.add(semenu);
40184             
40185             
40186         }
40187          
40188         
40189         if (this.btns) {
40190             for(var i =0; i< this.btns.length;i++) {
40191                 var b = this.btns[i];
40192                 b.cls =  'x-edit-none';
40193                 b.scope = editor;
40194                 tb.add(b);
40195             }
40196         
40197         }
40198         
40199         
40200         
40201         // disable everything...
40202         
40203         this.tb.items.each(function(item){
40204            if(item.id != editor.frameId+ '-sourceedit'){
40205                 item.disable();
40206             }
40207         });
40208         this.rendered = true;
40209         
40210         // the all the btns;
40211         editor.on('editorevent', this.updateToolbar, this);
40212         // other toolbars need to implement this..
40213         //editor.on('editmodechange', this.updateToolbar, this);
40214     },
40215     
40216     
40217     
40218     /**
40219      * Protected method that will not generally be called directly. It triggers
40220      * a toolbar update by reading the markup state of the current selection in the editor.
40221      */
40222     updateToolbar: function(){
40223
40224         if(!this.editor.activated){
40225             this.editor.onFirstFocus();
40226             return;
40227         }
40228
40229         var btns = this.tb.items.map, 
40230             doc = this.editor.doc,
40231             frameId = this.editor.frameId;
40232
40233         if(!this.disable.font && !Roo.isSafari){
40234             /*
40235             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40236             if(name != this.fontSelect.dom.value){
40237                 this.fontSelect.dom.value = name;
40238             }
40239             */
40240         }
40241         if(!this.disable.format){
40242             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40243             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40244             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40245         }
40246         if(!this.disable.alignments){
40247             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40248             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40249             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40250         }
40251         if(!Roo.isSafari && !this.disable.lists){
40252             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40253             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40254         }
40255         
40256         var ans = this.editor.getAllAncestors();
40257         if (this.formatCombo) {
40258             
40259             
40260             var store = this.formatCombo.store;
40261             this.formatCombo.setValue("");
40262             for (var i =0; i < ans.length;i++) {
40263                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40264                     // select it..
40265                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40266                     break;
40267                 }
40268             }
40269         }
40270         
40271         
40272         
40273         // hides menus... - so this cant be on a menu...
40274         Roo.menu.MenuMgr.hideAll();
40275
40276         //this.editorsyncValue();
40277     },
40278    
40279     
40280     createFontOptions : function(){
40281         var buf = [], fs = this.fontFamilies, ff, lc;
40282         for(var i = 0, len = fs.length; i< len; i++){
40283             ff = fs[i];
40284             lc = ff.toLowerCase();
40285             buf.push(
40286                 '<option value="',lc,'" style="font-family:',ff,';"',
40287                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40288                     ff,
40289                 '</option>'
40290             );
40291         }
40292         return buf.join('');
40293     },
40294     
40295     toggleSourceEdit : function(sourceEditMode){
40296         if(sourceEditMode === undefined){
40297             sourceEditMode = !this.sourceEditMode;
40298         }
40299         this.sourceEditMode = sourceEditMode === true;
40300         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40301         // just toggle the button?
40302         if(btn.pressed !== this.editor.sourceEditMode){
40303             btn.toggle(this.editor.sourceEditMode);
40304             return;
40305         }
40306         
40307         if(this.sourceEditMode){
40308             this.tb.items.each(function(item){
40309                 if(item.cmd != 'sourceedit'){
40310                     item.disable();
40311                 }
40312             });
40313           
40314         }else{
40315             if(this.initialized){
40316                 this.tb.items.each(function(item){
40317                     item.enable();
40318                 });
40319             }
40320             
40321         }
40322         // tell the editor that it's been pressed..
40323         this.editor.toggleSourceEdit(sourceEditMode);
40324        
40325     },
40326      /**
40327      * Object collection of toolbar tooltips for the buttons in the editor. The key
40328      * is the command id associated with that button and the value is a valid QuickTips object.
40329      * For example:
40330 <pre><code>
40331 {
40332     bold : {
40333         title: 'Bold (Ctrl+B)',
40334         text: 'Make the selected text bold.',
40335         cls: 'x-html-editor-tip'
40336     },
40337     italic : {
40338         title: 'Italic (Ctrl+I)',
40339         text: 'Make the selected text italic.',
40340         cls: 'x-html-editor-tip'
40341     },
40342     ...
40343 </code></pre>
40344     * @type Object
40345      */
40346     buttonTips : {
40347         bold : {
40348             title: 'Bold (Ctrl+B)',
40349             text: 'Make the selected text bold.',
40350             cls: 'x-html-editor-tip'
40351         },
40352         italic : {
40353             title: 'Italic (Ctrl+I)',
40354             text: 'Make the selected text italic.',
40355             cls: 'x-html-editor-tip'
40356         },
40357         underline : {
40358             title: 'Underline (Ctrl+U)',
40359             text: 'Underline the selected text.',
40360             cls: 'x-html-editor-tip'
40361         },
40362         increasefontsize : {
40363             title: 'Grow Text',
40364             text: 'Increase the font size.',
40365             cls: 'x-html-editor-tip'
40366         },
40367         decreasefontsize : {
40368             title: 'Shrink Text',
40369             text: 'Decrease the font size.',
40370             cls: 'x-html-editor-tip'
40371         },
40372         backcolor : {
40373             title: 'Text Highlight Color',
40374             text: 'Change the background color of the selected text.',
40375             cls: 'x-html-editor-tip'
40376         },
40377         forecolor : {
40378             title: 'Font Color',
40379             text: 'Change the color of the selected text.',
40380             cls: 'x-html-editor-tip'
40381         },
40382         justifyleft : {
40383             title: 'Align Text Left',
40384             text: 'Align text to the left.',
40385             cls: 'x-html-editor-tip'
40386         },
40387         justifycenter : {
40388             title: 'Center Text',
40389             text: 'Center text in the editor.',
40390             cls: 'x-html-editor-tip'
40391         },
40392         justifyright : {
40393             title: 'Align Text Right',
40394             text: 'Align text to the right.',
40395             cls: 'x-html-editor-tip'
40396         },
40397         insertunorderedlist : {
40398             title: 'Bullet List',
40399             text: 'Start a bulleted list.',
40400             cls: 'x-html-editor-tip'
40401         },
40402         insertorderedlist : {
40403             title: 'Numbered List',
40404             text: 'Start a numbered list.',
40405             cls: 'x-html-editor-tip'
40406         },
40407         createlink : {
40408             title: 'Hyperlink',
40409             text: 'Make the selected text a hyperlink.',
40410             cls: 'x-html-editor-tip'
40411         },
40412         sourceedit : {
40413             title: 'Source Edit',
40414             text: 'Switch to source editing mode.',
40415             cls: 'x-html-editor-tip'
40416         }
40417     },
40418     // private
40419     onDestroy : function(){
40420         if(this.rendered){
40421             
40422             this.tb.items.each(function(item){
40423                 if(item.menu){
40424                     item.menu.removeAll();
40425                     if(item.menu.el){
40426                         item.menu.el.destroy();
40427                     }
40428                 }
40429                 item.destroy();
40430             });
40431              
40432         }
40433     },
40434     onFirstFocus: function() {
40435         this.tb.items.each(function(item){
40436            item.enable();
40437         });
40438     }
40439 });
40440
40441
40442
40443
40444 // <script type="text/javascript">
40445 /*
40446  * Based on
40447  * Ext JS Library 1.1.1
40448  * Copyright(c) 2006-2007, Ext JS, LLC.
40449  *  
40450  
40451  */
40452
40453  
40454 /**
40455  * @class Roo.form.HtmlEditor.ToolbarContext
40456  * Context Toolbar
40457  * 
40458  * Usage:
40459  *
40460  new Roo.form.HtmlEditor({
40461     ....
40462     toolbars : [
40463         { xtype: 'ToolbarStandard', styles : {} }
40464         { xtype: 'ToolbarContext', disable : {} }
40465     ]
40466 })
40467
40468      
40469  * 
40470  * @config : {Object} disable List of elements to disable.. (not done yet.)
40471  * @config : {Object} styles  Map of styles available.
40472  * 
40473  */
40474
40475 Roo.form.HtmlEditor.ToolbarContext = function(config)
40476 {
40477     
40478     Roo.apply(this, config);
40479     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40480     // dont call parent... till later.
40481     this.styles = this.styles || {};
40482 }
40483 Roo.form.HtmlEditor.ToolbarContext.types = {
40484     'IMG' : {
40485         width : {
40486             title: "Width",
40487             width: 40
40488         },
40489         height:  {
40490             title: "Height",
40491             width: 40
40492         },
40493         align: {
40494             title: "Align",
40495             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40496             width : 80
40497             
40498         },
40499         border: {
40500             title: "Border",
40501             width: 40
40502         },
40503         alt: {
40504             title: "Alt",
40505             width: 120
40506         },
40507         src : {
40508             title: "Src",
40509             width: 220
40510         }
40511         
40512     },
40513     'A' : {
40514         name : {
40515             title: "Name",
40516             width: 50
40517         },
40518         href:  {
40519             title: "Href",
40520             width: 220
40521         } // border?
40522         
40523     },
40524     'TABLE' : {
40525         rows : {
40526             title: "Rows",
40527             width: 20
40528         },
40529         cols : {
40530             title: "Cols",
40531             width: 20
40532         },
40533         width : {
40534             title: "Width",
40535             width: 40
40536         },
40537         height : {
40538             title: "Height",
40539             width: 40
40540         },
40541         border : {
40542             title: "Border",
40543             width: 20
40544         }
40545     },
40546     'TD' : {
40547         width : {
40548             title: "Width",
40549             width: 40
40550         },
40551         height : {
40552             title: "Height",
40553             width: 40
40554         },   
40555         align: {
40556             title: "Align",
40557             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40558             width: 80
40559         },
40560         valign: {
40561             title: "Valign",
40562             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40563             width: 80
40564         },
40565         colspan: {
40566             title: "Colspan",
40567             width: 20
40568             
40569         }
40570     },
40571     'INPUT' : {
40572         name : {
40573             title: "name",
40574             width: 120
40575         },
40576         value : {
40577             title: "Value",
40578             width: 120
40579         },
40580         width : {
40581             title: "Width",
40582             width: 40
40583         }
40584     },
40585     'LABEL' : {
40586         'for' : {
40587             title: "For",
40588             width: 120
40589         }
40590     },
40591     'TEXTAREA' : {
40592           name : {
40593             title: "name",
40594             width: 120
40595         },
40596         rows : {
40597             title: "Rows",
40598             width: 20
40599         },
40600         cols : {
40601             title: "Cols",
40602             width: 20
40603         }
40604     },
40605     'SELECT' : {
40606         name : {
40607             title: "name",
40608             width: 120
40609         },
40610         selectoptions : {
40611             title: "Options",
40612             width: 200
40613         }
40614     },
40615     
40616     // should we really allow this??
40617     // should this just be 
40618     'BODY' : {
40619         title : {
40620             title: "title",
40621             width: 200,
40622             disabled : true
40623         }
40624     },
40625     '*' : {
40626         // empty..
40627     }
40628 };
40629
40630
40631
40632 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40633     
40634     tb: false,
40635     
40636     rendered: false,
40637     
40638     editor : false,
40639     /**
40640      * @cfg {Object} disable  List of toolbar elements to disable
40641          
40642      */
40643     disable : false,
40644     /**
40645      * @cfg {Object} styles List of styles 
40646      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40647      *
40648      * These must be defined in the page, so they get rendered correctly..
40649      * .headline { }
40650      * TD.underline { }
40651      * 
40652      */
40653     styles : false,
40654     
40655     
40656     
40657     toolbars : false,
40658     
40659     init : function(editor)
40660     {
40661         this.editor = editor;
40662         
40663         
40664         var fid = editor.frameId;
40665         var etb = this;
40666         function btn(id, toggle, handler){
40667             var xid = fid + '-'+ id ;
40668             return {
40669                 id : xid,
40670                 cmd : id,
40671                 cls : 'x-btn-icon x-edit-'+id,
40672                 enableToggle:toggle !== false,
40673                 scope: editor, // was editor...
40674                 handler:handler||editor.relayBtnCmd,
40675                 clickEvent:'mousedown',
40676                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40677                 tabIndex:-1
40678             };
40679         }
40680         // create a new element.
40681         var wdiv = editor.wrap.createChild({
40682                 tag: 'div'
40683             }, editor.wrap.dom.firstChild.nextSibling, true);
40684         
40685         // can we do this more than once??
40686         
40687          // stop form submits
40688       
40689  
40690         // disable everything...
40691         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40692         this.toolbars = {};
40693            
40694         for (var i in  ty) {
40695           
40696             this.toolbars[i] = this.buildToolbar(ty[i],i);
40697         }
40698         this.tb = this.toolbars.BODY;
40699         this.tb.el.show();
40700         this.buildFooter();
40701         this.footer.show();
40702          
40703         this.rendered = true;
40704         
40705         // the all the btns;
40706         editor.on('editorevent', this.updateToolbar, this);
40707         // other toolbars need to implement this..
40708         //editor.on('editmodechange', this.updateToolbar, this);
40709     },
40710     
40711     
40712     
40713     /**
40714      * Protected method that will not generally be called directly. It triggers
40715      * a toolbar update by reading the markup state of the current selection in the editor.
40716      */
40717     updateToolbar: function(ignore_a,ignore_b,sel){
40718
40719         
40720         if(!this.editor.activated){
40721              this.editor.onFirstFocus();
40722             return;
40723         }
40724         var updateFooter = sel ? false : true;
40725         
40726         
40727         var ans = this.editor.getAllAncestors();
40728         
40729         // pick
40730         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40731         
40732         if (!sel) { 
40733             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40734             sel = sel ? sel : this.editor.doc.body;
40735             sel = sel.tagName.length ? sel : this.editor.doc.body;
40736             
40737         }
40738         // pick a menu that exists..
40739         var tn = sel.tagName.toUpperCase();
40740         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40741         
40742         tn = sel.tagName.toUpperCase();
40743         
40744         var lastSel = this.tb.selectedNode
40745         
40746         this.tb.selectedNode = sel;
40747         
40748         // if current menu does not match..
40749         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40750                 
40751             this.tb.el.hide();
40752             ///console.log("show: " + tn);
40753             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40754             this.tb.el.show();
40755             // update name
40756             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40757             
40758             
40759             // update attributes
40760             if (this.tb.fields) {
40761                 this.tb.fields.each(function(e) {
40762                    e.setValue(sel.getAttribute(e.name));
40763                 });
40764             }
40765             
40766             // update styles
40767             var st = this.tb.fields.item(0);
40768             st.store.removeAll();
40769             var cn = sel.className.split(/\s+/);
40770             
40771             var avs = [];
40772             if (this.styles['*']) {
40773                 
40774                 Roo.each(this.styles['*'], function(v) {
40775                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40776                 });
40777             }
40778             if (this.styles[tn]) { 
40779                 Roo.each(this.styles[tn], function(v) {
40780                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40781                 });
40782             }
40783             
40784             st.store.loadData(avs);
40785             st.collapse();
40786             st.setValue(cn);
40787             
40788             // flag our selected Node.
40789             this.tb.selectedNode = sel;
40790            
40791            
40792             Roo.menu.MenuMgr.hideAll();
40793
40794         }
40795         
40796         if (!updateFooter) {
40797             return;
40798         }
40799         // update the footer
40800         //
40801         var html = '';
40802         
40803         this.footerEls = ans.reverse();
40804         Roo.each(this.footerEls, function(a,i) {
40805             if (!a) { return; }
40806             html += html.length ? ' &gt; '  :  '';
40807             
40808             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40809             
40810         });
40811        
40812         // 
40813         var sz = this.footDisp.up('td').getSize();
40814         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40815         this.footDisp.dom.style.marginLeft = '5px';
40816         
40817         this.footDisp.dom.style.overflow = 'hidden';
40818         
40819         this.footDisp.dom.innerHTML = html;
40820             
40821         //this.editorsyncValue();
40822     },
40823    
40824        
40825     // private
40826     onDestroy : function(){
40827         if(this.rendered){
40828             
40829             this.tb.items.each(function(item){
40830                 if(item.menu){
40831                     item.menu.removeAll();
40832                     if(item.menu.el){
40833                         item.menu.el.destroy();
40834                     }
40835                 }
40836                 item.destroy();
40837             });
40838              
40839         }
40840     },
40841     onFirstFocus: function() {
40842         // need to do this for all the toolbars..
40843         this.tb.items.each(function(item){
40844            item.enable();
40845         });
40846     },
40847     buildToolbar: function(tlist, nm)
40848     {
40849         var editor = this.editor;
40850          // create a new element.
40851         var wdiv = editor.wrap.createChild({
40852                 tag: 'div'
40853             }, editor.wrap.dom.firstChild.nextSibling, true);
40854         
40855        
40856         var tb = new Roo.Toolbar(wdiv);
40857         // add the name..
40858         
40859         tb.add(nm+ ":&nbsp;");
40860         
40861         // styles...
40862         if (this.styles) {
40863             
40864             // this needs a multi-select checkbox...
40865             tb.addField( new Roo.form.ComboBox({
40866                 store: new Roo.data.SimpleStore({
40867                     id : 'val',
40868                     fields: ['val', 'selected'],
40869                     data : [] 
40870                 }),
40871                 name : 'className',
40872                 displayField:'val',
40873                 typeAhead: false,
40874                 mode: 'local',
40875                 editable : false,
40876                 triggerAction: 'all',
40877                 emptyText:'Select Style',
40878                 selectOnFocus:true,
40879                 width: 130,
40880                 listeners : {
40881                     'select': function(c, r, i) {
40882                         // initial support only for on class per el..
40883                         tb.selectedNode.className =  r ? r.get('val') : '';
40884                     }
40885                 }
40886     
40887             }));
40888         }
40889             
40890         
40891         
40892         for (var i in tlist) {
40893             
40894             var item = tlist[i];
40895             tb.add(item.title + ":&nbsp;");
40896             
40897             
40898             
40899             
40900             if (item.opts) {
40901                 // opts == pulldown..
40902                 tb.addField( new Roo.form.ComboBox({
40903                     store: new Roo.data.SimpleStore({
40904                         id : 'val',
40905                         fields: ['val'],
40906                         data : item.opts  
40907                     }),
40908                     name : i,
40909                     displayField:'val',
40910                     typeAhead: false,
40911                     mode: 'local',
40912                     editable : false,
40913                     triggerAction: 'all',
40914                     emptyText:'Select',
40915                     selectOnFocus:true,
40916                     width: item.width ? item.width  : 130,
40917                     listeners : {
40918                         'select': function(c, r, i) {
40919                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40920                         }
40921                     }
40922
40923                 }));
40924                 continue;
40925                     
40926                  
40927                 
40928                 tb.addField( new Roo.form.TextField({
40929                     name: i,
40930                     width: 100,
40931                     //allowBlank:false,
40932                     value: ''
40933                 }));
40934                 continue;
40935             }
40936             tb.addField( new Roo.form.TextField({
40937                 name: i,
40938                 width: item.width,
40939                 //allowBlank:true,
40940                 value: '',
40941                 listeners: {
40942                     'change' : function(f, nv, ov) {
40943                         tb.selectedNode.setAttribute(f.name, nv);
40944                     }
40945                 }
40946             }));
40947              
40948         }
40949         tb.el.on('click', function(e){
40950             e.preventDefault(); // what does this do?
40951         });
40952         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40953         tb.el.hide();
40954         tb.name = nm;
40955         // dont need to disable them... as they will get hidden
40956         return tb;
40957          
40958         
40959     },
40960     buildFooter : function()
40961     {
40962         
40963         var fel = this.editor.wrap.createChild();
40964         this.footer = new Roo.Toolbar(fel);
40965         // toolbar has scrolly on left / right?
40966         var footDisp= new Roo.Toolbar.Fill();
40967         var _t = this;
40968         this.footer.add(
40969             {
40970                 text : '&lt;',
40971                 xtype: 'Button',
40972                 handler : function() {
40973                     _t.footDisp.scrollTo('left',0,true)
40974                 }
40975             }
40976         );
40977         this.footer.add( footDisp );
40978         this.footer.add( 
40979             {
40980                 text : '&gt;',
40981                 xtype: 'Button',
40982                 handler : function() {
40983                     // no animation..
40984                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40985                 }
40986             }
40987         );
40988         var fel = Roo.get(footDisp.el);
40989         fel.addClass('x-editor-context');
40990         this.footDispWrap = fel; 
40991         this.footDispWrap.overflow  = 'hidden';
40992         
40993         this.footDisp = fel.createChild();
40994         this.footDispWrap.on('click', this.onContextClick, this)
40995         
40996         
40997     },
40998     onContextClick : function (ev,dom)
40999     {
41000         ev.preventDefault();
41001         var  cn = dom.className;
41002         Roo.log(cn);
41003         if (!cn.match(/x-ed-loc-/)) {
41004             return;
41005         }
41006         var n = cn.split('-').pop();
41007         var ans = this.footerEls;
41008         var sel = ans[n];
41009         
41010          // pick
41011         var range = this.editor.createRange();
41012         
41013         range.selectNodeContents(sel);
41014         //range.selectNode(sel);
41015         
41016         
41017         var selection = this.editor.getSelection();
41018         selection.removeAllRanges();
41019         selection.addRange(range);
41020         
41021         
41022         
41023         this.updateToolbar(null, null, sel);
41024         
41025         
41026     }
41027     
41028     
41029     
41030     
41031     
41032 });
41033
41034
41035
41036
41037
41038 /*
41039  * Based on:
41040  * Ext JS Library 1.1.1
41041  * Copyright(c) 2006-2007, Ext JS, LLC.
41042  *
41043  * Originally Released Under LGPL - original licence link has changed is not relivant.
41044  *
41045  * Fork - LGPL
41046  * <script type="text/javascript">
41047  */
41048  
41049 /**
41050  * @class Roo.form.BasicForm
41051  * @extends Roo.util.Observable
41052  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41053  * @constructor
41054  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41055  * @param {Object} config Configuration options
41056  */
41057 Roo.form.BasicForm = function(el, config){
41058     this.allItems = [];
41059     this.childForms = [];
41060     Roo.apply(this, config);
41061     /*
41062      * The Roo.form.Field items in this form.
41063      * @type MixedCollection
41064      */
41065      
41066      
41067     this.items = new Roo.util.MixedCollection(false, function(o){
41068         return o.id || (o.id = Roo.id());
41069     });
41070     this.addEvents({
41071         /**
41072          * @event beforeaction
41073          * Fires before any action is performed. Return false to cancel the action.
41074          * @param {Form} this
41075          * @param {Action} action The action to be performed
41076          */
41077         beforeaction: true,
41078         /**
41079          * @event actionfailed
41080          * Fires when an action fails.
41081          * @param {Form} this
41082          * @param {Action} action The action that failed
41083          */
41084         actionfailed : true,
41085         /**
41086          * @event actioncomplete
41087          * Fires when an action is completed.
41088          * @param {Form} this
41089          * @param {Action} action The action that completed
41090          */
41091         actioncomplete : true
41092     });
41093     if(el){
41094         this.initEl(el);
41095     }
41096     Roo.form.BasicForm.superclass.constructor.call(this);
41097 };
41098
41099 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41100     /**
41101      * @cfg {String} method
41102      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41103      */
41104     /**
41105      * @cfg {DataReader} reader
41106      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41107      * This is optional as there is built-in support for processing JSON.
41108      */
41109     /**
41110      * @cfg {DataReader} errorReader
41111      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41112      * This is completely optional as there is built-in support for processing JSON.
41113      */
41114     /**
41115      * @cfg {String} url
41116      * The URL to use for form actions if one isn't supplied in the action options.
41117      */
41118     /**
41119      * @cfg {Boolean} fileUpload
41120      * Set to true if this form is a file upload.
41121      */
41122      
41123     /**
41124      * @cfg {Object} baseParams
41125      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41126      */
41127      /**
41128      
41129     /**
41130      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41131      */
41132     timeout: 30,
41133
41134     // private
41135     activeAction : null,
41136
41137     /**
41138      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41139      * or setValues() data instead of when the form was first created.
41140      */
41141     trackResetOnLoad : false,
41142     
41143     
41144     /**
41145      * childForms - used for multi-tab forms
41146      * @type {Array}
41147      */
41148     childForms : false,
41149     
41150     /**
41151      * allItems - full list of fields.
41152      * @type {Array}
41153      */
41154     allItems : false,
41155     
41156     /**
41157      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41158      * element by passing it or its id or mask the form itself by passing in true.
41159      * @type Mixed
41160      */
41161     waitMsgTarget : false,
41162
41163     // private
41164     initEl : function(el){
41165         this.el = Roo.get(el);
41166         this.id = this.el.id || Roo.id();
41167         this.el.on('submit', this.onSubmit, this);
41168         this.el.addClass('x-form');
41169     },
41170
41171     // private
41172     onSubmit : function(e){
41173         e.stopEvent();
41174     },
41175
41176     /**
41177      * Returns true if client-side validation on the form is successful.
41178      * @return Boolean
41179      */
41180     isValid : function(){
41181         var valid = true;
41182         this.items.each(function(f){
41183            if(!f.validate()){
41184                valid = false;
41185            }
41186         });
41187         return valid;
41188     },
41189
41190     /**
41191      * Returns true if any fields in this form have changed since their original load.
41192      * @return Boolean
41193      */
41194     isDirty : function(){
41195         var dirty = false;
41196         this.items.each(function(f){
41197            if(f.isDirty()){
41198                dirty = true;
41199                return false;
41200            }
41201         });
41202         return dirty;
41203     },
41204
41205     /**
41206      * Performs a predefined action (submit or load) or custom actions you define on this form.
41207      * @param {String} actionName The name of the action type
41208      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41209      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41210      * accept other config options):
41211      * <pre>
41212 Property          Type             Description
41213 ----------------  ---------------  ----------------------------------------------------------------------------------
41214 url               String           The url for the action (defaults to the form's url)
41215 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41216 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41217 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41218                                    validate the form on the client (defaults to false)
41219      * </pre>
41220      * @return {BasicForm} this
41221      */
41222     doAction : function(action, options){
41223         if(typeof action == 'string'){
41224             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41225         }
41226         if(this.fireEvent('beforeaction', this, action) !== false){
41227             this.beforeAction(action);
41228             action.run.defer(100, action);
41229         }
41230         return this;
41231     },
41232
41233     /**
41234      * Shortcut to do a submit action.
41235      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41236      * @return {BasicForm} this
41237      */
41238     submit : function(options){
41239         this.doAction('submit', options);
41240         return this;
41241     },
41242
41243     /**
41244      * Shortcut to do a load action.
41245      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41246      * @return {BasicForm} this
41247      */
41248     load : function(options){
41249         this.doAction('load', options);
41250         return this;
41251     },
41252
41253     /**
41254      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41255      * @param {Record} record The record to edit
41256      * @return {BasicForm} this
41257      */
41258     updateRecord : function(record){
41259         record.beginEdit();
41260         var fs = record.fields;
41261         fs.each(function(f){
41262             var field = this.findField(f.name);
41263             if(field){
41264                 record.set(f.name, field.getValue());
41265             }
41266         }, this);
41267         record.endEdit();
41268         return this;
41269     },
41270
41271     /**
41272      * Loads an Roo.data.Record into this form.
41273      * @param {Record} record The record to load
41274      * @return {BasicForm} this
41275      */
41276     loadRecord : function(record){
41277         this.setValues(record.data);
41278         return this;
41279     },
41280
41281     // private
41282     beforeAction : function(action){
41283         var o = action.options;
41284         
41285        
41286         if(this.waitMsgTarget === true){
41287             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41288         }else if(this.waitMsgTarget){
41289             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41290             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41291         }else {
41292             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41293         }
41294          
41295     },
41296
41297     // private
41298     afterAction : function(action, success){
41299         this.activeAction = null;
41300         var o = action.options;
41301         
41302         if(this.waitMsgTarget === true){
41303             this.el.unmask();
41304         }else if(this.waitMsgTarget){
41305             this.waitMsgTarget.unmask();
41306         }else{
41307             Roo.MessageBox.updateProgress(1);
41308             Roo.MessageBox.hide();
41309         }
41310          
41311         if(success){
41312             if(o.reset){
41313                 this.reset();
41314             }
41315             Roo.callback(o.success, o.scope, [this, action]);
41316             this.fireEvent('actioncomplete', this, action);
41317             
41318         }else{
41319             Roo.callback(o.failure, o.scope, [this, action]);
41320             // show an error message if no failed handler is set..
41321             if (!this.hasListener('actionfailed')) {
41322                 Roo.MessageBox.alert("Error",
41323                     typeof(action.result.errorMsg) != 'undefined' ?
41324                         action.result.errorMsg :
41325                         "Saving Failed, please check your entries"
41326                 );
41327             }
41328             
41329             this.fireEvent('actionfailed', this, action);
41330         }
41331         
41332     },
41333
41334     /**
41335      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41336      * @param {String} id The value to search for
41337      * @return Field
41338      */
41339     findField : function(id){
41340         var field = this.items.get(id);
41341         if(!field){
41342             this.items.each(function(f){
41343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41344                     field = f;
41345                     return false;
41346                 }
41347             });
41348         }
41349         return field || null;
41350     },
41351
41352     /**
41353      * Add a secondary form to this one, 
41354      * Used to provide tabbed forms. One form is primary, with hidden values 
41355      * which mirror the elements from the other forms.
41356      * 
41357      * @param {Roo.form.Form} form to add.
41358      * 
41359      */
41360     addForm : function(form)
41361     {
41362        
41363         if (this.childForms.indexOf(form) > -1) {
41364             // already added..
41365             return;
41366         }
41367         this.childForms.push(form);
41368         var n = '';
41369         Roo.each(form.allItems, function (fe) {
41370             
41371             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41372             if (this.findField(n)) { // already added..
41373                 return;
41374             }
41375             var add = new Roo.form.Hidden({
41376                 name : n
41377             });
41378             add.render(this.el);
41379             
41380             this.add( add );
41381         }, this);
41382         
41383     },
41384     /**
41385      * Mark fields in this form invalid in bulk.
41386      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41387      * @return {BasicForm} this
41388      */
41389     markInvalid : function(errors){
41390         if(errors instanceof Array){
41391             for(var i = 0, len = errors.length; i < len; i++){
41392                 var fieldError = errors[i];
41393                 var f = this.findField(fieldError.id);
41394                 if(f){
41395                     f.markInvalid(fieldError.msg);
41396                 }
41397             }
41398         }else{
41399             var field, id;
41400             for(id in errors){
41401                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41402                     field.markInvalid(errors[id]);
41403                 }
41404             }
41405         }
41406         Roo.each(this.childForms || [], function (f) {
41407             f.markInvalid(errors);
41408         });
41409         
41410         return this;
41411     },
41412
41413     /**
41414      * Set values for fields in this form in bulk.
41415      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41416      * @return {BasicForm} this
41417      */
41418     setValues : function(values){
41419         if(values instanceof Array){ // array of objects
41420             for(var i = 0, len = values.length; i < len; i++){
41421                 var v = values[i];
41422                 var f = this.findField(v.id);
41423                 if(f){
41424                     f.setValue(v.value);
41425                     if(this.trackResetOnLoad){
41426                         f.originalValue = f.getValue();
41427                     }
41428                 }
41429             }
41430         }else{ // object hash
41431             var field, id;
41432             for(id in values){
41433                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41434                     
41435                     if (field.setFromData && 
41436                         field.valueField && 
41437                         field.displayField &&
41438                         // combos' with local stores can 
41439                         // be queried via setValue()
41440                         // to set their value..
41441                         (field.store && !field.store.isLocal)
41442                         ) {
41443                         // it's a combo
41444                         var sd = { };
41445                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41446                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41447                         field.setFromData(sd);
41448                         
41449                     } else {
41450                         field.setValue(values[id]);
41451                     }
41452                     
41453                     
41454                     if(this.trackResetOnLoad){
41455                         field.originalValue = field.getValue();
41456                     }
41457                 }
41458             }
41459         }
41460          
41461         Roo.each(this.childForms || [], function (f) {
41462             f.setValues(values);
41463         });
41464                 
41465         return this;
41466     },
41467
41468     /**
41469      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41470      * they are returned as an array.
41471      * @param {Boolean} asString
41472      * @return {Object}
41473      */
41474     getValues : function(asString){
41475         if (this.childForms) {
41476             // copy values from the child forms
41477             Roo.each(this.childForms, function (f) {
41478                 this.setValues(f.getValues());
41479             }, this);
41480         }
41481         
41482         
41483         
41484         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41485         if(asString === true){
41486             return fs;
41487         }
41488         return Roo.urlDecode(fs);
41489     },
41490     
41491     /**
41492      * Returns the fields in this form as an object with key/value pairs. 
41493      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41494      * @return {Object}
41495      */
41496     getFieldValues : function(with_hidden)
41497     {
41498         if (this.childForms) {
41499             // copy values from the child forms
41500             // should this call getFieldValues - probably not as we do not currently copy
41501             // hidden fields when we generate..
41502             Roo.each(this.childForms, function (f) {
41503                 this.setValues(f.getValues());
41504             }, this);
41505         }
41506         
41507         var ret = {};
41508         this.items.each(function(f){
41509             if (!f.getName()) {
41510                 return;
41511             }
41512             var v = f.getValue();
41513             // not sure if this supported any more..
41514             if ((typeof(v) == 'object') && f.getRawValue) {
41515                 v = f.getRawValue() ; // dates..
41516             }
41517             // combo boxes where name != hiddenName...
41518             if (f.name != f.getName()) {
41519                 ret[f.name] = f.getRawValue();
41520             }
41521             ret[f.getName()] = v;
41522         });
41523         
41524         return ret;
41525     },
41526
41527     /**
41528      * Clears all invalid messages in this form.
41529      * @return {BasicForm} this
41530      */
41531     clearInvalid : function(){
41532         this.items.each(function(f){
41533            f.clearInvalid();
41534         });
41535         
41536         Roo.each(this.childForms || [], function (f) {
41537             f.clearInvalid();
41538         });
41539         
41540         
41541         return this;
41542     },
41543
41544     /**
41545      * Resets this form.
41546      * @return {BasicForm} this
41547      */
41548     reset : function(){
41549         this.items.each(function(f){
41550             f.reset();
41551         });
41552         
41553         Roo.each(this.childForms || [], function (f) {
41554             f.reset();
41555         });
41556        
41557         
41558         return this;
41559     },
41560
41561     /**
41562      * Add Roo.form components to this form.
41563      * @param {Field} field1
41564      * @param {Field} field2 (optional)
41565      * @param {Field} etc (optional)
41566      * @return {BasicForm} this
41567      */
41568     add : function(){
41569         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41570         return this;
41571     },
41572
41573
41574     /**
41575      * Removes a field from the items collection (does NOT remove its markup).
41576      * @param {Field} field
41577      * @return {BasicForm} this
41578      */
41579     remove : function(field){
41580         this.items.remove(field);
41581         return this;
41582     },
41583
41584     /**
41585      * Looks at the fields in this form, checks them for an id attribute,
41586      * and calls applyTo on the existing dom element with that id.
41587      * @return {BasicForm} this
41588      */
41589     render : function(){
41590         this.items.each(function(f){
41591             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41592                 f.applyTo(f.id);
41593             }
41594         });
41595         return this;
41596     },
41597
41598     /**
41599      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41600      * @param {Object} values
41601      * @return {BasicForm} this
41602      */
41603     applyToFields : function(o){
41604         this.items.each(function(f){
41605            Roo.apply(f, o);
41606         });
41607         return this;
41608     },
41609
41610     /**
41611      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41612      * @param {Object} values
41613      * @return {BasicForm} this
41614      */
41615     applyIfToFields : function(o){
41616         this.items.each(function(f){
41617            Roo.applyIf(f, o);
41618         });
41619         return this;
41620     }
41621 });
41622
41623 // back compat
41624 Roo.BasicForm = Roo.form.BasicForm;/*
41625  * Based on:
41626  * Ext JS Library 1.1.1
41627  * Copyright(c) 2006-2007, Ext JS, LLC.
41628  *
41629  * Originally Released Under LGPL - original licence link has changed is not relivant.
41630  *
41631  * Fork - LGPL
41632  * <script type="text/javascript">
41633  */
41634
41635 /**
41636  * @class Roo.form.Form
41637  * @extends Roo.form.BasicForm
41638  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41639  * @constructor
41640  * @param {Object} config Configuration options
41641  */
41642 Roo.form.Form = function(config){
41643     var xitems =  [];
41644     if (config.items) {
41645         xitems = config.items;
41646         delete config.items;
41647     }
41648    
41649     
41650     Roo.form.Form.superclass.constructor.call(this, null, config);
41651     this.url = this.url || this.action;
41652     if(!this.root){
41653         this.root = new Roo.form.Layout(Roo.applyIf({
41654             id: Roo.id()
41655         }, config));
41656     }
41657     this.active = this.root;
41658     /**
41659      * Array of all the buttons that have been added to this form via {@link addButton}
41660      * @type Array
41661      */
41662     this.buttons = [];
41663     this.allItems = [];
41664     this.addEvents({
41665         /**
41666          * @event clientvalidation
41667          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41668          * @param {Form} this
41669          * @param {Boolean} valid true if the form has passed client-side validation
41670          */
41671         clientvalidation: true,
41672         /**
41673          * @event rendered
41674          * Fires when the form is rendered
41675          * @param {Roo.form.Form} form
41676          */
41677         rendered : true
41678     });
41679     
41680     if (this.progressUrl) {
41681             // push a hidden field onto the list of fields..
41682             this.addxtype( {
41683                     xns: Roo.form, 
41684                     xtype : 'Hidden', 
41685                     name : 'UPLOAD_IDENTIFIER' 
41686             });
41687         }
41688         
41689     
41690     Roo.each(xitems, this.addxtype, this);
41691     
41692     
41693     
41694 };
41695
41696 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41697     /**
41698      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41699      */
41700     /**
41701      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41702      */
41703     /**
41704      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41705      */
41706     buttonAlign:'center',
41707
41708     /**
41709      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41710      */
41711     minButtonWidth:75,
41712
41713     /**
41714      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41715      * This property cascades to child containers if not set.
41716      */
41717     labelAlign:'left',
41718
41719     /**
41720      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41721      * fires a looping event with that state. This is required to bind buttons to the valid
41722      * state using the config value formBind:true on the button.
41723      */
41724     monitorValid : false,
41725
41726     /**
41727      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41728      */
41729     monitorPoll : 200,
41730     
41731     /**
41732      * @cfg {String} progressUrl - Url to return progress data 
41733      */
41734     
41735     progressUrl : false,
41736   
41737     /**
41738      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41739      * fields are added and the column is closed. If no fields are passed the column remains open
41740      * until end() is called.
41741      * @param {Object} config The config to pass to the column
41742      * @param {Field} field1 (optional)
41743      * @param {Field} field2 (optional)
41744      * @param {Field} etc (optional)
41745      * @return Column The column container object
41746      */
41747     column : function(c){
41748         var col = new Roo.form.Column(c);
41749         this.start(col);
41750         if(arguments.length > 1){ // duplicate code required because of Opera
41751             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41752             this.end();
41753         }
41754         return col;
41755     },
41756
41757     /**
41758      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41759      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41760      * until end() is called.
41761      * @param {Object} config The config to pass to the fieldset
41762      * @param {Field} field1 (optional)
41763      * @param {Field} field2 (optional)
41764      * @param {Field} etc (optional)
41765      * @return FieldSet The fieldset container object
41766      */
41767     fieldset : function(c){
41768         var fs = new Roo.form.FieldSet(c);
41769         this.start(fs);
41770         if(arguments.length > 1){ // duplicate code required because of Opera
41771             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41772             this.end();
41773         }
41774         return fs;
41775     },
41776
41777     /**
41778      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41779      * fields are added and the container is closed. If no fields are passed the container remains open
41780      * until end() is called.
41781      * @param {Object} config The config to pass to the Layout
41782      * @param {Field} field1 (optional)
41783      * @param {Field} field2 (optional)
41784      * @param {Field} etc (optional)
41785      * @return Layout The container object
41786      */
41787     container : function(c){
41788         var l = new Roo.form.Layout(c);
41789         this.start(l);
41790         if(arguments.length > 1){ // duplicate code required because of Opera
41791             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41792             this.end();
41793         }
41794         return l;
41795     },
41796
41797     /**
41798      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41799      * @param {Object} container A Roo.form.Layout or subclass of Layout
41800      * @return {Form} this
41801      */
41802     start : function(c){
41803         // cascade label info
41804         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41805         this.active.stack.push(c);
41806         c.ownerCt = this.active;
41807         this.active = c;
41808         return this;
41809     },
41810
41811     /**
41812      * Closes the current open container
41813      * @return {Form} this
41814      */
41815     end : function(){
41816         if(this.active == this.root){
41817             return this;
41818         }
41819         this.active = this.active.ownerCt;
41820         return this;
41821     },
41822
41823     /**
41824      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41825      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41826      * as the label of the field.
41827      * @param {Field} field1
41828      * @param {Field} field2 (optional)
41829      * @param {Field} etc. (optional)
41830      * @return {Form} this
41831      */
41832     add : function(){
41833         this.active.stack.push.apply(this.active.stack, arguments);
41834         this.allItems.push.apply(this.allItems,arguments);
41835         var r = [];
41836         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41837             if(a[i].isFormField){
41838                 r.push(a[i]);
41839             }
41840         }
41841         if(r.length > 0){
41842             Roo.form.Form.superclass.add.apply(this, r);
41843         }
41844         return this;
41845     },
41846     
41847
41848     
41849     
41850     
41851      /**
41852      * Find any element that has been added to a form, using it's ID or name
41853      * This can include framesets, columns etc. along with regular fields..
41854      * @param {String} id - id or name to find.
41855      
41856      * @return {Element} e - or false if nothing found.
41857      */
41858     findbyId : function(id)
41859     {
41860         var ret = false;
41861         if (!id) {
41862             return ret;
41863         }
41864         Roo.each(this.allItems, function(f){
41865             if (f.id == id || f.name == id ){
41866                 ret = f;
41867                 return false;
41868             }
41869         });
41870         return ret;
41871     },
41872
41873     
41874     
41875     /**
41876      * Render this form into the passed container. This should only be called once!
41877      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41878      * @return {Form} this
41879      */
41880     render : function(ct)
41881     {
41882         
41883         
41884         
41885         ct = Roo.get(ct);
41886         var o = this.autoCreate || {
41887             tag: 'form',
41888             method : this.method || 'POST',
41889             id : this.id || Roo.id()
41890         };
41891         this.initEl(ct.createChild(o));
41892
41893         this.root.render(this.el);
41894         
41895        
41896              
41897         this.items.each(function(f){
41898             f.render('x-form-el-'+f.id);
41899         });
41900
41901         if(this.buttons.length > 0){
41902             // tables are required to maintain order and for correct IE layout
41903             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41904                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41905                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41906             }}, null, true);
41907             var tr = tb.getElementsByTagName('tr')[0];
41908             for(var i = 0, len = this.buttons.length; i < len; i++) {
41909                 var b = this.buttons[i];
41910                 var td = document.createElement('td');
41911                 td.className = 'x-form-btn-td';
41912                 b.render(tr.appendChild(td));
41913             }
41914         }
41915         if(this.monitorValid){ // initialize after render
41916             this.startMonitoring();
41917         }
41918         this.fireEvent('rendered', this);
41919         return this;
41920     },
41921
41922     /**
41923      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41924      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41925      * object or a valid Roo.DomHelper element config
41926      * @param {Function} handler The function called when the button is clicked
41927      * @param {Object} scope (optional) The scope of the handler function
41928      * @return {Roo.Button}
41929      */
41930     addButton : function(config, handler, scope){
41931         var bc = {
41932             handler: handler,
41933             scope: scope,
41934             minWidth: this.minButtonWidth,
41935             hideParent:true
41936         };
41937         if(typeof config == "string"){
41938             bc.text = config;
41939         }else{
41940             Roo.apply(bc, config);
41941         }
41942         var btn = new Roo.Button(null, bc);
41943         this.buttons.push(btn);
41944         return btn;
41945     },
41946
41947      /**
41948      * Adds a series of form elements (using the xtype property as the factory method.
41949      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41950      * @param {Object} config 
41951      */
41952     
41953     addxtype : function()
41954     {
41955         var ar = Array.prototype.slice.call(arguments, 0);
41956         var ret = false;
41957         for(var i = 0; i < ar.length; i++) {
41958             if (!ar[i]) {
41959                 continue; // skip -- if this happends something invalid got sent, we 
41960                 // should ignore it, as basically that interface element will not show up
41961                 // and that should be pretty obvious!!
41962             }
41963             
41964             if (Roo.form[ar[i].xtype]) {
41965                 ar[i].form = this;
41966                 var fe = Roo.factory(ar[i], Roo.form);
41967                 if (!ret) {
41968                     ret = fe;
41969                 }
41970                 fe.form = this;
41971                 if (fe.store) {
41972                     fe.store.form = this;
41973                 }
41974                 if (fe.isLayout) {  
41975                          
41976                     this.start(fe);
41977                     this.allItems.push(fe);
41978                     if (fe.items && fe.addxtype) {
41979                         fe.addxtype.apply(fe, fe.items);
41980                         delete fe.items;
41981                     }
41982                      this.end();
41983                     continue;
41984                 }
41985                 
41986                 
41987                  
41988                 this.add(fe);
41989               //  console.log('adding ' + ar[i].xtype);
41990             }
41991             if (ar[i].xtype == 'Button') {  
41992                 //console.log('adding button');
41993                 //console.log(ar[i]);
41994                 this.addButton(ar[i]);
41995                 this.allItems.push(fe);
41996                 continue;
41997             }
41998             
41999             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42000                 alert('end is not supported on xtype any more, use items');
42001             //    this.end();
42002             //    //console.log('adding end');
42003             }
42004             
42005         }
42006         return ret;
42007     },
42008     
42009     /**
42010      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42011      * option "monitorValid"
42012      */
42013     startMonitoring : function(){
42014         if(!this.bound){
42015             this.bound = true;
42016             Roo.TaskMgr.start({
42017                 run : this.bindHandler,
42018                 interval : this.monitorPoll || 200,
42019                 scope: this
42020             });
42021         }
42022     },
42023
42024     /**
42025      * Stops monitoring of the valid state of this form
42026      */
42027     stopMonitoring : function(){
42028         this.bound = false;
42029     },
42030
42031     // private
42032     bindHandler : function(){
42033         if(!this.bound){
42034             return false; // stops binding
42035         }
42036         var valid = true;
42037         this.items.each(function(f){
42038             if(!f.isValid(true)){
42039                 valid = false;
42040                 return false;
42041             }
42042         });
42043         for(var i = 0, len = this.buttons.length; i < len; i++){
42044             var btn = this.buttons[i];
42045             if(btn.formBind === true && btn.disabled === valid){
42046                 btn.setDisabled(!valid);
42047             }
42048         }
42049         this.fireEvent('clientvalidation', this, valid);
42050     }
42051     
42052     
42053     
42054     
42055     
42056     
42057     
42058     
42059 });
42060
42061
42062 // back compat
42063 Roo.Form = Roo.form.Form;
42064 /*
42065  * Based on:
42066  * Ext JS Library 1.1.1
42067  * Copyright(c) 2006-2007, Ext JS, LLC.
42068  *
42069  * Originally Released Under LGPL - original licence link has changed is not relivant.
42070  *
42071  * Fork - LGPL
42072  * <script type="text/javascript">
42073  */
42074  
42075  /**
42076  * @class Roo.form.Action
42077  * Internal Class used to handle form actions
42078  * @constructor
42079  * @param {Roo.form.BasicForm} el The form element or its id
42080  * @param {Object} config Configuration options
42081  */
42082  
42083  
42084 // define the action interface
42085 Roo.form.Action = function(form, options){
42086     this.form = form;
42087     this.options = options || {};
42088 };
42089 /**
42090  * Client Validation Failed
42091  * @const 
42092  */
42093 Roo.form.Action.CLIENT_INVALID = 'client';
42094 /**
42095  * Server Validation Failed
42096  * @const 
42097  */
42098  Roo.form.Action.SERVER_INVALID = 'server';
42099  /**
42100  * Connect to Server Failed
42101  * @const 
42102  */
42103 Roo.form.Action.CONNECT_FAILURE = 'connect';
42104 /**
42105  * Reading Data from Server Failed
42106  * @const 
42107  */
42108 Roo.form.Action.LOAD_FAILURE = 'load';
42109
42110 Roo.form.Action.prototype = {
42111     type : 'default',
42112     failureType : undefined,
42113     response : undefined,
42114     result : undefined,
42115
42116     // interface method
42117     run : function(options){
42118
42119     },
42120
42121     // interface method
42122     success : function(response){
42123
42124     },
42125
42126     // interface method
42127     handleResponse : function(response){
42128
42129     },
42130
42131     // default connection failure
42132     failure : function(response){
42133         
42134         this.response = response;
42135         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42136         this.form.afterAction(this, false);
42137     },
42138
42139     processResponse : function(response){
42140         this.response = response;
42141         if(!response.responseText){
42142             return true;
42143         }
42144         this.result = this.handleResponse(response);
42145         return this.result;
42146     },
42147
42148     // utility functions used internally
42149     getUrl : function(appendParams){
42150         var url = this.options.url || this.form.url || this.form.el.dom.action;
42151         if(appendParams){
42152             var p = this.getParams();
42153             if(p){
42154                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42155             }
42156         }
42157         return url;
42158     },
42159
42160     getMethod : function(){
42161         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42162     },
42163
42164     getParams : function(){
42165         var bp = this.form.baseParams;
42166         var p = this.options.params;
42167         if(p){
42168             if(typeof p == "object"){
42169                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42170             }else if(typeof p == 'string' && bp){
42171                 p += '&' + Roo.urlEncode(bp);
42172             }
42173         }else if(bp){
42174             p = Roo.urlEncode(bp);
42175         }
42176         return p;
42177     },
42178
42179     createCallback : function(){
42180         return {
42181             success: this.success,
42182             failure: this.failure,
42183             scope: this,
42184             timeout: (this.form.timeout*1000),
42185             upload: this.form.fileUpload ? this.success : undefined
42186         };
42187     }
42188 };
42189
42190 Roo.form.Action.Submit = function(form, options){
42191     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42192 };
42193
42194 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42195     type : 'submit',
42196
42197     haveProgress : false,
42198     uploadComplete : false,
42199     
42200     // uploadProgress indicator.
42201     uploadProgress : function()
42202     {
42203         if (!this.form.progressUrl) {
42204             return;
42205         }
42206         
42207         if (!this.haveProgress) {
42208             Roo.MessageBox.progress("Uploading", "Uploading");
42209         }
42210         if (this.uploadComplete) {
42211            Roo.MessageBox.hide();
42212            return;
42213         }
42214         
42215         this.haveProgress = true;
42216    
42217         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42218         
42219         var c = new Roo.data.Connection();
42220         c.request({
42221             url : this.form.progressUrl,
42222             params: {
42223                 id : uid
42224             },
42225             method: 'GET',
42226             success : function(req){
42227                //console.log(data);
42228                 var rdata = false;
42229                 var edata;
42230                 try  {
42231                    rdata = Roo.decode(req.responseText)
42232                 } catch (e) {
42233                     Roo.log("Invalid data from server..");
42234                     Roo.log(edata);
42235                     return;
42236                 }
42237                 if (!rdata || !rdata.success) {
42238                     Roo.log(rdata);
42239                     return;
42240                 }
42241                 var data = rdata.data;
42242                 
42243                 if (this.uploadComplete) {
42244                    Roo.MessageBox.hide();
42245                    return;
42246                 }
42247                    
42248                 if (data){
42249                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42250                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42251                     );
42252                 }
42253                 this.uploadProgress.defer(2000,this);
42254             },
42255        
42256             failure: function(data) {
42257                 Roo.log('progress url failed ');
42258                 Roo.log(data);
42259             },
42260             scope : this
42261         });
42262            
42263     },
42264     
42265     
42266     run : function()
42267     {
42268         // run get Values on the form, so it syncs any secondary forms.
42269         this.form.getValues();
42270         
42271         var o = this.options;
42272         var method = this.getMethod();
42273         var isPost = method == 'POST';
42274         if(o.clientValidation === false || this.form.isValid()){
42275             
42276             if (this.form.progressUrl) {
42277                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42278                     (new Date() * 1) + '' + Math.random());
42279                     
42280             } 
42281             
42282             
42283             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42284                 form:this.form.el.dom,
42285                 url:this.getUrl(!isPost),
42286                 method: method,
42287                 params:isPost ? this.getParams() : null,
42288                 isUpload: this.form.fileUpload
42289             }));
42290             
42291             this.uploadProgress();
42292
42293         }else if (o.clientValidation !== false){ // client validation failed
42294             this.failureType = Roo.form.Action.CLIENT_INVALID;
42295             this.form.afterAction(this, false);
42296         }
42297     },
42298
42299     success : function(response)
42300     {
42301         this.uploadComplete= true;
42302         if (this.haveProgress) {
42303             Roo.MessageBox.hide();
42304         }
42305         
42306         
42307         var result = this.processResponse(response);
42308         if(result === true || result.success){
42309             this.form.afterAction(this, true);
42310             return;
42311         }
42312         if(result.errors){
42313             this.form.markInvalid(result.errors);
42314             this.failureType = Roo.form.Action.SERVER_INVALID;
42315         }
42316         this.form.afterAction(this, false);
42317     },
42318     failure : function(response)
42319     {
42320         this.uploadComplete= true;
42321         if (this.haveProgress) {
42322             Roo.MessageBox.hide();
42323         }
42324         
42325         this.response = response;
42326         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42327         this.form.afterAction(this, false);
42328     },
42329     
42330     handleResponse : function(response){
42331         if(this.form.errorReader){
42332             var rs = this.form.errorReader.read(response);
42333             var errors = [];
42334             if(rs.records){
42335                 for(var i = 0, len = rs.records.length; i < len; i++) {
42336                     var r = rs.records[i];
42337                     errors[i] = r.data;
42338                 }
42339             }
42340             if(errors.length < 1){
42341                 errors = null;
42342             }
42343             return {
42344                 success : rs.success,
42345                 errors : errors
42346             };
42347         }
42348         var ret = false;
42349         try {
42350             ret = Roo.decode(response.responseText);
42351         } catch (e) {
42352             ret = {
42353                 success: false,
42354                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42355                 errors : []
42356             };
42357         }
42358         return ret;
42359         
42360     }
42361 });
42362
42363
42364 Roo.form.Action.Load = function(form, options){
42365     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42366     this.reader = this.form.reader;
42367 };
42368
42369 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42370     type : 'load',
42371
42372     run : function(){
42373         
42374         Roo.Ajax.request(Roo.apply(
42375                 this.createCallback(), {
42376                     method:this.getMethod(),
42377                     url:this.getUrl(false),
42378                     params:this.getParams()
42379         }));
42380     },
42381
42382     success : function(response){
42383         
42384         var result = this.processResponse(response);
42385         if(result === true || !result.success || !result.data){
42386             this.failureType = Roo.form.Action.LOAD_FAILURE;
42387             this.form.afterAction(this, false);
42388             return;
42389         }
42390         this.form.clearInvalid();
42391         this.form.setValues(result.data);
42392         this.form.afterAction(this, true);
42393     },
42394
42395     handleResponse : function(response){
42396         if(this.form.reader){
42397             var rs = this.form.reader.read(response);
42398             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42399             return {
42400                 success : rs.success,
42401                 data : data
42402             };
42403         }
42404         return Roo.decode(response.responseText);
42405     }
42406 });
42407
42408 Roo.form.Action.ACTION_TYPES = {
42409     'load' : Roo.form.Action.Load,
42410     'submit' : Roo.form.Action.Submit
42411 };/*
42412  * Based on:
42413  * Ext JS Library 1.1.1
42414  * Copyright(c) 2006-2007, Ext JS, LLC.
42415  *
42416  * Originally Released Under LGPL - original licence link has changed is not relivant.
42417  *
42418  * Fork - LGPL
42419  * <script type="text/javascript">
42420  */
42421  
42422 /**
42423  * @class Roo.form.Layout
42424  * @extends Roo.Component
42425  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42426  * @constructor
42427  * @param {Object} config Configuration options
42428  */
42429 Roo.form.Layout = function(config){
42430     var xitems = [];
42431     if (config.items) {
42432         xitems = config.items;
42433         delete config.items;
42434     }
42435     Roo.form.Layout.superclass.constructor.call(this, config);
42436     this.stack = [];
42437     Roo.each(xitems, this.addxtype, this);
42438      
42439 };
42440
42441 Roo.extend(Roo.form.Layout, Roo.Component, {
42442     /**
42443      * @cfg {String/Object} autoCreate
42444      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42445      */
42446     /**
42447      * @cfg {String/Object/Function} style
42448      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42449      * a function which returns such a specification.
42450      */
42451     /**
42452      * @cfg {String} labelAlign
42453      * Valid values are "left," "top" and "right" (defaults to "left")
42454      */
42455     /**
42456      * @cfg {Number} labelWidth
42457      * Fixed width in pixels of all field labels (defaults to undefined)
42458      */
42459     /**
42460      * @cfg {Boolean} clear
42461      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42462      */
42463     clear : true,
42464     /**
42465      * @cfg {String} labelSeparator
42466      * The separator to use after field labels (defaults to ':')
42467      */
42468     labelSeparator : ':',
42469     /**
42470      * @cfg {Boolean} hideLabels
42471      * True to suppress the display of field labels in this layout (defaults to false)
42472      */
42473     hideLabels : false,
42474
42475     // private
42476     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42477     
42478     isLayout : true,
42479     
42480     // private
42481     onRender : function(ct, position){
42482         if(this.el){ // from markup
42483             this.el = Roo.get(this.el);
42484         }else {  // generate
42485             var cfg = this.getAutoCreate();
42486             this.el = ct.createChild(cfg, position);
42487         }
42488         if(this.style){
42489             this.el.applyStyles(this.style);
42490         }
42491         if(this.labelAlign){
42492             this.el.addClass('x-form-label-'+this.labelAlign);
42493         }
42494         if(this.hideLabels){
42495             this.labelStyle = "display:none";
42496             this.elementStyle = "padding-left:0;";
42497         }else{
42498             if(typeof this.labelWidth == 'number'){
42499                 this.labelStyle = "width:"+this.labelWidth+"px;";
42500                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42501             }
42502             if(this.labelAlign == 'top'){
42503                 this.labelStyle = "width:auto;";
42504                 this.elementStyle = "padding-left:0;";
42505             }
42506         }
42507         var stack = this.stack;
42508         var slen = stack.length;
42509         if(slen > 0){
42510             if(!this.fieldTpl){
42511                 var t = new Roo.Template(
42512                     '<div class="x-form-item {5}">',
42513                         '<label for="{0}" style="{2}">{1}{4}</label>',
42514                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42515                         '</div>',
42516                     '</div><div class="x-form-clear-left"></div>'
42517                 );
42518                 t.disableFormats = true;
42519                 t.compile();
42520                 Roo.form.Layout.prototype.fieldTpl = t;
42521             }
42522             for(var i = 0; i < slen; i++) {
42523                 if(stack[i].isFormField){
42524                     this.renderField(stack[i]);
42525                 }else{
42526                     this.renderComponent(stack[i]);
42527                 }
42528             }
42529         }
42530         if(this.clear){
42531             this.el.createChild({cls:'x-form-clear'});
42532         }
42533     },
42534
42535     // private
42536     renderField : function(f){
42537         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42538                f.id, //0
42539                f.fieldLabel, //1
42540                f.labelStyle||this.labelStyle||'', //2
42541                this.elementStyle||'', //3
42542                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42543                f.itemCls||this.itemCls||''  //5
42544        ], true).getPrevSibling());
42545     },
42546
42547     // private
42548     renderComponent : function(c){
42549         c.render(c.isLayout ? this.el : this.el.createChild());    
42550     },
42551     /**
42552      * Adds a object form elements (using the xtype property as the factory method.)
42553      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42554      * @param {Object} config 
42555      */
42556     addxtype : function(o)
42557     {
42558         // create the lement.
42559         o.form = this.form;
42560         var fe = Roo.factory(o, Roo.form);
42561         this.form.allItems.push(fe);
42562         this.stack.push(fe);
42563         
42564         if (fe.isFormField) {
42565             this.form.items.add(fe);
42566         }
42567          
42568         return fe;
42569     }
42570 });
42571
42572 /**
42573  * @class Roo.form.Column
42574  * @extends Roo.form.Layout
42575  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42576  * @constructor
42577  * @param {Object} config Configuration options
42578  */
42579 Roo.form.Column = function(config){
42580     Roo.form.Column.superclass.constructor.call(this, config);
42581 };
42582
42583 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42584     /**
42585      * @cfg {Number/String} width
42586      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42587      */
42588     /**
42589      * @cfg {String/Object} autoCreate
42590      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42591      */
42592
42593     // private
42594     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42595
42596     // private
42597     onRender : function(ct, position){
42598         Roo.form.Column.superclass.onRender.call(this, ct, position);
42599         if(this.width){
42600             this.el.setWidth(this.width);
42601         }
42602     }
42603 });
42604
42605
42606 /**
42607  * @class Roo.form.Row
42608  * @extends Roo.form.Layout
42609  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42610  * @constructor
42611  * @param {Object} config Configuration options
42612  */
42613
42614  
42615 Roo.form.Row = function(config){
42616     Roo.form.Row.superclass.constructor.call(this, config);
42617 };
42618  
42619 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42620       /**
42621      * @cfg {Number/String} width
42622      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42623      */
42624     /**
42625      * @cfg {Number/String} height
42626      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42627      */
42628     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42629     
42630     padWidth : 20,
42631     // private
42632     onRender : function(ct, position){
42633         //console.log('row render');
42634         if(!this.rowTpl){
42635             var t = new Roo.Template(
42636                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42637                     '<label for="{0}" style="{2}">{1}{4}</label>',
42638                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42639                     '</div>',
42640                 '</div>'
42641             );
42642             t.disableFormats = true;
42643             t.compile();
42644             Roo.form.Layout.prototype.rowTpl = t;
42645         }
42646         this.fieldTpl = this.rowTpl;
42647         
42648         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42649         var labelWidth = 100;
42650         
42651         if ((this.labelAlign != 'top')) {
42652             if (typeof this.labelWidth == 'number') {
42653                 labelWidth = this.labelWidth
42654             }
42655             this.padWidth =  20 + labelWidth;
42656             
42657         }
42658         
42659         Roo.form.Column.superclass.onRender.call(this, ct, position);
42660         if(this.width){
42661             this.el.setWidth(this.width);
42662         }
42663         if(this.height){
42664             this.el.setHeight(this.height);
42665         }
42666     },
42667     
42668     // private
42669     renderField : function(f){
42670         f.fieldEl = this.fieldTpl.append(this.el, [
42671                f.id, f.fieldLabel,
42672                f.labelStyle||this.labelStyle||'',
42673                this.elementStyle||'',
42674                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42675                f.itemCls||this.itemCls||'',
42676                f.width ? f.width + this.padWidth : 160 + this.padWidth
42677        ],true);
42678     }
42679 });
42680  
42681
42682 /**
42683  * @class Roo.form.FieldSet
42684  * @extends Roo.form.Layout
42685  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42686  * @constructor
42687  * @param {Object} config Configuration options
42688  */
42689 Roo.form.FieldSet = function(config){
42690     Roo.form.FieldSet.superclass.constructor.call(this, config);
42691 };
42692
42693 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42694     /**
42695      * @cfg {String} legend
42696      * The text to display as the legend for the FieldSet (defaults to '')
42697      */
42698     /**
42699      * @cfg {String/Object} autoCreate
42700      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42701      */
42702
42703     // private
42704     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42705
42706     // private
42707     onRender : function(ct, position){
42708         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42709         if(this.legend){
42710             this.setLegend(this.legend);
42711         }
42712     },
42713
42714     // private
42715     setLegend : function(text){
42716         if(this.rendered){
42717             this.el.child('legend').update(text);
42718         }
42719     }
42720 });/*
42721  * Based on:
42722  * Ext JS Library 1.1.1
42723  * Copyright(c) 2006-2007, Ext JS, LLC.
42724  *
42725  * Originally Released Under LGPL - original licence link has changed is not relivant.
42726  *
42727  * Fork - LGPL
42728  * <script type="text/javascript">
42729  */
42730 /**
42731  * @class Roo.form.VTypes
42732  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42733  * @singleton
42734  */
42735 Roo.form.VTypes = function(){
42736     // closure these in so they are only created once.
42737     var alpha = /^[a-zA-Z_]+$/;
42738     var alphanum = /^[a-zA-Z0-9_]+$/;
42739     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42740     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42741
42742     // All these messages and functions are configurable
42743     return {
42744         /**
42745          * The function used to validate email addresses
42746          * @param {String} value The email address
42747          */
42748         'email' : function(v){
42749             return email.test(v);
42750         },
42751         /**
42752          * The error text to display when the email validation function returns false
42753          * @type String
42754          */
42755         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42756         /**
42757          * The keystroke filter mask to be applied on email input
42758          * @type RegExp
42759          */
42760         'emailMask' : /[a-z0-9_\.\-@]/i,
42761
42762         /**
42763          * The function used to validate URLs
42764          * @param {String} value The URL
42765          */
42766         'url' : function(v){
42767             return url.test(v);
42768         },
42769         /**
42770          * The error text to display when the url validation function returns false
42771          * @type String
42772          */
42773         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42774         
42775         /**
42776          * The function used to validate alpha values
42777          * @param {String} value The value
42778          */
42779         'alpha' : function(v){
42780             return alpha.test(v);
42781         },
42782         /**
42783          * The error text to display when the alpha validation function returns false
42784          * @type String
42785          */
42786         'alphaText' : 'This field should only contain letters and _',
42787         /**
42788          * The keystroke filter mask to be applied on alpha input
42789          * @type RegExp
42790          */
42791         'alphaMask' : /[a-z_]/i,
42792
42793         /**
42794          * The function used to validate alphanumeric values
42795          * @param {String} value The value
42796          */
42797         'alphanum' : function(v){
42798             return alphanum.test(v);
42799         },
42800         /**
42801          * The error text to display when the alphanumeric validation function returns false
42802          * @type String
42803          */
42804         'alphanumText' : 'This field should only contain letters, numbers and _',
42805         /**
42806          * The keystroke filter mask to be applied on alphanumeric input
42807          * @type RegExp
42808          */
42809         'alphanumMask' : /[a-z0-9_]/i
42810     };
42811 }();//<script type="text/javascript">
42812
42813 /**
42814  * @class Roo.form.FCKeditor
42815  * @extends Roo.form.TextArea
42816  * Wrapper around the FCKEditor http://www.fckeditor.net
42817  * @constructor
42818  * Creates a new FCKeditor
42819  * @param {Object} config Configuration options
42820  */
42821 Roo.form.FCKeditor = function(config){
42822     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42823     this.addEvents({
42824          /**
42825          * @event editorinit
42826          * Fired when the editor is initialized - you can add extra handlers here..
42827          * @param {FCKeditor} this
42828          * @param {Object} the FCK object.
42829          */
42830         editorinit : true
42831     });
42832     
42833     
42834 };
42835 Roo.form.FCKeditor.editors = { };
42836 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42837 {
42838     //defaultAutoCreate : {
42839     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42840     //},
42841     // private
42842     /**
42843      * @cfg {Object} fck options - see fck manual for details.
42844      */
42845     fckconfig : false,
42846     
42847     /**
42848      * @cfg {Object} fck toolbar set (Basic or Default)
42849      */
42850     toolbarSet : 'Basic',
42851     /**
42852      * @cfg {Object} fck BasePath
42853      */ 
42854     basePath : '/fckeditor/',
42855     
42856     
42857     frame : false,
42858     
42859     value : '',
42860     
42861    
42862     onRender : function(ct, position)
42863     {
42864         if(!this.el){
42865             this.defaultAutoCreate = {
42866                 tag: "textarea",
42867                 style:"width:300px;height:60px;",
42868                 autocomplete: "off"
42869             };
42870         }
42871         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42872         /*
42873         if(this.grow){
42874             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42875             if(this.preventScrollbars){
42876                 this.el.setStyle("overflow", "hidden");
42877             }
42878             this.el.setHeight(this.growMin);
42879         }
42880         */
42881         //console.log('onrender' + this.getId() );
42882         Roo.form.FCKeditor.editors[this.getId()] = this;
42883          
42884
42885         this.replaceTextarea() ;
42886         
42887     },
42888     
42889     getEditor : function() {
42890         return this.fckEditor;
42891     },
42892     /**
42893      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42894      * @param {Mixed} value The value to set
42895      */
42896     
42897     
42898     setValue : function(value)
42899     {
42900         //console.log('setValue: ' + value);
42901         
42902         if(typeof(value) == 'undefined') { // not sure why this is happending...
42903             return;
42904         }
42905         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42906         
42907         //if(!this.el || !this.getEditor()) {
42908         //    this.value = value;
42909             //this.setValue.defer(100,this,[value]);    
42910         //    return;
42911         //} 
42912         
42913         if(!this.getEditor()) {
42914             return;
42915         }
42916         
42917         this.getEditor().SetData(value);
42918         
42919         //
42920
42921     },
42922
42923     /**
42924      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42925      * @return {Mixed} value The field value
42926      */
42927     getValue : function()
42928     {
42929         
42930         if (this.frame && this.frame.dom.style.display == 'none') {
42931             return Roo.form.FCKeditor.superclass.getValue.call(this);
42932         }
42933         
42934         if(!this.el || !this.getEditor()) {
42935            
42936            // this.getValue.defer(100,this); 
42937             return this.value;
42938         }
42939        
42940         
42941         var value=this.getEditor().GetData();
42942         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42943         return Roo.form.FCKeditor.superclass.getValue.call(this);
42944         
42945
42946     },
42947
42948     /**
42949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42950      * @return {Mixed} value The field value
42951      */
42952     getRawValue : function()
42953     {
42954         if (this.frame && this.frame.dom.style.display == 'none') {
42955             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42956         }
42957         
42958         if(!this.el || !this.getEditor()) {
42959             //this.getRawValue.defer(100,this); 
42960             return this.value;
42961             return;
42962         }
42963         
42964         
42965         
42966         var value=this.getEditor().GetData();
42967         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42968         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42969          
42970     },
42971     
42972     setSize : function(w,h) {
42973         
42974         
42975         
42976         //if (this.frame && this.frame.dom.style.display == 'none') {
42977         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42978         //    return;
42979         //}
42980         //if(!this.el || !this.getEditor()) {
42981         //    this.setSize.defer(100,this, [w,h]); 
42982         //    return;
42983         //}
42984         
42985         
42986         
42987         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42988         
42989         this.frame.dom.setAttribute('width', w);
42990         this.frame.dom.setAttribute('height', h);
42991         this.frame.setSize(w,h);
42992         
42993     },
42994     
42995     toggleSourceEdit : function(value) {
42996         
42997       
42998          
42999         this.el.dom.style.display = value ? '' : 'none';
43000         this.frame.dom.style.display = value ?  'none' : '';
43001         
43002     },
43003     
43004     
43005     focus: function(tag)
43006     {
43007         if (this.frame.dom.style.display == 'none') {
43008             return Roo.form.FCKeditor.superclass.focus.call(this);
43009         }
43010         if(!this.el || !this.getEditor()) {
43011             this.focus.defer(100,this, [tag]); 
43012             return;
43013         }
43014         
43015         
43016         
43017         
43018         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43019         this.getEditor().Focus();
43020         if (tgs.length) {
43021             if (!this.getEditor().Selection.GetSelection()) {
43022                 this.focus.defer(100,this, [tag]); 
43023                 return;
43024             }
43025             
43026             
43027             var r = this.getEditor().EditorDocument.createRange();
43028             r.setStart(tgs[0],0);
43029             r.setEnd(tgs[0],0);
43030             this.getEditor().Selection.GetSelection().removeAllRanges();
43031             this.getEditor().Selection.GetSelection().addRange(r);
43032             this.getEditor().Focus();
43033         }
43034         
43035     },
43036     
43037     
43038     
43039     replaceTextarea : function()
43040     {
43041         if ( document.getElementById( this.getId() + '___Frame' ) )
43042             return ;
43043         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43044         //{
43045             // We must check the elements firstly using the Id and then the name.
43046         var oTextarea = document.getElementById( this.getId() );
43047         
43048         var colElementsByName = document.getElementsByName( this.getId() ) ;
43049          
43050         oTextarea.style.display = 'none' ;
43051
43052         if ( oTextarea.tabIndex ) {            
43053             this.TabIndex = oTextarea.tabIndex ;
43054         }
43055         
43056         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43057         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43058         this.frame = Roo.get(this.getId() + '___Frame')
43059     },
43060     
43061     _getConfigHtml : function()
43062     {
43063         var sConfig = '' ;
43064
43065         for ( var o in this.fckconfig ) {
43066             sConfig += sConfig.length > 0  ? '&amp;' : '';
43067             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43068         }
43069
43070         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43071     },
43072     
43073     
43074     _getIFrameHtml : function()
43075     {
43076         var sFile = 'fckeditor.html' ;
43077         /* no idea what this is about..
43078         try
43079         {
43080             if ( (/fcksource=true/i).test( window.top.location.search ) )
43081                 sFile = 'fckeditor.original.html' ;
43082         }
43083         catch (e) { 
43084         */
43085
43086         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43087         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43088         
43089         
43090         var html = '<iframe id="' + this.getId() +
43091             '___Frame" src="' + sLink +
43092             '" width="' + this.width +
43093             '" height="' + this.height + '"' +
43094             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43095             ' frameborder="0" scrolling="no"></iframe>' ;
43096
43097         return html ;
43098     },
43099     
43100     _insertHtmlBefore : function( html, element )
43101     {
43102         if ( element.insertAdjacentHTML )       {
43103             // IE
43104             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43105         } else { // Gecko
43106             var oRange = document.createRange() ;
43107             oRange.setStartBefore( element ) ;
43108             var oFragment = oRange.createContextualFragment( html );
43109             element.parentNode.insertBefore( oFragment, element ) ;
43110         }
43111     }
43112     
43113     
43114   
43115     
43116     
43117     
43118     
43119
43120 });
43121
43122 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43123
43124 function FCKeditor_OnComplete(editorInstance){
43125     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43126     f.fckEditor = editorInstance;
43127     //console.log("loaded");
43128     f.fireEvent('editorinit', f, editorInstance);
43129
43130   
43131
43132  
43133
43134
43135
43136
43137
43138
43139
43140
43141
43142
43143
43144
43145
43146
43147
43148 //<script type="text/javascript">
43149 /**
43150  * @class Roo.form.GridField
43151  * @extends Roo.form.Field
43152  * Embed a grid (or editable grid into a form)
43153  * STATUS ALPHA
43154  * 
43155  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43156  * it needs 
43157  * xgrid.store = Roo.data.Store
43158  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43159  * xgrid.store.reader = Roo.data.JsonReader 
43160  * 
43161  * 
43162  * @constructor
43163  * Creates a new GridField
43164  * @param {Object} config Configuration options
43165  */
43166 Roo.form.GridField = function(config){
43167     Roo.form.GridField.superclass.constructor.call(this, config);
43168      
43169 };
43170
43171 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43172     /**
43173      * @cfg {Number} width  - used to restrict width of grid..
43174      */
43175     width : 100,
43176     /**
43177      * @cfg {Number} height - used to restrict height of grid..
43178      */
43179     height : 50,
43180      /**
43181      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43182          * 
43183          *}
43184      */
43185     xgrid : false, 
43186     /**
43187      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43188      * {tag: "input", type: "checkbox", autocomplete: "off"})
43189      */
43190    // defaultAutoCreate : { tag: 'div' },
43191     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43192     /**
43193      * @cfg {String} addTitle Text to include for adding a title.
43194      */
43195     addTitle : false,
43196     //
43197     onResize : function(){
43198         Roo.form.Field.superclass.onResize.apply(this, arguments);
43199     },
43200
43201     initEvents : function(){
43202         // Roo.form.Checkbox.superclass.initEvents.call(this);
43203         // has no events...
43204        
43205     },
43206
43207
43208     getResizeEl : function(){
43209         return this.wrap;
43210     },
43211
43212     getPositionEl : function(){
43213         return this.wrap;
43214     },
43215
43216     // private
43217     onRender : function(ct, position){
43218         
43219         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43220         var style = this.style;
43221         delete this.style;
43222         
43223         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43224         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43225         this.viewEl = this.wrap.createChild({ tag: 'div' });
43226         if (style) {
43227             this.viewEl.applyStyles(style);
43228         }
43229         if (this.width) {
43230             this.viewEl.setWidth(this.width);
43231         }
43232         if (this.height) {
43233             this.viewEl.setHeight(this.height);
43234         }
43235         //if(this.inputValue !== undefined){
43236         //this.setValue(this.value);
43237         
43238         
43239         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43240         
43241         
43242         this.grid.render();
43243         this.grid.getDataSource().on('remove', this.refreshValue, this);
43244         this.grid.getDataSource().on('update', this.refreshValue, this);
43245         this.grid.on('afteredit', this.refreshValue, this);
43246  
43247     },
43248      
43249     
43250     /**
43251      * Sets the value of the item. 
43252      * @param {String} either an object  or a string..
43253      */
43254     setValue : function(v){
43255         //this.value = v;
43256         v = v || []; // empty set..
43257         // this does not seem smart - it really only affects memoryproxy grids..
43258         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43259             var ds = this.grid.getDataSource();
43260             // assumes a json reader..
43261             var data = {}
43262             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43263             ds.loadData( data);
43264         }
43265         // clear selection so it does not get stale.
43266         if (this.grid.sm) { 
43267             this.grid.sm.clearSelections();
43268         }
43269         
43270         Roo.form.GridField.superclass.setValue.call(this, v);
43271         this.refreshValue();
43272         // should load data in the grid really....
43273     },
43274     
43275     // private
43276     refreshValue: function() {
43277          var val = [];
43278         this.grid.getDataSource().each(function(r) {
43279             val.push(r.data);
43280         });
43281         this.el.dom.value = Roo.encode(val);
43282     }
43283     
43284      
43285     
43286     
43287 });/*
43288  * Based on:
43289  * Ext JS Library 1.1.1
43290  * Copyright(c) 2006-2007, Ext JS, LLC.
43291  *
43292  * Originally Released Under LGPL - original licence link has changed is not relivant.
43293  *
43294  * Fork - LGPL
43295  * <script type="text/javascript">
43296  */
43297 /**
43298  * @class Roo.form.DisplayField
43299  * @extends Roo.form.Field
43300  * A generic Field to display non-editable data.
43301  * @constructor
43302  * Creates a new Display Field item.
43303  * @param {Object} config Configuration options
43304  */
43305 Roo.form.DisplayField = function(config){
43306     Roo.form.DisplayField.superclass.constructor.call(this, config);
43307     
43308 };
43309
43310 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43311     inputType:      'hidden',
43312     allowBlank:     true,
43313     readOnly:         true,
43314     
43315  
43316     /**
43317      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43318      */
43319     focusClass : undefined,
43320     /**
43321      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43322      */
43323     fieldClass: 'x-form-field',
43324     
43325      /**
43326      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43327      */
43328     valueRenderer: undefined,
43329     
43330     width: 100,
43331     /**
43332      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43333      * {tag: "input", type: "checkbox", autocomplete: "off"})
43334      */
43335      
43336  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43337
43338     onResize : function(){
43339         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43340         
43341     },
43342
43343     initEvents : function(){
43344         // Roo.form.Checkbox.superclass.initEvents.call(this);
43345         // has no events...
43346        
43347     },
43348
43349
43350     getResizeEl : function(){
43351         return this.wrap;
43352     },
43353
43354     getPositionEl : function(){
43355         return this.wrap;
43356     },
43357
43358     // private
43359     onRender : function(ct, position){
43360         
43361         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43362         //if(this.inputValue !== undefined){
43363         this.wrap = this.el.wrap();
43364         
43365         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43366         
43367         if (this.bodyStyle) {
43368             this.viewEl.applyStyles(this.bodyStyle);
43369         }
43370         //this.viewEl.setStyle('padding', '2px');
43371         
43372         this.setValue(this.value);
43373         
43374     },
43375 /*
43376     // private
43377     initValue : Roo.emptyFn,
43378
43379   */
43380
43381         // private
43382     onClick : function(){
43383         
43384     },
43385
43386     /**
43387      * Sets the checked state of the checkbox.
43388      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43389      */
43390     setValue : function(v){
43391         this.value = v;
43392         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43393         // this might be called before we have a dom element..
43394         if (!this.viewEl) {
43395             return;
43396         }
43397         this.viewEl.dom.innerHTML = html;
43398         Roo.form.DisplayField.superclass.setValue.call(this, v);
43399
43400     }
43401 });/*
43402  * 
43403  * Licence- LGPL
43404  * 
43405  */
43406
43407 /**
43408  * @class Roo.form.DayPicker
43409  * @extends Roo.form.Field
43410  * A Day picker show [M] [T] [W] ....
43411  * @constructor
43412  * Creates a new Day Picker
43413  * @param {Object} config Configuration options
43414  */
43415 Roo.form.DayPicker= function(config){
43416     Roo.form.DayPicker.superclass.constructor.call(this, config);
43417      
43418 };
43419
43420 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43421     /**
43422      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43423      */
43424     focusClass : undefined,
43425     /**
43426      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43427      */
43428     fieldClass: "x-form-field",
43429    
43430     /**
43431      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43432      * {tag: "input", type: "checkbox", autocomplete: "off"})
43433      */
43434     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43435     
43436    
43437     actionMode : 'viewEl', 
43438     //
43439     // private
43440  
43441     inputType : 'hidden',
43442     
43443      
43444     inputElement: false, // real input element?
43445     basedOn: false, // ????
43446     
43447     isFormField: true, // not sure where this is needed!!!!
43448
43449     onResize : function(){
43450         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43451         if(!this.boxLabel){
43452             this.el.alignTo(this.wrap, 'c-c');
43453         }
43454     },
43455
43456     initEvents : function(){
43457         Roo.form.Checkbox.superclass.initEvents.call(this);
43458         this.el.on("click", this.onClick,  this);
43459         this.el.on("change", this.onClick,  this);
43460     },
43461
43462
43463     getResizeEl : function(){
43464         return this.wrap;
43465     },
43466
43467     getPositionEl : function(){
43468         return this.wrap;
43469     },
43470
43471     
43472     // private
43473     onRender : function(ct, position){
43474         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43475        
43476         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43477         
43478         var r1 = '<table><tr>';
43479         var r2 = '<tr class="x-form-daypick-icons">';
43480         for (var i=0; i < 7; i++) {
43481             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43482             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43483         }
43484         
43485         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43486         viewEl.select('img').on('click', this.onClick, this);
43487         this.viewEl = viewEl;   
43488         
43489         
43490         // this will not work on Chrome!!!
43491         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43492         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43493         
43494         
43495           
43496
43497     },
43498
43499     // private
43500     initValue : Roo.emptyFn,
43501
43502     /**
43503      * Returns the checked state of the checkbox.
43504      * @return {Boolean} True if checked, else false
43505      */
43506     getValue : function(){
43507         return this.el.dom.value;
43508         
43509     },
43510
43511         // private
43512     onClick : function(e){ 
43513         //this.setChecked(!this.checked);
43514         Roo.get(e.target).toggleClass('x-menu-item-checked');
43515         this.refreshValue();
43516         //if(this.el.dom.checked != this.checked){
43517         //    this.setValue(this.el.dom.checked);
43518        // }
43519     },
43520     
43521     // private
43522     refreshValue : function()
43523     {
43524         var val = '';
43525         this.viewEl.select('img',true).each(function(e,i,n)  {
43526             val += e.is(".x-menu-item-checked") ? String(n) : '';
43527         });
43528         this.setValue(val, true);
43529     },
43530
43531     /**
43532      * Sets the checked state of the checkbox.
43533      * On is always based on a string comparison between inputValue and the param.
43534      * @param {Boolean/String} value - the value to set 
43535      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43536      */
43537     setValue : function(v,suppressEvent){
43538         if (!this.el.dom) {
43539             return;
43540         }
43541         var old = this.el.dom.value ;
43542         this.el.dom.value = v;
43543         if (suppressEvent) {
43544             return ;
43545         }
43546          
43547         // update display..
43548         this.viewEl.select('img',true).each(function(e,i,n)  {
43549             
43550             var on = e.is(".x-menu-item-checked");
43551             var newv = v.indexOf(String(n)) > -1;
43552             if (on != newv) {
43553                 e.toggleClass('x-menu-item-checked');
43554             }
43555             
43556         });
43557         
43558         
43559         this.fireEvent('change', this, v, old);
43560         
43561         
43562     },
43563    
43564     // handle setting of hidden value by some other method!!?!?
43565     setFromHidden: function()
43566     {
43567         if(!this.el){
43568             return;
43569         }
43570         //console.log("SET FROM HIDDEN");
43571         //alert('setFrom hidden');
43572         this.setValue(this.el.dom.value);
43573     },
43574     
43575     onDestroy : function()
43576     {
43577         if(this.viewEl){
43578             Roo.get(this.viewEl).remove();
43579         }
43580          
43581         Roo.form.DayPicker.superclass.onDestroy.call(this);
43582     }
43583
43584 });/*
43585  * RooJS Library 1.1.1
43586  * Copyright(c) 2008-2011  Alan Knowles
43587  *
43588  * License - LGPL
43589  */
43590  
43591
43592 /**
43593  * @class Roo.form.ComboCheck
43594  * @extends Roo.form.ComboBox
43595  * A combobox for multiple select items.
43596  *
43597  * FIXME - could do with a reset button..
43598  * 
43599  * @constructor
43600  * Create a new ComboCheck
43601  * @param {Object} config Configuration options
43602  */
43603 Roo.form.ComboCheck = function(config){
43604     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43605     // should verify some data...
43606     // like
43607     // hiddenName = required..
43608     // displayField = required
43609     // valudField == required
43610     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43611     var _t = this;
43612     Roo.each(req, function(e) {
43613         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43614             throw "Roo.form.ComboCheck : missing value for: " + e;
43615         }
43616     });
43617     
43618     
43619 };
43620
43621 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43622      
43623      
43624     editable : false,
43625      
43626     selectedClass: 'x-menu-item-checked', 
43627     
43628     // private
43629     onRender : function(ct, position){
43630         var _t = this;
43631         
43632         
43633         
43634         if(!this.tpl){
43635             var cls = 'x-combo-list';
43636
43637             
43638             this.tpl =  new Roo.Template({
43639                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43640                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43641                    '<span>{' + this.displayField + '}</span>' +
43642                     '</div>' 
43643                 
43644             });
43645         }
43646  
43647         
43648         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43649         this.view.singleSelect = false;
43650         this.view.multiSelect = true;
43651         this.view.toggleSelect = true;
43652         this.pageTb.add(new Roo.Toolbar.Fill(), {
43653             
43654             text: 'Done',
43655             handler: function()
43656             {
43657                 _t.collapse();
43658             }
43659         });
43660     },
43661     
43662     onViewOver : function(e, t){
43663         // do nothing...
43664         return;
43665         
43666     },
43667     
43668     onViewClick : function(doFocus,index){
43669         return;
43670         
43671     },
43672     select: function () {
43673         //Roo.log("SELECT CALLED");
43674     },
43675      
43676     selectByValue : function(xv, scrollIntoView){
43677         var ar = this.getValueArray();
43678         var sels = [];
43679         
43680         Roo.each(ar, function(v) {
43681             if(v === undefined || v === null){
43682                 return;
43683             }
43684             var r = this.findRecord(this.valueField, v);
43685             if(r){
43686                 sels.push(this.store.indexOf(r))
43687                 
43688             }
43689         },this);
43690         this.view.select(sels);
43691         return false;
43692     },
43693     
43694     
43695     
43696     onSelect : function(record, index){
43697        // Roo.log("onselect Called");
43698        // this is only called by the clear button now..
43699         this.view.clearSelections();
43700         this.setValue('[]');
43701         if (this.value != this.valueBefore) {
43702             this.fireEvent('change', this, this.value, this.valueBefore);
43703         }
43704     },
43705     getValueArray : function()
43706     {
43707         var ar = [] ;
43708         
43709         try {
43710             Roo.log(this.value);
43711             var ar = Roo.decode(this.value);
43712             return  ar instanceof Array ? ar : []; //?? valid?
43713             
43714         } catch(e) {
43715             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43716             return [];
43717         }
43718          
43719     },
43720     expand : function ()
43721     {
43722         Roo.form.ComboCheck.superclass.expand.call(this);
43723         this.valueBefore = this.value;
43724         
43725
43726     },
43727     
43728     collapse : function(){
43729         Roo.form.ComboCheck.superclass.collapse.call(this);
43730         var sl = this.view.getSelectedIndexes();
43731         var st = this.store;
43732         var nv = [];
43733         var tv = [];
43734         var r;
43735         Roo.each(sl, function(i) {
43736             r = st.getAt(i);
43737             nv.push(r.get(this.valueField));
43738         },this);
43739         this.setValue(Roo.encode(nv));
43740         if (this.value != this.valueBefore) {
43741
43742             this.fireEvent('change', this, this.value, this.valueBefore);
43743         }
43744         
43745     },
43746     
43747     setValue : function(v){
43748         // Roo.log(v);
43749         this.value = v;
43750         
43751         var vals = this.getValueArray();
43752         var tv = [];
43753         Roo.each(vals, function(k) {
43754             var r = this.findRecord(this.valueField, k);
43755             if(r){
43756                 tv.push(r.data[this.displayField]);
43757             }else if(this.valueNotFoundText !== undefined){
43758                 tv.push( this.valueNotFoundText );
43759             }
43760         },this);
43761        // Roo.log(tv);
43762         
43763         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43764         this.hiddenField.value = v;
43765         this.value = v;
43766     }
43767     
43768 });//<script type="text/javasscript">
43769  
43770
43771 /**
43772  * @class Roo.DDView
43773  * A DnD enabled version of Roo.View.
43774  * @param {Element/String} container The Element in which to create the View.
43775  * @param {String} tpl The template string used to create the markup for each element of the View
43776  * @param {Object} config The configuration properties. These include all the config options of
43777  * {@link Roo.View} plus some specific to this class.<br>
43778  * <p>
43779  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43780  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43781  * <p>
43782  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43783 .x-view-drag-insert-above {
43784         border-top:1px dotted #3366cc;
43785 }
43786 .x-view-drag-insert-below {
43787         border-bottom:1px dotted #3366cc;
43788 }
43789 </code></pre>
43790  * 
43791  */
43792  
43793 Roo.DDView = function(container, tpl, config) {
43794     Roo.DDView.superclass.constructor.apply(this, arguments);
43795     this.getEl().setStyle("outline", "0px none");
43796     this.getEl().unselectable();
43797     if (this.dragGroup) {
43798                 this.setDraggable(this.dragGroup.split(","));
43799     }
43800     if (this.dropGroup) {
43801                 this.setDroppable(this.dropGroup.split(","));
43802     }
43803     if (this.deletable) {
43804         this.setDeletable();
43805     }
43806     this.isDirtyFlag = false;
43807         this.addEvents({
43808                 "drop" : true
43809         });
43810 };
43811
43812 Roo.extend(Roo.DDView, Roo.View, {
43813 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43814 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43815 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43816 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43817
43818         isFormField: true,
43819
43820         reset: Roo.emptyFn,
43821         
43822         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43823
43824         validate: function() {
43825                 return true;
43826         },
43827         
43828         destroy: function() {
43829                 this.purgeListeners();
43830                 this.getEl.removeAllListeners();
43831                 this.getEl().remove();
43832                 if (this.dragZone) {
43833                         if (this.dragZone.destroy) {
43834                                 this.dragZone.destroy();
43835                         }
43836                 }
43837                 if (this.dropZone) {
43838                         if (this.dropZone.destroy) {
43839                                 this.dropZone.destroy();
43840                         }
43841                 }
43842         },
43843
43844 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43845         getName: function() {
43846                 return this.name;
43847         },
43848
43849 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43850         setValue: function(v) {
43851                 if (!this.store) {
43852                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43853                 }
43854                 var data = {};
43855                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43856                 this.store.proxy = new Roo.data.MemoryProxy(data);
43857                 this.store.load();
43858         },
43859
43860 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43861         getValue: function() {
43862                 var result = '(';
43863                 this.store.each(function(rec) {
43864                         result += rec.id + ',';
43865                 });
43866                 return result.substr(0, result.length - 1) + ')';
43867         },
43868         
43869         getIds: function() {
43870                 var i = 0, result = new Array(this.store.getCount());
43871                 this.store.each(function(rec) {
43872                         result[i++] = rec.id;
43873                 });
43874                 return result;
43875         },
43876         
43877         isDirty: function() {
43878                 return this.isDirtyFlag;
43879         },
43880
43881 /**
43882  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43883  *      whole Element becomes the target, and this causes the drop gesture to append.
43884  */
43885     getTargetFromEvent : function(e) {
43886                 var target = e.getTarget();
43887                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43888                 target = target.parentNode;
43889                 }
43890                 if (!target) {
43891                         target = this.el.dom.lastChild || this.el.dom;
43892                 }
43893                 return target;
43894     },
43895
43896 /**
43897  *      Create the drag data which consists of an object which has the property "ddel" as
43898  *      the drag proxy element. 
43899  */
43900     getDragData : function(e) {
43901         var target = this.findItemFromChild(e.getTarget());
43902                 if(target) {
43903                         this.handleSelection(e);
43904                         var selNodes = this.getSelectedNodes();
43905             var dragData = {
43906                 source: this,
43907                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43908                 nodes: selNodes,
43909                 records: []
43910                         };
43911                         var selectedIndices = this.getSelectedIndexes();
43912                         for (var i = 0; i < selectedIndices.length; i++) {
43913                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43914                         }
43915                         if (selNodes.length == 1) {
43916                                 dragData.ddel = target.cloneNode(true); // the div element
43917                         } else {
43918                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43919                                 div.className = 'multi-proxy';
43920                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43921                                         div.appendChild(selNodes[i].cloneNode(true));
43922                                 }
43923                                 dragData.ddel = div;
43924                         }
43925             //console.log(dragData)
43926             //console.log(dragData.ddel.innerHTML)
43927                         return dragData;
43928                 }
43929         //console.log('nodragData')
43930                 return false;
43931     },
43932     
43933 /**     Specify to which ddGroup items in this DDView may be dragged. */
43934     setDraggable: function(ddGroup) {
43935         if (ddGroup instanceof Array) {
43936                 Roo.each(ddGroup, this.setDraggable, this);
43937                 return;
43938         }
43939         if (this.dragZone) {
43940                 this.dragZone.addToGroup(ddGroup);
43941         } else {
43942                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43943                                 containerScroll: true,
43944                                 ddGroup: ddGroup 
43945
43946                         });
43947 //                      Draggability implies selection. DragZone's mousedown selects the element.
43948                         if (!this.multiSelect) { this.singleSelect = true; }
43949
43950 //                      Wire the DragZone's handlers up to methods in *this*
43951                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43952                 }
43953     },
43954
43955 /**     Specify from which ddGroup this DDView accepts drops. */
43956     setDroppable: function(ddGroup) {
43957         if (ddGroup instanceof Array) {
43958                 Roo.each(ddGroup, this.setDroppable, this);
43959                 return;
43960         }
43961         if (this.dropZone) {
43962                 this.dropZone.addToGroup(ddGroup);
43963         } else {
43964                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43965                                 containerScroll: true,
43966                                 ddGroup: ddGroup
43967                         });
43968
43969 //                      Wire the DropZone's handlers up to methods in *this*
43970                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43971                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43972                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43973                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43974                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43975                 }
43976     },
43977
43978 /**     Decide whether to drop above or below a View node. */
43979     getDropPoint : function(e, n, dd){
43980         if (n == this.el.dom) { return "above"; }
43981                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43982                 var c = t + (b - t) / 2;
43983                 var y = Roo.lib.Event.getPageY(e);
43984                 if(y <= c) {
43985                         return "above";
43986                 }else{
43987                         return "below";
43988                 }
43989     },
43990
43991     onNodeEnter : function(n, dd, e, data){
43992                 return false;
43993     },
43994     
43995     onNodeOver : function(n, dd, e, data){
43996                 var pt = this.getDropPoint(e, n, dd);
43997                 // set the insert point style on the target node
43998                 var dragElClass = this.dropNotAllowed;
43999                 if (pt) {
44000                         var targetElClass;
44001                         if (pt == "above"){
44002                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44003                                 targetElClass = "x-view-drag-insert-above";
44004                         } else {
44005                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44006                                 targetElClass = "x-view-drag-insert-below";
44007                         }
44008                         if (this.lastInsertClass != targetElClass){
44009                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44010                                 this.lastInsertClass = targetElClass;
44011                         }
44012                 }
44013                 return dragElClass;
44014         },
44015
44016     onNodeOut : function(n, dd, e, data){
44017                 this.removeDropIndicators(n);
44018     },
44019
44020     onNodeDrop : function(n, dd, e, data){
44021         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44022                 return false;
44023         }
44024         var pt = this.getDropPoint(e, n, dd);
44025                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44026                 if (pt == "below") { insertAt++; }
44027                 for (var i = 0; i < data.records.length; i++) {
44028                         var r = data.records[i];
44029                         var dup = this.store.getById(r.id);
44030                         if (dup && (dd != this.dragZone)) {
44031                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44032                         } else {
44033                                 if (data.copy) {
44034                                         this.store.insert(insertAt++, r.copy());
44035                                 } else {
44036                                         data.source.isDirtyFlag = true;
44037                                         r.store.remove(r);
44038                                         this.store.insert(insertAt++, r);
44039                                 }
44040                                 this.isDirtyFlag = true;
44041                         }
44042                 }
44043                 this.dragZone.cachedTarget = null;
44044                 return true;
44045     },
44046
44047     removeDropIndicators : function(n){
44048                 if(n){
44049                         Roo.fly(n).removeClass([
44050                                 "x-view-drag-insert-above",
44051                                 "x-view-drag-insert-below"]);
44052                         this.lastInsertClass = "_noclass";
44053                 }
44054     },
44055
44056 /**
44057  *      Utility method. Add a delete option to the DDView's context menu.
44058  *      @param {String} imageUrl The URL of the "delete" icon image.
44059  */
44060         setDeletable: function(imageUrl) {
44061                 if (!this.singleSelect && !this.multiSelect) {
44062                         this.singleSelect = true;
44063                 }
44064                 var c = this.getContextMenu();
44065                 this.contextMenu.on("itemclick", function(item) {
44066                         switch (item.id) {
44067                                 case "delete":
44068                                         this.remove(this.getSelectedIndexes());
44069                                         break;
44070                         }
44071                 }, this);
44072                 this.contextMenu.add({
44073                         icon: imageUrl,
44074                         id: "delete",
44075                         text: 'Delete'
44076                 });
44077         },
44078         
44079 /**     Return the context menu for this DDView. */
44080         getContextMenu: function() {
44081                 if (!this.contextMenu) {
44082 //                      Create the View's context menu
44083                         this.contextMenu = new Roo.menu.Menu({
44084                                 id: this.id + "-contextmenu"
44085                         });
44086                         this.el.on("contextmenu", this.showContextMenu, this);
44087                 }
44088                 return this.contextMenu;
44089         },
44090         
44091         disableContextMenu: function() {
44092                 if (this.contextMenu) {
44093                         this.el.un("contextmenu", this.showContextMenu, this);
44094                 }
44095         },
44096
44097         showContextMenu: function(e, item) {
44098         item = this.findItemFromChild(e.getTarget());
44099                 if (item) {
44100                         e.stopEvent();
44101                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44102                         this.contextMenu.showAt(e.getXY());
44103             }
44104     },
44105
44106 /**
44107  *      Remove {@link Roo.data.Record}s at the specified indices.
44108  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44109  */
44110     remove: function(selectedIndices) {
44111                 selectedIndices = [].concat(selectedIndices);
44112                 for (var i = 0; i < selectedIndices.length; i++) {
44113                         var rec = this.store.getAt(selectedIndices[i]);
44114                         this.store.remove(rec);
44115                 }
44116     },
44117
44118 /**
44119  *      Double click fires the event, but also, if this is draggable, and there is only one other
44120  *      related DropZone, it transfers the selected node.
44121  */
44122     onDblClick : function(e){
44123         var item = this.findItemFromChild(e.getTarget());
44124         if(item){
44125             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44126                 return false;
44127             }
44128             if (this.dragGroup) {
44129                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44130                     while (targets.indexOf(this.dropZone) > -1) {
44131                             targets.remove(this.dropZone);
44132                                 }
44133                     if (targets.length == 1) {
44134                                         this.dragZone.cachedTarget = null;
44135                         var el = Roo.get(targets[0].getEl());
44136                         var box = el.getBox(true);
44137                         targets[0].onNodeDrop(el.dom, {
44138                                 target: el.dom,
44139                                 xy: [box.x, box.y + box.height - 1]
44140                         }, null, this.getDragData(e));
44141                     }
44142                 }
44143         }
44144     },
44145     
44146     handleSelection: function(e) {
44147                 this.dragZone.cachedTarget = null;
44148         var item = this.findItemFromChild(e.getTarget());
44149         if (!item) {
44150                 this.clearSelections(true);
44151                 return;
44152         }
44153                 if (item && (this.multiSelect || this.singleSelect)){
44154                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44155                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44156                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44157                                 this.unselect(item);
44158                         } else {
44159                                 this.select(item, this.multiSelect && e.ctrlKey);
44160                                 this.lastSelection = item;
44161                         }
44162                 }
44163     },
44164
44165     onItemClick : function(item, index, e){
44166                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44167                         return false;
44168                 }
44169                 return true;
44170     },
44171
44172     unselect : function(nodeInfo, suppressEvent){
44173                 var node = this.getNode(nodeInfo);
44174                 if(node && this.isSelected(node)){
44175                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44176                                 Roo.fly(node).removeClass(this.selectedClass);
44177                                 this.selections.remove(node);
44178                                 if(!suppressEvent){
44179                                         this.fireEvent("selectionchange", this, this.selections);
44180                                 }
44181                         }
44182                 }
44183     }
44184 });
44185 /*
44186  * Based on:
44187  * Ext JS Library 1.1.1
44188  * Copyright(c) 2006-2007, Ext JS, LLC.
44189  *
44190  * Originally Released Under LGPL - original licence link has changed is not relivant.
44191  *
44192  * Fork - LGPL
44193  * <script type="text/javascript">
44194  */
44195  
44196 /**
44197  * @class Roo.LayoutManager
44198  * @extends Roo.util.Observable
44199  * Base class for layout managers.
44200  */
44201 Roo.LayoutManager = function(container, config){
44202     Roo.LayoutManager.superclass.constructor.call(this);
44203     this.el = Roo.get(container);
44204     // ie scrollbar fix
44205     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44206         document.body.scroll = "no";
44207     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44208         this.el.position('relative');
44209     }
44210     this.id = this.el.id;
44211     this.el.addClass("x-layout-container");
44212     /** false to disable window resize monitoring @type Boolean */
44213     this.monitorWindowResize = true;
44214     this.regions = {};
44215     this.addEvents({
44216         /**
44217          * @event layout
44218          * Fires when a layout is performed. 
44219          * @param {Roo.LayoutManager} this
44220          */
44221         "layout" : true,
44222         /**
44223          * @event regionresized
44224          * Fires when the user resizes a region. 
44225          * @param {Roo.LayoutRegion} region The resized region
44226          * @param {Number} newSize The new size (width for east/west, height for north/south)
44227          */
44228         "regionresized" : true,
44229         /**
44230          * @event regioncollapsed
44231          * Fires when a region is collapsed. 
44232          * @param {Roo.LayoutRegion} region The collapsed region
44233          */
44234         "regioncollapsed" : true,
44235         /**
44236          * @event regionexpanded
44237          * Fires when a region is expanded.  
44238          * @param {Roo.LayoutRegion} region The expanded region
44239          */
44240         "regionexpanded" : true
44241     });
44242     this.updating = false;
44243     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44244 };
44245
44246 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44247     /**
44248      * Returns true if this layout is currently being updated
44249      * @return {Boolean}
44250      */
44251     isUpdating : function(){
44252         return this.updating; 
44253     },
44254     
44255     /**
44256      * Suspend the LayoutManager from doing auto-layouts while
44257      * making multiple add or remove calls
44258      */
44259     beginUpdate : function(){
44260         this.updating = true;    
44261     },
44262     
44263     /**
44264      * Restore auto-layouts and optionally disable the manager from performing a layout
44265      * @param {Boolean} noLayout true to disable a layout update 
44266      */
44267     endUpdate : function(noLayout){
44268         this.updating = false;
44269         if(!noLayout){
44270             this.layout();
44271         }    
44272     },
44273     
44274     layout: function(){
44275         
44276     },
44277     
44278     onRegionResized : function(region, newSize){
44279         this.fireEvent("regionresized", region, newSize);
44280         this.layout();
44281     },
44282     
44283     onRegionCollapsed : function(region){
44284         this.fireEvent("regioncollapsed", region);
44285     },
44286     
44287     onRegionExpanded : function(region){
44288         this.fireEvent("regionexpanded", region);
44289     },
44290         
44291     /**
44292      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44293      * performs box-model adjustments.
44294      * @return {Object} The size as an object {width: (the width), height: (the height)}
44295      */
44296     getViewSize : function(){
44297         var size;
44298         if(this.el.dom != document.body){
44299             size = this.el.getSize();
44300         }else{
44301             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44302         }
44303         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44304         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44305         return size;
44306     },
44307     
44308     /**
44309      * Returns the Element this layout is bound to.
44310      * @return {Roo.Element}
44311      */
44312     getEl : function(){
44313         return this.el;
44314     },
44315     
44316     /**
44317      * Returns the specified region.
44318      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44319      * @return {Roo.LayoutRegion}
44320      */
44321     getRegion : function(target){
44322         return this.regions[target.toLowerCase()];
44323     },
44324     
44325     onWindowResize : function(){
44326         if(this.monitorWindowResize){
44327             this.layout();
44328         }
44329     }
44330 });/*
44331  * Based on:
44332  * Ext JS Library 1.1.1
44333  * Copyright(c) 2006-2007, Ext JS, LLC.
44334  *
44335  * Originally Released Under LGPL - original licence link has changed is not relivant.
44336  *
44337  * Fork - LGPL
44338  * <script type="text/javascript">
44339  */
44340 /**
44341  * @class Roo.BorderLayout
44342  * @extends Roo.LayoutManager
44343  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44344  * please see: <br><br>
44345  * <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>
44346  * <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>
44347  * Example:
44348  <pre><code>
44349  var layout = new Roo.BorderLayout(document.body, {
44350     north: {
44351         initialSize: 25,
44352         titlebar: false
44353     },
44354     west: {
44355         split:true,
44356         initialSize: 200,
44357         minSize: 175,
44358         maxSize: 400,
44359         titlebar: true,
44360         collapsible: true
44361     },
44362     east: {
44363         split:true,
44364         initialSize: 202,
44365         minSize: 175,
44366         maxSize: 400,
44367         titlebar: true,
44368         collapsible: true
44369     },
44370     south: {
44371         split:true,
44372         initialSize: 100,
44373         minSize: 100,
44374         maxSize: 200,
44375         titlebar: true,
44376         collapsible: true
44377     },
44378     center: {
44379         titlebar: true,
44380         autoScroll:true,
44381         resizeTabs: true,
44382         minTabWidth: 50,
44383         preferredTabWidth: 150
44384     }
44385 });
44386
44387 // shorthand
44388 var CP = Roo.ContentPanel;
44389
44390 layout.beginUpdate();
44391 layout.add("north", new CP("north", "North"));
44392 layout.add("south", new CP("south", {title: "South", closable: true}));
44393 layout.add("west", new CP("west", {title: "West"}));
44394 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44395 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44396 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44397 layout.getRegion("center").showPanel("center1");
44398 layout.endUpdate();
44399 </code></pre>
44400
44401 <b>The container the layout is rendered into can be either the body element or any other element.
44402 If it is not the body element, the container needs to either be an absolute positioned element,
44403 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44404 the container size if it is not the body element.</b>
44405
44406 * @constructor
44407 * Create a new BorderLayout
44408 * @param {String/HTMLElement/Element} container The container this layout is bound to
44409 * @param {Object} config Configuration options
44410  */
44411 Roo.BorderLayout = function(container, config){
44412     config = config || {};
44413     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44414     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44415     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44416         var target = this.factory.validRegions[i];
44417         if(config[target]){
44418             this.addRegion(target, config[target]);
44419         }
44420     }
44421 };
44422
44423 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44424     /**
44425      * Creates and adds a new region if it doesn't already exist.
44426      * @param {String} target The target region key (north, south, east, west or center).
44427      * @param {Object} config The regions config object
44428      * @return {BorderLayoutRegion} The new region
44429      */
44430     addRegion : function(target, config){
44431         if(!this.regions[target]){
44432             var r = this.factory.create(target, this, config);
44433             this.bindRegion(target, r);
44434         }
44435         return this.regions[target];
44436     },
44437
44438     // private (kinda)
44439     bindRegion : function(name, r){
44440         this.regions[name] = r;
44441         r.on("visibilitychange", this.layout, this);
44442         r.on("paneladded", this.layout, this);
44443         r.on("panelremoved", this.layout, this);
44444         r.on("invalidated", this.layout, this);
44445         r.on("resized", this.onRegionResized, this);
44446         r.on("collapsed", this.onRegionCollapsed, this);
44447         r.on("expanded", this.onRegionExpanded, this);
44448     },
44449
44450     /**
44451      * Performs a layout update.
44452      */
44453     layout : function(){
44454         if(this.updating) return;
44455         var size = this.getViewSize();
44456         var w = size.width;
44457         var h = size.height;
44458         var centerW = w;
44459         var centerH = h;
44460         var centerY = 0;
44461         var centerX = 0;
44462         //var x = 0, y = 0;
44463
44464         var rs = this.regions;
44465         var north = rs["north"];
44466         var south = rs["south"]; 
44467         var west = rs["west"];
44468         var east = rs["east"];
44469         var center = rs["center"];
44470         //if(this.hideOnLayout){ // not supported anymore
44471             //c.el.setStyle("display", "none");
44472         //}
44473         if(north && north.isVisible()){
44474             var b = north.getBox();
44475             var m = north.getMargins();
44476             b.width = w - (m.left+m.right);
44477             b.x = m.left;
44478             b.y = m.top;
44479             centerY = b.height + b.y + m.bottom;
44480             centerH -= centerY;
44481             north.updateBox(this.safeBox(b));
44482         }
44483         if(south && south.isVisible()){
44484             var b = south.getBox();
44485             var m = south.getMargins();
44486             b.width = w - (m.left+m.right);
44487             b.x = m.left;
44488             var totalHeight = (b.height + m.top + m.bottom);
44489             b.y = h - totalHeight + m.top;
44490             centerH -= totalHeight;
44491             south.updateBox(this.safeBox(b));
44492         }
44493         if(west && west.isVisible()){
44494             var b = west.getBox();
44495             var m = west.getMargins();
44496             b.height = centerH - (m.top+m.bottom);
44497             b.x = m.left;
44498             b.y = centerY + m.top;
44499             var totalWidth = (b.width + m.left + m.right);
44500             centerX += totalWidth;
44501             centerW -= totalWidth;
44502             west.updateBox(this.safeBox(b));
44503         }
44504         if(east && east.isVisible()){
44505             var b = east.getBox();
44506             var m = east.getMargins();
44507             b.height = centerH - (m.top+m.bottom);
44508             var totalWidth = (b.width + m.left + m.right);
44509             b.x = w - totalWidth + m.left;
44510             b.y = centerY + m.top;
44511             centerW -= totalWidth;
44512             east.updateBox(this.safeBox(b));
44513         }
44514         if(center){
44515             var m = center.getMargins();
44516             var centerBox = {
44517                 x: centerX + m.left,
44518                 y: centerY + m.top,
44519                 width: centerW - (m.left+m.right),
44520                 height: centerH - (m.top+m.bottom)
44521             };
44522             //if(this.hideOnLayout){
44523                 //center.el.setStyle("display", "block");
44524             //}
44525             center.updateBox(this.safeBox(centerBox));
44526         }
44527         this.el.repaint();
44528         this.fireEvent("layout", this);
44529     },
44530
44531     // private
44532     safeBox : function(box){
44533         box.width = Math.max(0, box.width);
44534         box.height = Math.max(0, box.height);
44535         return box;
44536     },
44537
44538     /**
44539      * Adds a ContentPanel (or subclass) to this layout.
44540      * @param {String} target The target region key (north, south, east, west or center).
44541      * @param {Roo.ContentPanel} panel The panel to add
44542      * @return {Roo.ContentPanel} The added panel
44543      */
44544     add : function(target, panel){
44545          
44546         target = target.toLowerCase();
44547         return this.regions[target].add(panel);
44548     },
44549
44550     /**
44551      * Remove a ContentPanel (or subclass) to this layout.
44552      * @param {String} target The target region key (north, south, east, west or center).
44553      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44554      * @return {Roo.ContentPanel} The removed panel
44555      */
44556     remove : function(target, panel){
44557         target = target.toLowerCase();
44558         return this.regions[target].remove(panel);
44559     },
44560
44561     /**
44562      * Searches all regions for a panel with the specified id
44563      * @param {String} panelId
44564      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44565      */
44566     findPanel : function(panelId){
44567         var rs = this.regions;
44568         for(var target in rs){
44569             if(typeof rs[target] != "function"){
44570                 var p = rs[target].getPanel(panelId);
44571                 if(p){
44572                     return p;
44573                 }
44574             }
44575         }
44576         return null;
44577     },
44578
44579     /**
44580      * Searches all regions for a panel with the specified id and activates (shows) it.
44581      * @param {String/ContentPanel} panelId The panels id or the panel itself
44582      * @return {Roo.ContentPanel} The shown panel or null
44583      */
44584     showPanel : function(panelId) {
44585       var rs = this.regions;
44586       for(var target in rs){
44587          var r = rs[target];
44588          if(typeof r != "function"){
44589             if(r.hasPanel(panelId)){
44590                return r.showPanel(panelId);
44591             }
44592          }
44593       }
44594       return null;
44595    },
44596
44597    /**
44598      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44599      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44600      */
44601     restoreState : function(provider){
44602         if(!provider){
44603             provider = Roo.state.Manager;
44604         }
44605         var sm = new Roo.LayoutStateManager();
44606         sm.init(this, provider);
44607     },
44608
44609     /**
44610      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44611      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44612      * a valid ContentPanel config object.  Example:
44613      * <pre><code>
44614 // Create the main layout
44615 var layout = new Roo.BorderLayout('main-ct', {
44616     west: {
44617         split:true,
44618         minSize: 175,
44619         titlebar: true
44620     },
44621     center: {
44622         title:'Components'
44623     }
44624 }, 'main-ct');
44625
44626 // Create and add multiple ContentPanels at once via configs
44627 layout.batchAdd({
44628    west: {
44629        id: 'source-files',
44630        autoCreate:true,
44631        title:'Ext Source Files',
44632        autoScroll:true,
44633        fitToFrame:true
44634    },
44635    center : {
44636        el: cview,
44637        autoScroll:true,
44638        fitToFrame:true,
44639        toolbar: tb,
44640        resizeEl:'cbody'
44641    }
44642 });
44643 </code></pre>
44644      * @param {Object} regions An object containing ContentPanel configs by region name
44645      */
44646     batchAdd : function(regions){
44647         this.beginUpdate();
44648         for(var rname in regions){
44649             var lr = this.regions[rname];
44650             if(lr){
44651                 this.addTypedPanels(lr, regions[rname]);
44652             }
44653         }
44654         this.endUpdate();
44655     },
44656
44657     // private
44658     addTypedPanels : function(lr, ps){
44659         if(typeof ps == 'string'){
44660             lr.add(new Roo.ContentPanel(ps));
44661         }
44662         else if(ps instanceof Array){
44663             for(var i =0, len = ps.length; i < len; i++){
44664                 this.addTypedPanels(lr, ps[i]);
44665             }
44666         }
44667         else if(!ps.events){ // raw config?
44668             var el = ps.el;
44669             delete ps.el; // prevent conflict
44670             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44671         }
44672         else {  // panel object assumed!
44673             lr.add(ps);
44674         }
44675     },
44676     /**
44677      * Adds a xtype elements to the layout.
44678      * <pre><code>
44679
44680 layout.addxtype({
44681        xtype : 'ContentPanel',
44682        region: 'west',
44683        items: [ .... ]
44684    }
44685 );
44686
44687 layout.addxtype({
44688         xtype : 'NestedLayoutPanel',
44689         region: 'west',
44690         layout: {
44691            center: { },
44692            west: { }   
44693         },
44694         items : [ ... list of content panels or nested layout panels.. ]
44695    }
44696 );
44697 </code></pre>
44698      * @param {Object} cfg Xtype definition of item to add.
44699      */
44700     addxtype : function(cfg)
44701     {
44702         // basically accepts a pannel...
44703         // can accept a layout region..!?!?
44704         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44705         
44706         if (!cfg.xtype.match(/Panel$/)) {
44707             return false;
44708         }
44709         var ret = false;
44710         
44711         if (typeof(cfg.region) == 'undefined') {
44712             Roo.log("Failed to add Panel, region was not set");
44713             Roo.log(cfg);
44714             return false;
44715         }
44716         var region = cfg.region;
44717         delete cfg.region;
44718         
44719           
44720         var xitems = [];
44721         if (cfg.items) {
44722             xitems = cfg.items;
44723             delete cfg.items;
44724         }
44725         
44726         
44727         switch(cfg.xtype) 
44728         {
44729             case 'ContentPanel':  // ContentPanel (el, cfg)
44730             case 'ScrollPanel':  // ContentPanel (el, cfg)
44731                 if(cfg.autoCreate) {
44732                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44733                 } else {
44734                     var el = this.el.createChild();
44735                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44736                 }
44737                 
44738                 this.add(region, ret);
44739                 break;
44740             
44741             
44742             case 'TreePanel': // our new panel!
44743                 cfg.el = this.el.createChild();
44744                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44745                 this.add(region, ret);
44746                 break;
44747             
44748             case 'NestedLayoutPanel': 
44749                 // create a new Layout (which is  a Border Layout...
44750                 var el = this.el.createChild();
44751                 var clayout = cfg.layout;
44752                 delete cfg.layout;
44753                 clayout.items   = clayout.items  || [];
44754                 // replace this exitems with the clayout ones..
44755                 xitems = clayout.items;
44756                  
44757                 
44758                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44759                     cfg.background = false;
44760                 }
44761                 var layout = new Roo.BorderLayout(el, clayout);
44762                 
44763                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44764                 //console.log('adding nested layout panel '  + cfg.toSource());
44765                 this.add(region, ret);
44766                 
44767                 break;
44768                 
44769             case 'GridPanel': 
44770             
44771                 // needs grid and region
44772                 
44773                 //var el = this.getRegion(region).el.createChild();
44774                 var el = this.el.createChild();
44775                 // create the grid first...
44776                 
44777                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44778                 delete cfg.grid;
44779                 if (region == 'center' && this.active ) {
44780                     cfg.background = false;
44781                 }
44782                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44783                 
44784                 this.add(region, ret);
44785                 if (cfg.background) {
44786                     ret.on('activate', function(gp) {
44787                         if (!gp.grid.rendered) {
44788                             gp.grid.render();
44789                         }
44790                     });
44791                 } else {
44792                     grid.render();
44793                 }
44794                 break;
44795            
44796                
44797                 
44798                 
44799             default: 
44800                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44801                 return null;
44802              // GridPanel (grid, cfg)
44803             
44804         }
44805         this.beginUpdate();
44806         // add children..
44807         Roo.each(xitems, function(i)  {
44808             ret.addxtype(i);
44809         });
44810         this.endUpdate();
44811         return ret;
44812         
44813     }
44814 });
44815
44816 /**
44817  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44818  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44819  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44820  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44821  * <pre><code>
44822 // shorthand
44823 var CP = Roo.ContentPanel;
44824
44825 var layout = Roo.BorderLayout.create({
44826     north: {
44827         initialSize: 25,
44828         titlebar: false,
44829         panels: [new CP("north", "North")]
44830     },
44831     west: {
44832         split:true,
44833         initialSize: 200,
44834         minSize: 175,
44835         maxSize: 400,
44836         titlebar: true,
44837         collapsible: true,
44838         panels: [new CP("west", {title: "West"})]
44839     },
44840     east: {
44841         split:true,
44842         initialSize: 202,
44843         minSize: 175,
44844         maxSize: 400,
44845         titlebar: true,
44846         collapsible: true,
44847         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44848     },
44849     south: {
44850         split:true,
44851         initialSize: 100,
44852         minSize: 100,
44853         maxSize: 200,
44854         titlebar: true,
44855         collapsible: true,
44856         panels: [new CP("south", {title: "South", closable: true})]
44857     },
44858     center: {
44859         titlebar: true,
44860         autoScroll:true,
44861         resizeTabs: true,
44862         minTabWidth: 50,
44863         preferredTabWidth: 150,
44864         panels: [
44865             new CP("center1", {title: "Close Me", closable: true}),
44866             new CP("center2", {title: "Center Panel", closable: false})
44867         ]
44868     }
44869 }, document.body);
44870
44871 layout.getRegion("center").showPanel("center1");
44872 </code></pre>
44873  * @param config
44874  * @param targetEl
44875  */
44876 Roo.BorderLayout.create = function(config, targetEl){
44877     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44878     layout.beginUpdate();
44879     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44880     for(var j = 0, jlen = regions.length; j < jlen; j++){
44881         var lr = regions[j];
44882         if(layout.regions[lr] && config[lr].panels){
44883             var r = layout.regions[lr];
44884             var ps = config[lr].panels;
44885             layout.addTypedPanels(r, ps);
44886         }
44887     }
44888     layout.endUpdate();
44889     return layout;
44890 };
44891
44892 // private
44893 Roo.BorderLayout.RegionFactory = {
44894     // private
44895     validRegions : ["north","south","east","west","center"],
44896
44897     // private
44898     create : function(target, mgr, config){
44899         target = target.toLowerCase();
44900         if(config.lightweight || config.basic){
44901             return new Roo.BasicLayoutRegion(mgr, config, target);
44902         }
44903         switch(target){
44904             case "north":
44905                 return new Roo.NorthLayoutRegion(mgr, config);
44906             case "south":
44907                 return new Roo.SouthLayoutRegion(mgr, config);
44908             case "east":
44909                 return new Roo.EastLayoutRegion(mgr, config);
44910             case "west":
44911                 return new Roo.WestLayoutRegion(mgr, config);
44912             case "center":
44913                 return new Roo.CenterLayoutRegion(mgr, config);
44914         }
44915         throw 'Layout region "'+target+'" not supported.';
44916     }
44917 };/*
44918  * Based on:
44919  * Ext JS Library 1.1.1
44920  * Copyright(c) 2006-2007, Ext JS, LLC.
44921  *
44922  * Originally Released Under LGPL - original licence link has changed is not relivant.
44923  *
44924  * Fork - LGPL
44925  * <script type="text/javascript">
44926  */
44927  
44928 /**
44929  * @class Roo.BasicLayoutRegion
44930  * @extends Roo.util.Observable
44931  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44932  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44933  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44934  */
44935 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44936     this.mgr = mgr;
44937     this.position  = pos;
44938     this.events = {
44939         /**
44940          * @scope Roo.BasicLayoutRegion
44941          */
44942         
44943         /**
44944          * @event beforeremove
44945          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44946          * @param {Roo.LayoutRegion} this
44947          * @param {Roo.ContentPanel} panel The panel
44948          * @param {Object} e The cancel event object
44949          */
44950         "beforeremove" : true,
44951         /**
44952          * @event invalidated
44953          * Fires when the layout for this region is changed.
44954          * @param {Roo.LayoutRegion} this
44955          */
44956         "invalidated" : true,
44957         /**
44958          * @event visibilitychange
44959          * Fires when this region is shown or hidden 
44960          * @param {Roo.LayoutRegion} this
44961          * @param {Boolean} visibility true or false
44962          */
44963         "visibilitychange" : true,
44964         /**
44965          * @event paneladded
44966          * Fires when a panel is added. 
44967          * @param {Roo.LayoutRegion} this
44968          * @param {Roo.ContentPanel} panel The panel
44969          */
44970         "paneladded" : true,
44971         /**
44972          * @event panelremoved
44973          * Fires when a panel is removed. 
44974          * @param {Roo.LayoutRegion} this
44975          * @param {Roo.ContentPanel} panel The panel
44976          */
44977         "panelremoved" : true,
44978         /**
44979          * @event collapsed
44980          * Fires when this region is collapsed.
44981          * @param {Roo.LayoutRegion} this
44982          */
44983         "collapsed" : true,
44984         /**
44985          * @event expanded
44986          * Fires when this region is expanded.
44987          * @param {Roo.LayoutRegion} this
44988          */
44989         "expanded" : true,
44990         /**
44991          * @event slideshow
44992          * Fires when this region is slid into view.
44993          * @param {Roo.LayoutRegion} this
44994          */
44995         "slideshow" : true,
44996         /**
44997          * @event slidehide
44998          * Fires when this region slides out of view. 
44999          * @param {Roo.LayoutRegion} this
45000          */
45001         "slidehide" : true,
45002         /**
45003          * @event panelactivated
45004          * Fires when a panel is activated. 
45005          * @param {Roo.LayoutRegion} this
45006          * @param {Roo.ContentPanel} panel The activated panel
45007          */
45008         "panelactivated" : true,
45009         /**
45010          * @event resized
45011          * Fires when the user resizes this region. 
45012          * @param {Roo.LayoutRegion} this
45013          * @param {Number} newSize The new size (width for east/west, height for north/south)
45014          */
45015         "resized" : true
45016     };
45017     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45018     this.panels = new Roo.util.MixedCollection();
45019     this.panels.getKey = this.getPanelId.createDelegate(this);
45020     this.box = null;
45021     this.activePanel = null;
45022     // ensure listeners are added...
45023     
45024     if (config.listeners || config.events) {
45025         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45026             listeners : config.listeners || {},
45027             events : config.events || {}
45028         });
45029     }
45030     
45031     if(skipConfig !== true){
45032         this.applyConfig(config);
45033     }
45034 };
45035
45036 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45037     getPanelId : function(p){
45038         return p.getId();
45039     },
45040     
45041     applyConfig : function(config){
45042         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45043         this.config = config;
45044         
45045     },
45046     
45047     /**
45048      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45049      * the width, for horizontal (north, south) the height.
45050      * @param {Number} newSize The new width or height
45051      */
45052     resizeTo : function(newSize){
45053         var el = this.el ? this.el :
45054                  (this.activePanel ? this.activePanel.getEl() : null);
45055         if(el){
45056             switch(this.position){
45057                 case "east":
45058                 case "west":
45059                     el.setWidth(newSize);
45060                     this.fireEvent("resized", this, newSize);
45061                 break;
45062                 case "north":
45063                 case "south":
45064                     el.setHeight(newSize);
45065                     this.fireEvent("resized", this, newSize);
45066                 break;                
45067             }
45068         }
45069     },
45070     
45071     getBox : function(){
45072         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45073     },
45074     
45075     getMargins : function(){
45076         return this.margins;
45077     },
45078     
45079     updateBox : function(box){
45080         this.box = box;
45081         var el = this.activePanel.getEl();
45082         el.dom.style.left = box.x + "px";
45083         el.dom.style.top = box.y + "px";
45084         this.activePanel.setSize(box.width, box.height);
45085     },
45086     
45087     /**
45088      * Returns the container element for this region.
45089      * @return {Roo.Element}
45090      */
45091     getEl : function(){
45092         return this.activePanel;
45093     },
45094     
45095     /**
45096      * Returns true if this region is currently visible.
45097      * @return {Boolean}
45098      */
45099     isVisible : function(){
45100         return this.activePanel ? true : false;
45101     },
45102     
45103     setActivePanel : function(panel){
45104         panel = this.getPanel(panel);
45105         if(this.activePanel && this.activePanel != panel){
45106             this.activePanel.setActiveState(false);
45107             this.activePanel.getEl().setLeftTop(-10000,-10000);
45108         }
45109         this.activePanel = panel;
45110         panel.setActiveState(true);
45111         if(this.box){
45112             panel.setSize(this.box.width, this.box.height);
45113         }
45114         this.fireEvent("panelactivated", this, panel);
45115         this.fireEvent("invalidated");
45116     },
45117     
45118     /**
45119      * Show the specified panel.
45120      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45121      * @return {Roo.ContentPanel} The shown panel or null
45122      */
45123     showPanel : function(panel){
45124         if(panel = this.getPanel(panel)){
45125             this.setActivePanel(panel);
45126         }
45127         return panel;
45128     },
45129     
45130     /**
45131      * Get the active panel for this region.
45132      * @return {Roo.ContentPanel} The active panel or null
45133      */
45134     getActivePanel : function(){
45135         return this.activePanel;
45136     },
45137     
45138     /**
45139      * Add the passed ContentPanel(s)
45140      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45141      * @return {Roo.ContentPanel} The panel added (if only one was added)
45142      */
45143     add : function(panel){
45144         if(arguments.length > 1){
45145             for(var i = 0, len = arguments.length; i < len; i++) {
45146                 this.add(arguments[i]);
45147             }
45148             return null;
45149         }
45150         if(this.hasPanel(panel)){
45151             this.showPanel(panel);
45152             return panel;
45153         }
45154         var el = panel.getEl();
45155         if(el.dom.parentNode != this.mgr.el.dom){
45156             this.mgr.el.dom.appendChild(el.dom);
45157         }
45158         if(panel.setRegion){
45159             panel.setRegion(this);
45160         }
45161         this.panels.add(panel);
45162         el.setStyle("position", "absolute");
45163         if(!panel.background){
45164             this.setActivePanel(panel);
45165             if(this.config.initialSize && this.panels.getCount()==1){
45166                 this.resizeTo(this.config.initialSize);
45167             }
45168         }
45169         this.fireEvent("paneladded", this, panel);
45170         return panel;
45171     },
45172     
45173     /**
45174      * Returns true if the panel is in this region.
45175      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45176      * @return {Boolean}
45177      */
45178     hasPanel : function(panel){
45179         if(typeof panel == "object"){ // must be panel obj
45180             panel = panel.getId();
45181         }
45182         return this.getPanel(panel) ? true : false;
45183     },
45184     
45185     /**
45186      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45187      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45188      * @param {Boolean} preservePanel Overrides the config preservePanel option
45189      * @return {Roo.ContentPanel} The panel that was removed
45190      */
45191     remove : function(panel, preservePanel){
45192         panel = this.getPanel(panel);
45193         if(!panel){
45194             return null;
45195         }
45196         var e = {};
45197         this.fireEvent("beforeremove", this, panel, e);
45198         if(e.cancel === true){
45199             return null;
45200         }
45201         var panelId = panel.getId();
45202         this.panels.removeKey(panelId);
45203         return panel;
45204     },
45205     
45206     /**
45207      * Returns the panel specified or null if it's not in this region.
45208      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45209      * @return {Roo.ContentPanel}
45210      */
45211     getPanel : function(id){
45212         if(typeof id == "object"){ // must be panel obj
45213             return id;
45214         }
45215         return this.panels.get(id);
45216     },
45217     
45218     /**
45219      * Returns this regions position (north/south/east/west/center).
45220      * @return {String} 
45221      */
45222     getPosition: function(){
45223         return this.position;    
45224     }
45225 });/*
45226  * Based on:
45227  * Ext JS Library 1.1.1
45228  * Copyright(c) 2006-2007, Ext JS, LLC.
45229  *
45230  * Originally Released Under LGPL - original licence link has changed is not relivant.
45231  *
45232  * Fork - LGPL
45233  * <script type="text/javascript">
45234  */
45235  
45236 /**
45237  * @class Roo.LayoutRegion
45238  * @extends Roo.BasicLayoutRegion
45239  * This class represents a region in a layout manager.
45240  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45241  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45242  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45243  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45244  * @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})
45245  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45246  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45247  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45248  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45249  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45250  * @cfg {String}    title           The title for the region (overrides panel titles)
45251  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45252  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45253  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45254  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45255  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45256  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45257  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45258  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45259  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45260  * @cfg {Boolean}   showPin         True to show a pin button
45261  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45262  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45263  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45264  * @cfg {Number}    width           For East/West panels
45265  * @cfg {Number}    height          For North/South panels
45266  * @cfg {Boolean}   split           To show the splitter
45267  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45268  */
45269 Roo.LayoutRegion = function(mgr, config, pos){
45270     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45271     var dh = Roo.DomHelper;
45272     /** This region's container element 
45273     * @type Roo.Element */
45274     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45275     /** This region's title element 
45276     * @type Roo.Element */
45277
45278     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45279         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45280         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45281     ]}, true);
45282     this.titleEl.enableDisplayMode();
45283     /** This region's title text element 
45284     * @type HTMLElement */
45285     this.titleTextEl = this.titleEl.dom.firstChild;
45286     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45287     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45288     this.closeBtn.enableDisplayMode();
45289     this.closeBtn.on("click", this.closeClicked, this);
45290     this.closeBtn.hide();
45291
45292     this.createBody(config);
45293     this.visible = true;
45294     this.collapsed = false;
45295
45296     if(config.hideWhenEmpty){
45297         this.hide();
45298         this.on("paneladded", this.validateVisibility, this);
45299         this.on("panelremoved", this.validateVisibility, this);
45300     }
45301     this.applyConfig(config);
45302 };
45303
45304 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45305
45306     createBody : function(){
45307         /** This region's body element 
45308         * @type Roo.Element */
45309         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45310     },
45311
45312     applyConfig : function(c){
45313         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45314             var dh = Roo.DomHelper;
45315             if(c.titlebar !== false){
45316                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45317                 this.collapseBtn.on("click", this.collapse, this);
45318                 this.collapseBtn.enableDisplayMode();
45319
45320                 if(c.showPin === true || this.showPin){
45321                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45322                     this.stickBtn.enableDisplayMode();
45323                     this.stickBtn.on("click", this.expand, this);
45324                     this.stickBtn.hide();
45325                 }
45326             }
45327             /** This region's collapsed element
45328             * @type Roo.Element */
45329             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45330                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45331             ]}, true);
45332             if(c.floatable !== false){
45333                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45334                this.collapsedEl.on("click", this.collapseClick, this);
45335             }
45336
45337             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45338                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45339                    id: "message", unselectable: "on", style:{"float":"left"}});
45340                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45341              }
45342             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45343             this.expandBtn.on("click", this.expand, this);
45344         }
45345         if(this.collapseBtn){
45346             this.collapseBtn.setVisible(c.collapsible == true);
45347         }
45348         this.cmargins = c.cmargins || this.cmargins ||
45349                          (this.position == "west" || this.position == "east" ?
45350                              {top: 0, left: 2, right:2, bottom: 0} :
45351                              {top: 2, left: 0, right:0, bottom: 2});
45352         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45353         this.bottomTabs = c.tabPosition != "top";
45354         this.autoScroll = c.autoScroll || false;
45355         if(this.autoScroll){
45356             this.bodyEl.setStyle("overflow", "auto");
45357         }else{
45358             this.bodyEl.setStyle("overflow", "hidden");
45359         }
45360         //if(c.titlebar !== false){
45361             if((!c.titlebar && !c.title) || c.titlebar === false){
45362                 this.titleEl.hide();
45363             }else{
45364                 this.titleEl.show();
45365                 if(c.title){
45366                     this.titleTextEl.innerHTML = c.title;
45367                 }
45368             }
45369         //}
45370         this.duration = c.duration || .30;
45371         this.slideDuration = c.slideDuration || .45;
45372         this.config = c;
45373         if(c.collapsed){
45374             this.collapse(true);
45375         }
45376         if(c.hidden){
45377             this.hide();
45378         }
45379     },
45380     /**
45381      * Returns true if this region is currently visible.
45382      * @return {Boolean}
45383      */
45384     isVisible : function(){
45385         return this.visible;
45386     },
45387
45388     /**
45389      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45390      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45391      */
45392     setCollapsedTitle : function(title){
45393         title = title || "&#160;";
45394         if(this.collapsedTitleTextEl){
45395             this.collapsedTitleTextEl.innerHTML = title;
45396         }
45397     },
45398
45399     getBox : function(){
45400         var b;
45401         if(!this.collapsed){
45402             b = this.el.getBox(false, true);
45403         }else{
45404             b = this.collapsedEl.getBox(false, true);
45405         }
45406         return b;
45407     },
45408
45409     getMargins : function(){
45410         return this.collapsed ? this.cmargins : this.margins;
45411     },
45412
45413     highlight : function(){
45414         this.el.addClass("x-layout-panel-dragover");
45415     },
45416
45417     unhighlight : function(){
45418         this.el.removeClass("x-layout-panel-dragover");
45419     },
45420
45421     updateBox : function(box){
45422         this.box = box;
45423         if(!this.collapsed){
45424             this.el.dom.style.left = box.x + "px";
45425             this.el.dom.style.top = box.y + "px";
45426             this.updateBody(box.width, box.height);
45427         }else{
45428             this.collapsedEl.dom.style.left = box.x + "px";
45429             this.collapsedEl.dom.style.top = box.y + "px";
45430             this.collapsedEl.setSize(box.width, box.height);
45431         }
45432         if(this.tabs){
45433             this.tabs.autoSizeTabs();
45434         }
45435     },
45436
45437     updateBody : function(w, h){
45438         if(w !== null){
45439             this.el.setWidth(w);
45440             w -= this.el.getBorderWidth("rl");
45441             if(this.config.adjustments){
45442                 w += this.config.adjustments[0];
45443             }
45444         }
45445         if(h !== null){
45446             this.el.setHeight(h);
45447             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45448             h -= this.el.getBorderWidth("tb");
45449             if(this.config.adjustments){
45450                 h += this.config.adjustments[1];
45451             }
45452             this.bodyEl.setHeight(h);
45453             if(this.tabs){
45454                 h = this.tabs.syncHeight(h);
45455             }
45456         }
45457         if(this.panelSize){
45458             w = w !== null ? w : this.panelSize.width;
45459             h = h !== null ? h : this.panelSize.height;
45460         }
45461         if(this.activePanel){
45462             var el = this.activePanel.getEl();
45463             w = w !== null ? w : el.getWidth();
45464             h = h !== null ? h : el.getHeight();
45465             this.panelSize = {width: w, height: h};
45466             this.activePanel.setSize(w, h);
45467         }
45468         if(Roo.isIE && this.tabs){
45469             this.tabs.el.repaint();
45470         }
45471     },
45472
45473     /**
45474      * Returns the container element for this region.
45475      * @return {Roo.Element}
45476      */
45477     getEl : function(){
45478         return this.el;
45479     },
45480
45481     /**
45482      * Hides this region.
45483      */
45484     hide : function(){
45485         if(!this.collapsed){
45486             this.el.dom.style.left = "-2000px";
45487             this.el.hide();
45488         }else{
45489             this.collapsedEl.dom.style.left = "-2000px";
45490             this.collapsedEl.hide();
45491         }
45492         this.visible = false;
45493         this.fireEvent("visibilitychange", this, false);
45494     },
45495
45496     /**
45497      * Shows this region if it was previously hidden.
45498      */
45499     show : function(){
45500         if(!this.collapsed){
45501             this.el.show();
45502         }else{
45503             this.collapsedEl.show();
45504         }
45505         this.visible = true;
45506         this.fireEvent("visibilitychange", this, true);
45507     },
45508
45509     closeClicked : function(){
45510         if(this.activePanel){
45511             this.remove(this.activePanel);
45512         }
45513     },
45514
45515     collapseClick : function(e){
45516         if(this.isSlid){
45517            e.stopPropagation();
45518            this.slideIn();
45519         }else{
45520            e.stopPropagation();
45521            this.slideOut();
45522         }
45523     },
45524
45525     /**
45526      * Collapses this region.
45527      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45528      */
45529     collapse : function(skipAnim){
45530         if(this.collapsed) return;
45531         this.collapsed = true;
45532         if(this.split){
45533             this.split.el.hide();
45534         }
45535         if(this.config.animate && skipAnim !== true){
45536             this.fireEvent("invalidated", this);
45537             this.animateCollapse();
45538         }else{
45539             this.el.setLocation(-20000,-20000);
45540             this.el.hide();
45541             this.collapsedEl.show();
45542             this.fireEvent("collapsed", this);
45543             this.fireEvent("invalidated", this);
45544         }
45545     },
45546
45547     animateCollapse : function(){
45548         // overridden
45549     },
45550
45551     /**
45552      * Expands this region if it was previously collapsed.
45553      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45554      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45555      */
45556     expand : function(e, skipAnim){
45557         if(e) e.stopPropagation();
45558         if(!this.collapsed || this.el.hasActiveFx()) return;
45559         if(this.isSlid){
45560             this.afterSlideIn();
45561             skipAnim = true;
45562         }
45563         this.collapsed = false;
45564         if(this.config.animate && skipAnim !== true){
45565             this.animateExpand();
45566         }else{
45567             this.el.show();
45568             if(this.split){
45569                 this.split.el.show();
45570             }
45571             this.collapsedEl.setLocation(-2000,-2000);
45572             this.collapsedEl.hide();
45573             this.fireEvent("invalidated", this);
45574             this.fireEvent("expanded", this);
45575         }
45576     },
45577
45578     animateExpand : function(){
45579         // overridden
45580     },
45581
45582     initTabs : function()
45583     {
45584         this.bodyEl.setStyle("overflow", "hidden");
45585         var ts = new Roo.TabPanel(
45586                 this.bodyEl.dom,
45587                 {
45588                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45589                     disableTooltips: this.config.disableTabTips,
45590                     toolbar : this.config.toolbar
45591                 }
45592         );
45593         if(this.config.hideTabs){
45594             ts.stripWrap.setDisplayed(false);
45595         }
45596         this.tabs = ts;
45597         ts.resizeTabs = this.config.resizeTabs === true;
45598         ts.minTabWidth = this.config.minTabWidth || 40;
45599         ts.maxTabWidth = this.config.maxTabWidth || 250;
45600         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45601         ts.monitorResize = false;
45602         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45603         ts.bodyEl.addClass('x-layout-tabs-body');
45604         this.panels.each(this.initPanelAsTab, this);
45605     },
45606
45607     initPanelAsTab : function(panel){
45608         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45609                     this.config.closeOnTab && panel.isClosable());
45610         if(panel.tabTip !== undefined){
45611             ti.setTooltip(panel.tabTip);
45612         }
45613         ti.on("activate", function(){
45614               this.setActivePanel(panel);
45615         }, this);
45616         if(this.config.closeOnTab){
45617             ti.on("beforeclose", function(t, e){
45618                 e.cancel = true;
45619                 this.remove(panel);
45620             }, this);
45621         }
45622         return ti;
45623     },
45624
45625     updatePanelTitle : function(panel, title){
45626         if(this.activePanel == panel){
45627             this.updateTitle(title);
45628         }
45629         if(this.tabs){
45630             var ti = this.tabs.getTab(panel.getEl().id);
45631             ti.setText(title);
45632             if(panel.tabTip !== undefined){
45633                 ti.setTooltip(panel.tabTip);
45634             }
45635         }
45636     },
45637
45638     updateTitle : function(title){
45639         if(this.titleTextEl && !this.config.title){
45640             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45641         }
45642     },
45643
45644     setActivePanel : function(panel){
45645         panel = this.getPanel(panel);
45646         if(this.activePanel && this.activePanel != panel){
45647             this.activePanel.setActiveState(false);
45648         }
45649         this.activePanel = panel;
45650         panel.setActiveState(true);
45651         if(this.panelSize){
45652             panel.setSize(this.panelSize.width, this.panelSize.height);
45653         }
45654         if(this.closeBtn){
45655             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45656         }
45657         this.updateTitle(panel.getTitle());
45658         if(this.tabs){
45659             this.fireEvent("invalidated", this);
45660         }
45661         this.fireEvent("panelactivated", this, panel);
45662     },
45663
45664     /**
45665      * Shows the specified panel.
45666      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45667      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45668      */
45669     showPanel : function(panel){
45670         if(panel = this.getPanel(panel)){
45671             if(this.tabs){
45672                 var tab = this.tabs.getTab(panel.getEl().id);
45673                 if(tab.isHidden()){
45674                     this.tabs.unhideTab(tab.id);
45675                 }
45676                 tab.activate();
45677             }else{
45678                 this.setActivePanel(panel);
45679             }
45680         }
45681         return panel;
45682     },
45683
45684     /**
45685      * Get the active panel for this region.
45686      * @return {Roo.ContentPanel} The active panel or null
45687      */
45688     getActivePanel : function(){
45689         return this.activePanel;
45690     },
45691
45692     validateVisibility : function(){
45693         if(this.panels.getCount() < 1){
45694             this.updateTitle("&#160;");
45695             this.closeBtn.hide();
45696             this.hide();
45697         }else{
45698             if(!this.isVisible()){
45699                 this.show();
45700             }
45701         }
45702     },
45703
45704     /**
45705      * Adds the passed ContentPanel(s) to this region.
45706      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45707      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45708      */
45709     add : function(panel){
45710         if(arguments.length > 1){
45711             for(var i = 0, len = arguments.length; i < len; i++) {
45712                 this.add(arguments[i]);
45713             }
45714             return null;
45715         }
45716         if(this.hasPanel(panel)){
45717             this.showPanel(panel);
45718             return panel;
45719         }
45720         panel.setRegion(this);
45721         this.panels.add(panel);
45722         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45723             this.bodyEl.dom.appendChild(panel.getEl().dom);
45724             if(panel.background !== true){
45725                 this.setActivePanel(panel);
45726             }
45727             this.fireEvent("paneladded", this, panel);
45728             return panel;
45729         }
45730         if(!this.tabs){
45731             this.initTabs();
45732         }else{
45733             this.initPanelAsTab(panel);
45734         }
45735         if(panel.background !== true){
45736             this.tabs.activate(panel.getEl().id);
45737         }
45738         this.fireEvent("paneladded", this, panel);
45739         return panel;
45740     },
45741
45742     /**
45743      * Hides the tab for the specified panel.
45744      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45745      */
45746     hidePanel : function(panel){
45747         if(this.tabs && (panel = this.getPanel(panel))){
45748             this.tabs.hideTab(panel.getEl().id);
45749         }
45750     },
45751
45752     /**
45753      * Unhides the tab for a previously hidden panel.
45754      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45755      */
45756     unhidePanel : function(panel){
45757         if(this.tabs && (panel = this.getPanel(panel))){
45758             this.tabs.unhideTab(panel.getEl().id);
45759         }
45760     },
45761
45762     clearPanels : function(){
45763         while(this.panels.getCount() > 0){
45764              this.remove(this.panels.first());
45765         }
45766     },
45767
45768     /**
45769      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45770      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45771      * @param {Boolean} preservePanel Overrides the config preservePanel option
45772      * @return {Roo.ContentPanel} The panel that was removed
45773      */
45774     remove : function(panel, preservePanel){
45775         panel = this.getPanel(panel);
45776         if(!panel){
45777             return null;
45778         }
45779         var e = {};
45780         this.fireEvent("beforeremove", this, panel, e);
45781         if(e.cancel === true){
45782             return null;
45783         }
45784         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45785         var panelId = panel.getId();
45786         this.panels.removeKey(panelId);
45787         if(preservePanel){
45788             document.body.appendChild(panel.getEl().dom);
45789         }
45790         if(this.tabs){
45791             this.tabs.removeTab(panel.getEl().id);
45792         }else if (!preservePanel){
45793             this.bodyEl.dom.removeChild(panel.getEl().dom);
45794         }
45795         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45796             var p = this.panels.first();
45797             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45798             tempEl.appendChild(p.getEl().dom);
45799             this.bodyEl.update("");
45800             this.bodyEl.dom.appendChild(p.getEl().dom);
45801             tempEl = null;
45802             this.updateTitle(p.getTitle());
45803             this.tabs = null;
45804             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45805             this.setActivePanel(p);
45806         }
45807         panel.setRegion(null);
45808         if(this.activePanel == panel){
45809             this.activePanel = null;
45810         }
45811         if(this.config.autoDestroy !== false && preservePanel !== true){
45812             try{panel.destroy();}catch(e){}
45813         }
45814         this.fireEvent("panelremoved", this, panel);
45815         return panel;
45816     },
45817
45818     /**
45819      * Returns the TabPanel component used by this region
45820      * @return {Roo.TabPanel}
45821      */
45822     getTabs : function(){
45823         return this.tabs;
45824     },
45825
45826     createTool : function(parentEl, className){
45827         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45828             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45829         btn.addClassOnOver("x-layout-tools-button-over");
45830         return btn;
45831     }
45832 });/*
45833  * Based on:
45834  * Ext JS Library 1.1.1
45835  * Copyright(c) 2006-2007, Ext JS, LLC.
45836  *
45837  * Originally Released Under LGPL - original licence link has changed is not relivant.
45838  *
45839  * Fork - LGPL
45840  * <script type="text/javascript">
45841  */
45842  
45843
45844
45845 /**
45846  * @class Roo.SplitLayoutRegion
45847  * @extends Roo.LayoutRegion
45848  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45849  */
45850 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45851     this.cursor = cursor;
45852     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45853 };
45854
45855 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45856     splitTip : "Drag to resize.",
45857     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45858     useSplitTips : false,
45859
45860     applyConfig : function(config){
45861         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45862         if(config.split){
45863             if(!this.split){
45864                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45865                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45866                 /** The SplitBar for this region 
45867                 * @type Roo.SplitBar */
45868                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45869                 this.split.on("moved", this.onSplitMove, this);
45870                 this.split.useShim = config.useShim === true;
45871                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45872                 if(this.useSplitTips){
45873                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45874                 }
45875                 if(config.collapsible){
45876                     this.split.el.on("dblclick", this.collapse,  this);
45877                 }
45878             }
45879             if(typeof config.minSize != "undefined"){
45880                 this.split.minSize = config.minSize;
45881             }
45882             if(typeof config.maxSize != "undefined"){
45883                 this.split.maxSize = config.maxSize;
45884             }
45885             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45886                 this.hideSplitter();
45887             }
45888         }
45889     },
45890
45891     getHMaxSize : function(){
45892          var cmax = this.config.maxSize || 10000;
45893          var center = this.mgr.getRegion("center");
45894          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45895     },
45896
45897     getVMaxSize : function(){
45898          var cmax = this.config.maxSize || 10000;
45899          var center = this.mgr.getRegion("center");
45900          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45901     },
45902
45903     onSplitMove : function(split, newSize){
45904         this.fireEvent("resized", this, newSize);
45905     },
45906     
45907     /** 
45908      * Returns the {@link Roo.SplitBar} for this region.
45909      * @return {Roo.SplitBar}
45910      */
45911     getSplitBar : function(){
45912         return this.split;
45913     },
45914     
45915     hide : function(){
45916         this.hideSplitter();
45917         Roo.SplitLayoutRegion.superclass.hide.call(this);
45918     },
45919
45920     hideSplitter : function(){
45921         if(this.split){
45922             this.split.el.setLocation(-2000,-2000);
45923             this.split.el.hide();
45924         }
45925     },
45926
45927     show : function(){
45928         if(this.split){
45929             this.split.el.show();
45930         }
45931         Roo.SplitLayoutRegion.superclass.show.call(this);
45932     },
45933     
45934     beforeSlide: function(){
45935         if(Roo.isGecko){// firefox overflow auto bug workaround
45936             this.bodyEl.clip();
45937             if(this.tabs) this.tabs.bodyEl.clip();
45938             if(this.activePanel){
45939                 this.activePanel.getEl().clip();
45940                 
45941                 if(this.activePanel.beforeSlide){
45942                     this.activePanel.beforeSlide();
45943                 }
45944             }
45945         }
45946     },
45947     
45948     afterSlide : function(){
45949         if(Roo.isGecko){// firefox overflow auto bug workaround
45950             this.bodyEl.unclip();
45951             if(this.tabs) this.tabs.bodyEl.unclip();
45952             if(this.activePanel){
45953                 this.activePanel.getEl().unclip();
45954                 if(this.activePanel.afterSlide){
45955                     this.activePanel.afterSlide();
45956                 }
45957             }
45958         }
45959     },
45960
45961     initAutoHide : function(){
45962         if(this.autoHide !== false){
45963             if(!this.autoHideHd){
45964                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45965                 this.autoHideHd = {
45966                     "mouseout": function(e){
45967                         if(!e.within(this.el, true)){
45968                             st.delay(500);
45969                         }
45970                     },
45971                     "mouseover" : function(e){
45972                         st.cancel();
45973                     },
45974                     scope : this
45975                 };
45976             }
45977             this.el.on(this.autoHideHd);
45978         }
45979     },
45980
45981     clearAutoHide : function(){
45982         if(this.autoHide !== false){
45983             this.el.un("mouseout", this.autoHideHd.mouseout);
45984             this.el.un("mouseover", this.autoHideHd.mouseover);
45985         }
45986     },
45987
45988     clearMonitor : function(){
45989         Roo.get(document).un("click", this.slideInIf, this);
45990     },
45991
45992     // these names are backwards but not changed for compat
45993     slideOut : function(){
45994         if(this.isSlid || this.el.hasActiveFx()){
45995             return;
45996         }
45997         this.isSlid = true;
45998         if(this.collapseBtn){
45999             this.collapseBtn.hide();
46000         }
46001         this.closeBtnState = this.closeBtn.getStyle('display');
46002         this.closeBtn.hide();
46003         if(this.stickBtn){
46004             this.stickBtn.show();
46005         }
46006         this.el.show();
46007         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46008         this.beforeSlide();
46009         this.el.setStyle("z-index", 10001);
46010         this.el.slideIn(this.getSlideAnchor(), {
46011             callback: function(){
46012                 this.afterSlide();
46013                 this.initAutoHide();
46014                 Roo.get(document).on("click", this.slideInIf, this);
46015                 this.fireEvent("slideshow", this);
46016             },
46017             scope: this,
46018             block: true
46019         });
46020     },
46021
46022     afterSlideIn : function(){
46023         this.clearAutoHide();
46024         this.isSlid = false;
46025         this.clearMonitor();
46026         this.el.setStyle("z-index", "");
46027         if(this.collapseBtn){
46028             this.collapseBtn.show();
46029         }
46030         this.closeBtn.setStyle('display', this.closeBtnState);
46031         if(this.stickBtn){
46032             this.stickBtn.hide();
46033         }
46034         this.fireEvent("slidehide", this);
46035     },
46036
46037     slideIn : function(cb){
46038         if(!this.isSlid || this.el.hasActiveFx()){
46039             Roo.callback(cb);
46040             return;
46041         }
46042         this.isSlid = false;
46043         this.beforeSlide();
46044         this.el.slideOut(this.getSlideAnchor(), {
46045             callback: function(){
46046                 this.el.setLeftTop(-10000, -10000);
46047                 this.afterSlide();
46048                 this.afterSlideIn();
46049                 Roo.callback(cb);
46050             },
46051             scope: this,
46052             block: true
46053         });
46054     },
46055     
46056     slideInIf : function(e){
46057         if(!e.within(this.el)){
46058             this.slideIn();
46059         }
46060     },
46061
46062     animateCollapse : function(){
46063         this.beforeSlide();
46064         this.el.setStyle("z-index", 20000);
46065         var anchor = this.getSlideAnchor();
46066         this.el.slideOut(anchor, {
46067             callback : function(){
46068                 this.el.setStyle("z-index", "");
46069                 this.collapsedEl.slideIn(anchor, {duration:.3});
46070                 this.afterSlide();
46071                 this.el.setLocation(-10000,-10000);
46072                 this.el.hide();
46073                 this.fireEvent("collapsed", this);
46074             },
46075             scope: this,
46076             block: true
46077         });
46078     },
46079
46080     animateExpand : function(){
46081         this.beforeSlide();
46082         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46083         this.el.setStyle("z-index", 20000);
46084         this.collapsedEl.hide({
46085             duration:.1
46086         });
46087         this.el.slideIn(this.getSlideAnchor(), {
46088             callback : function(){
46089                 this.el.setStyle("z-index", "");
46090                 this.afterSlide();
46091                 if(this.split){
46092                     this.split.el.show();
46093                 }
46094                 this.fireEvent("invalidated", this);
46095                 this.fireEvent("expanded", this);
46096             },
46097             scope: this,
46098             block: true
46099         });
46100     },
46101
46102     anchors : {
46103         "west" : "left",
46104         "east" : "right",
46105         "north" : "top",
46106         "south" : "bottom"
46107     },
46108
46109     sanchors : {
46110         "west" : "l",
46111         "east" : "r",
46112         "north" : "t",
46113         "south" : "b"
46114     },
46115
46116     canchors : {
46117         "west" : "tl-tr",
46118         "east" : "tr-tl",
46119         "north" : "tl-bl",
46120         "south" : "bl-tl"
46121     },
46122
46123     getAnchor : function(){
46124         return this.anchors[this.position];
46125     },
46126
46127     getCollapseAnchor : function(){
46128         return this.canchors[this.position];
46129     },
46130
46131     getSlideAnchor : function(){
46132         return this.sanchors[this.position];
46133     },
46134
46135     getAlignAdj : function(){
46136         var cm = this.cmargins;
46137         switch(this.position){
46138             case "west":
46139                 return [0, 0];
46140             break;
46141             case "east":
46142                 return [0, 0];
46143             break;
46144             case "north":
46145                 return [0, 0];
46146             break;
46147             case "south":
46148                 return [0, 0];
46149             break;
46150         }
46151     },
46152
46153     getExpandAdj : function(){
46154         var c = this.collapsedEl, cm = this.cmargins;
46155         switch(this.position){
46156             case "west":
46157                 return [-(cm.right+c.getWidth()+cm.left), 0];
46158             break;
46159             case "east":
46160                 return [cm.right+c.getWidth()+cm.left, 0];
46161             break;
46162             case "north":
46163                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46164             break;
46165             case "south":
46166                 return [0, cm.top+cm.bottom+c.getHeight()];
46167             break;
46168         }
46169     }
46170 });/*
46171  * Based on:
46172  * Ext JS Library 1.1.1
46173  * Copyright(c) 2006-2007, Ext JS, LLC.
46174  *
46175  * Originally Released Under LGPL - original licence link has changed is not relivant.
46176  *
46177  * Fork - LGPL
46178  * <script type="text/javascript">
46179  */
46180 /*
46181  * These classes are private internal classes
46182  */
46183 Roo.CenterLayoutRegion = function(mgr, config){
46184     Roo.LayoutRegion.call(this, mgr, config, "center");
46185     this.visible = true;
46186     this.minWidth = config.minWidth || 20;
46187     this.minHeight = config.minHeight || 20;
46188 };
46189
46190 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46191     hide : function(){
46192         // center panel can't be hidden
46193     },
46194     
46195     show : function(){
46196         // center panel can't be hidden
46197     },
46198     
46199     getMinWidth: function(){
46200         return this.minWidth;
46201     },
46202     
46203     getMinHeight: function(){
46204         return this.minHeight;
46205     }
46206 });
46207
46208
46209 Roo.NorthLayoutRegion = function(mgr, config){
46210     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46211     if(this.split){
46212         this.split.placement = Roo.SplitBar.TOP;
46213         this.split.orientation = Roo.SplitBar.VERTICAL;
46214         this.split.el.addClass("x-layout-split-v");
46215     }
46216     var size = config.initialSize || config.height;
46217     if(typeof size != "undefined"){
46218         this.el.setHeight(size);
46219     }
46220 };
46221 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46222     orientation: Roo.SplitBar.VERTICAL,
46223     getBox : function(){
46224         if(this.collapsed){
46225             return this.collapsedEl.getBox();
46226         }
46227         var box = this.el.getBox();
46228         if(this.split){
46229             box.height += this.split.el.getHeight();
46230         }
46231         return box;
46232     },
46233     
46234     updateBox : function(box){
46235         if(this.split && !this.collapsed){
46236             box.height -= this.split.el.getHeight();
46237             this.split.el.setLeft(box.x);
46238             this.split.el.setTop(box.y+box.height);
46239             this.split.el.setWidth(box.width);
46240         }
46241         if(this.collapsed){
46242             this.updateBody(box.width, null);
46243         }
46244         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46245     }
46246 });
46247
46248 Roo.SouthLayoutRegion = function(mgr, config){
46249     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46250     if(this.split){
46251         this.split.placement = Roo.SplitBar.BOTTOM;
46252         this.split.orientation = Roo.SplitBar.VERTICAL;
46253         this.split.el.addClass("x-layout-split-v");
46254     }
46255     var size = config.initialSize || config.height;
46256     if(typeof size != "undefined"){
46257         this.el.setHeight(size);
46258     }
46259 };
46260 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46261     orientation: Roo.SplitBar.VERTICAL,
46262     getBox : function(){
46263         if(this.collapsed){
46264             return this.collapsedEl.getBox();
46265         }
46266         var box = this.el.getBox();
46267         if(this.split){
46268             var sh = this.split.el.getHeight();
46269             box.height += sh;
46270             box.y -= sh;
46271         }
46272         return box;
46273     },
46274     
46275     updateBox : function(box){
46276         if(this.split && !this.collapsed){
46277             var sh = this.split.el.getHeight();
46278             box.height -= sh;
46279             box.y += sh;
46280             this.split.el.setLeft(box.x);
46281             this.split.el.setTop(box.y-sh);
46282             this.split.el.setWidth(box.width);
46283         }
46284         if(this.collapsed){
46285             this.updateBody(box.width, null);
46286         }
46287         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46288     }
46289 });
46290
46291 Roo.EastLayoutRegion = function(mgr, config){
46292     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46293     if(this.split){
46294         this.split.placement = Roo.SplitBar.RIGHT;
46295         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46296         this.split.el.addClass("x-layout-split-h");
46297     }
46298     var size = config.initialSize || config.width;
46299     if(typeof size != "undefined"){
46300         this.el.setWidth(size);
46301     }
46302 };
46303 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46304     orientation: Roo.SplitBar.HORIZONTAL,
46305     getBox : function(){
46306         if(this.collapsed){
46307             return this.collapsedEl.getBox();
46308         }
46309         var box = this.el.getBox();
46310         if(this.split){
46311             var sw = this.split.el.getWidth();
46312             box.width += sw;
46313             box.x -= sw;
46314         }
46315         return box;
46316     },
46317
46318     updateBox : function(box){
46319         if(this.split && !this.collapsed){
46320             var sw = this.split.el.getWidth();
46321             box.width -= sw;
46322             this.split.el.setLeft(box.x);
46323             this.split.el.setTop(box.y);
46324             this.split.el.setHeight(box.height);
46325             box.x += sw;
46326         }
46327         if(this.collapsed){
46328             this.updateBody(null, box.height);
46329         }
46330         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46331     }
46332 });
46333
46334 Roo.WestLayoutRegion = function(mgr, config){
46335     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46336     if(this.split){
46337         this.split.placement = Roo.SplitBar.LEFT;
46338         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46339         this.split.el.addClass("x-layout-split-h");
46340     }
46341     var size = config.initialSize || config.width;
46342     if(typeof size != "undefined"){
46343         this.el.setWidth(size);
46344     }
46345 };
46346 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46347     orientation: Roo.SplitBar.HORIZONTAL,
46348     getBox : function(){
46349         if(this.collapsed){
46350             return this.collapsedEl.getBox();
46351         }
46352         var box = this.el.getBox();
46353         if(this.split){
46354             box.width += this.split.el.getWidth();
46355         }
46356         return box;
46357     },
46358     
46359     updateBox : function(box){
46360         if(this.split && !this.collapsed){
46361             var sw = this.split.el.getWidth();
46362             box.width -= sw;
46363             this.split.el.setLeft(box.x+box.width);
46364             this.split.el.setTop(box.y);
46365             this.split.el.setHeight(box.height);
46366         }
46367         if(this.collapsed){
46368             this.updateBody(null, box.height);
46369         }
46370         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46371     }
46372 });
46373 /*
46374  * Based on:
46375  * Ext JS Library 1.1.1
46376  * Copyright(c) 2006-2007, Ext JS, LLC.
46377  *
46378  * Originally Released Under LGPL - original licence link has changed is not relivant.
46379  *
46380  * Fork - LGPL
46381  * <script type="text/javascript">
46382  */
46383  
46384  
46385 /*
46386  * Private internal class for reading and applying state
46387  */
46388 Roo.LayoutStateManager = function(layout){
46389      // default empty state
46390      this.state = {
46391         north: {},
46392         south: {},
46393         east: {},
46394         west: {}       
46395     };
46396 };
46397
46398 Roo.LayoutStateManager.prototype = {
46399     init : function(layout, provider){
46400         this.provider = provider;
46401         var state = provider.get(layout.id+"-layout-state");
46402         if(state){
46403             var wasUpdating = layout.isUpdating();
46404             if(!wasUpdating){
46405                 layout.beginUpdate();
46406             }
46407             for(var key in state){
46408                 if(typeof state[key] != "function"){
46409                     var rstate = state[key];
46410                     var r = layout.getRegion(key);
46411                     if(r && rstate){
46412                         if(rstate.size){
46413                             r.resizeTo(rstate.size);
46414                         }
46415                         if(rstate.collapsed == true){
46416                             r.collapse(true);
46417                         }else{
46418                             r.expand(null, true);
46419                         }
46420                     }
46421                 }
46422             }
46423             if(!wasUpdating){
46424                 layout.endUpdate();
46425             }
46426             this.state = state; 
46427         }
46428         this.layout = layout;
46429         layout.on("regionresized", this.onRegionResized, this);
46430         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46431         layout.on("regionexpanded", this.onRegionExpanded, this);
46432     },
46433     
46434     storeState : function(){
46435         this.provider.set(this.layout.id+"-layout-state", this.state);
46436     },
46437     
46438     onRegionResized : function(region, newSize){
46439         this.state[region.getPosition()].size = newSize;
46440         this.storeState();
46441     },
46442     
46443     onRegionCollapsed : function(region){
46444         this.state[region.getPosition()].collapsed = true;
46445         this.storeState();
46446     },
46447     
46448     onRegionExpanded : function(region){
46449         this.state[region.getPosition()].collapsed = false;
46450         this.storeState();
46451     }
46452 };/*
46453  * Based on:
46454  * Ext JS Library 1.1.1
46455  * Copyright(c) 2006-2007, Ext JS, LLC.
46456  *
46457  * Originally Released Under LGPL - original licence link has changed is not relivant.
46458  *
46459  * Fork - LGPL
46460  * <script type="text/javascript">
46461  */
46462 /**
46463  * @class Roo.ContentPanel
46464  * @extends Roo.util.Observable
46465  * A basic ContentPanel element.
46466  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46467  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46468  * @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
46469  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46470  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46471  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46472  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46473  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46474  * @cfg {String} title          The title for this panel
46475  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46476  * @cfg {String} url            Calls {@link #setUrl} with this value
46477  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46478  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46479  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46480  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46481
46482  * @constructor
46483  * Create a new ContentPanel.
46484  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46485  * @param {String/Object} config A string to set only the title or a config object
46486  * @param {String} content (optional) Set the HTML content for this panel
46487  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46488  */
46489 Roo.ContentPanel = function(el, config, content){
46490     
46491      
46492     /*
46493     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46494         config = el;
46495         el = Roo.id();
46496     }
46497     if (config && config.parentLayout) { 
46498         el = config.parentLayout.el.createChild(); 
46499     }
46500     */
46501     if(el.autoCreate){ // xtype is available if this is called from factory
46502         config = el;
46503         el = Roo.id();
46504     }
46505     this.el = Roo.get(el);
46506     if(!this.el && config && config.autoCreate){
46507         if(typeof config.autoCreate == "object"){
46508             if(!config.autoCreate.id){
46509                 config.autoCreate.id = config.id||el;
46510             }
46511             this.el = Roo.DomHelper.append(document.body,
46512                         config.autoCreate, true);
46513         }else{
46514             this.el = Roo.DomHelper.append(document.body,
46515                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46516         }
46517     }
46518     this.closable = false;
46519     this.loaded = false;
46520     this.active = false;
46521     if(typeof config == "string"){
46522         this.title = config;
46523     }else{
46524         Roo.apply(this, config);
46525     }
46526     
46527     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46528         this.wrapEl = this.el.wrap();
46529         this.toolbar.container = this.el.insertSibling(false, 'before');
46530         this.toolbar = new Roo.Toolbar(this.toolbar);
46531     }
46532     
46533     
46534     
46535     if(this.resizeEl){
46536         this.resizeEl = Roo.get(this.resizeEl, true);
46537     }else{
46538         this.resizeEl = this.el;
46539     }
46540     this.addEvents({
46541         /**
46542          * @event activate
46543          * Fires when this panel is activated. 
46544          * @param {Roo.ContentPanel} this
46545          */
46546         "activate" : true,
46547         /**
46548          * @event deactivate
46549          * Fires when this panel is activated. 
46550          * @param {Roo.ContentPanel} this
46551          */
46552         "deactivate" : true,
46553
46554         /**
46555          * @event resize
46556          * Fires when this panel is resized if fitToFrame is true.
46557          * @param {Roo.ContentPanel} this
46558          * @param {Number} width The width after any component adjustments
46559          * @param {Number} height The height after any component adjustments
46560          */
46561         "resize" : true,
46562         
46563          /**
46564          * @event render
46565          * Fires when this tab is created
46566          * @param {Roo.ContentPanel} this
46567          */
46568         "render" : true
46569         
46570         
46571         
46572     });
46573     if(this.autoScroll){
46574         this.resizeEl.setStyle("overflow", "auto");
46575     } else {
46576         // fix randome scrolling
46577         this.el.on('scroll', function() {
46578             Roo.log('fix random scolling');
46579             this.scrollTo('top',0); 
46580         });
46581     }
46582     content = content || this.content;
46583     if(content){
46584         this.setContent(content);
46585     }
46586     if(config && config.url){
46587         this.setUrl(this.url, this.params, this.loadOnce);
46588     }
46589     
46590     
46591     
46592     Roo.ContentPanel.superclass.constructor.call(this);
46593     
46594     this.fireEvent('render', this);
46595 };
46596
46597 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46598     tabTip:'',
46599     setRegion : function(region){
46600         this.region = region;
46601         if(region){
46602            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46603         }else{
46604            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46605         } 
46606     },
46607     
46608     /**
46609      * Returns the toolbar for this Panel if one was configured. 
46610      * @return {Roo.Toolbar} 
46611      */
46612     getToolbar : function(){
46613         return this.toolbar;
46614     },
46615     
46616     setActiveState : function(active){
46617         this.active = active;
46618         if(!active){
46619             this.fireEvent("deactivate", this);
46620         }else{
46621             this.fireEvent("activate", this);
46622         }
46623     },
46624     /**
46625      * Updates this panel's element
46626      * @param {String} content The new content
46627      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46628     */
46629     setContent : function(content, loadScripts){
46630         this.el.update(content, loadScripts);
46631     },
46632
46633     ignoreResize : function(w, h){
46634         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46635             return true;
46636         }else{
46637             this.lastSize = {width: w, height: h};
46638             return false;
46639         }
46640     },
46641     /**
46642      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46643      * @return {Roo.UpdateManager} The UpdateManager
46644      */
46645     getUpdateManager : function(){
46646         return this.el.getUpdateManager();
46647     },
46648      /**
46649      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46650      * @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:
46651 <pre><code>
46652 panel.load({
46653     url: "your-url.php",
46654     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46655     callback: yourFunction,
46656     scope: yourObject, //(optional scope)
46657     discardUrl: false,
46658     nocache: false,
46659     text: "Loading...",
46660     timeout: 30,
46661     scripts: false
46662 });
46663 </code></pre>
46664      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46665      * 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.
46666      * @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}
46667      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46668      * @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.
46669      * @return {Roo.ContentPanel} this
46670      */
46671     load : function(){
46672         var um = this.el.getUpdateManager();
46673         um.update.apply(um, arguments);
46674         return this;
46675     },
46676
46677
46678     /**
46679      * 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.
46680      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46681      * @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)
46682      * @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)
46683      * @return {Roo.UpdateManager} The UpdateManager
46684      */
46685     setUrl : function(url, params, loadOnce){
46686         if(this.refreshDelegate){
46687             this.removeListener("activate", this.refreshDelegate);
46688         }
46689         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46690         this.on("activate", this.refreshDelegate);
46691         return this.el.getUpdateManager();
46692     },
46693     
46694     _handleRefresh : function(url, params, loadOnce){
46695         if(!loadOnce || !this.loaded){
46696             var updater = this.el.getUpdateManager();
46697             updater.update(url, params, this._setLoaded.createDelegate(this));
46698         }
46699     },
46700     
46701     _setLoaded : function(){
46702         this.loaded = true;
46703     }, 
46704     
46705     /**
46706      * Returns this panel's id
46707      * @return {String} 
46708      */
46709     getId : function(){
46710         return this.el.id;
46711     },
46712     
46713     /** 
46714      * Returns this panel's element - used by regiosn to add.
46715      * @return {Roo.Element} 
46716      */
46717     getEl : function(){
46718         return this.wrapEl || this.el;
46719     },
46720     
46721     adjustForComponents : function(width, height){
46722         if(this.resizeEl != this.el){
46723             width -= this.el.getFrameWidth('lr');
46724             height -= this.el.getFrameWidth('tb');
46725         }
46726         if(this.toolbar){
46727             var te = this.toolbar.getEl();
46728             height -= te.getHeight();
46729             te.setWidth(width);
46730         }
46731         if(this.adjustments){
46732             width += this.adjustments[0];
46733             height += this.adjustments[1];
46734         }
46735         return {"width": width, "height": height};
46736     },
46737     
46738     setSize : function(width, height){
46739         if(this.fitToFrame && !this.ignoreResize(width, height)){
46740             if(this.fitContainer && this.resizeEl != this.el){
46741                 this.el.setSize(width, height);
46742             }
46743             var size = this.adjustForComponents(width, height);
46744             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46745             this.fireEvent('resize', this, size.width, size.height);
46746         }
46747     },
46748     
46749     /**
46750      * Returns this panel's title
46751      * @return {String} 
46752      */
46753     getTitle : function(){
46754         return this.title;
46755     },
46756     
46757     /**
46758      * Set this panel's title
46759      * @param {String} title
46760      */
46761     setTitle : function(title){
46762         this.title = title;
46763         if(this.region){
46764             this.region.updatePanelTitle(this, title);
46765         }
46766     },
46767     
46768     /**
46769      * Returns true is this panel was configured to be closable
46770      * @return {Boolean} 
46771      */
46772     isClosable : function(){
46773         return this.closable;
46774     },
46775     
46776     beforeSlide : function(){
46777         this.el.clip();
46778         this.resizeEl.clip();
46779     },
46780     
46781     afterSlide : function(){
46782         this.el.unclip();
46783         this.resizeEl.unclip();
46784     },
46785     
46786     /**
46787      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46788      *   Will fail silently if the {@link #setUrl} method has not been called.
46789      *   This does not activate the panel, just updates its content.
46790      */
46791     refresh : function(){
46792         if(this.refreshDelegate){
46793            this.loaded = false;
46794            this.refreshDelegate();
46795         }
46796     },
46797     
46798     /**
46799      * Destroys this panel
46800      */
46801     destroy : function(){
46802         this.el.removeAllListeners();
46803         var tempEl = document.createElement("span");
46804         tempEl.appendChild(this.el.dom);
46805         tempEl.innerHTML = "";
46806         this.el.remove();
46807         this.el = null;
46808     },
46809     
46810     /**
46811      * form - if the content panel contains a form - this is a reference to it.
46812      * @type {Roo.form.Form}
46813      */
46814     form : false,
46815     /**
46816      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46817      *    This contains a reference to it.
46818      * @type {Roo.View}
46819      */
46820     view : false,
46821     
46822       /**
46823      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46824      * <pre><code>
46825
46826 layout.addxtype({
46827        xtype : 'Form',
46828        items: [ .... ]
46829    }
46830 );
46831
46832 </code></pre>
46833      * @param {Object} cfg Xtype definition of item to add.
46834      */
46835     
46836     addxtype : function(cfg) {
46837         // add form..
46838         if (cfg.xtype.match(/^Form$/)) {
46839             var el = this.el.createChild();
46840
46841             this.form = new  Roo.form.Form(cfg);
46842             
46843             
46844             if ( this.form.allItems.length) this.form.render(el.dom);
46845             return this.form;
46846         }
46847         // should only have one of theses..
46848         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46849             // views..
46850             cfg.el = this.el.appendChild(document.createElement("div"));
46851             // factory?
46852             
46853             var ret = new Roo.factory(cfg);
46854             ret.render && ret.render(false, ''); // render blank..
46855             this.view = ret;
46856             return ret;
46857         }
46858         return false;
46859     }
46860 });
46861
46862 /**
46863  * @class Roo.GridPanel
46864  * @extends Roo.ContentPanel
46865  * @constructor
46866  * Create a new GridPanel.
46867  * @param {Roo.grid.Grid} grid The grid for this panel
46868  * @param {String/Object} config A string to set only the panel's title, or a config object
46869  */
46870 Roo.GridPanel = function(grid, config){
46871     
46872   
46873     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46874         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46875         
46876     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46877     
46878     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46879     
46880     if(this.toolbar){
46881         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46882     }
46883     // xtype created footer. - not sure if will work as we normally have to render first..
46884     if (this.footer && !this.footer.el && this.footer.xtype) {
46885         
46886         this.footer.container = this.grid.getView().getFooterPanel(true);
46887         this.footer.dataSource = this.grid.dataSource;
46888         this.footer = Roo.factory(this.footer, Roo);
46889         
46890     }
46891     
46892     grid.monitorWindowResize = false; // turn off autosizing
46893     grid.autoHeight = false;
46894     grid.autoWidth = false;
46895     this.grid = grid;
46896     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46897 };
46898
46899 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46900     getId : function(){
46901         return this.grid.id;
46902     },
46903     
46904     /**
46905      * Returns the grid for this panel
46906      * @return {Roo.grid.Grid} 
46907      */
46908     getGrid : function(){
46909         return this.grid;    
46910     },
46911     
46912     setSize : function(width, height){
46913         if(!this.ignoreResize(width, height)){
46914             var grid = this.grid;
46915             var size = this.adjustForComponents(width, height);
46916             grid.getGridEl().setSize(size.width, size.height);
46917             grid.autoSize();
46918         }
46919     },
46920     
46921     beforeSlide : function(){
46922         this.grid.getView().scroller.clip();
46923     },
46924     
46925     afterSlide : function(){
46926         this.grid.getView().scroller.unclip();
46927     },
46928     
46929     destroy : function(){
46930         this.grid.destroy();
46931         delete this.grid;
46932         Roo.GridPanel.superclass.destroy.call(this); 
46933     }
46934 });
46935
46936
46937 /**
46938  * @class Roo.NestedLayoutPanel
46939  * @extends Roo.ContentPanel
46940  * @constructor
46941  * Create a new NestedLayoutPanel.
46942  * 
46943  * 
46944  * @param {Roo.BorderLayout} layout The layout for this panel
46945  * @param {String/Object} config A string to set only the title or a config object
46946  */
46947 Roo.NestedLayoutPanel = function(layout, config)
46948 {
46949     // construct with only one argument..
46950     /* FIXME - implement nicer consturctors
46951     if (layout.layout) {
46952         config = layout;
46953         layout = config.layout;
46954         delete config.layout;
46955     }
46956     if (layout.xtype && !layout.getEl) {
46957         // then layout needs constructing..
46958         layout = Roo.factory(layout, Roo);
46959     }
46960     */
46961     
46962     
46963     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46964     
46965     layout.monitorWindowResize = false; // turn off autosizing
46966     this.layout = layout;
46967     this.layout.getEl().addClass("x-layout-nested-layout");
46968     
46969     
46970     
46971     
46972 };
46973
46974 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46975
46976     setSize : function(width, height){
46977         if(!this.ignoreResize(width, height)){
46978             var size = this.adjustForComponents(width, height);
46979             var el = this.layout.getEl();
46980             el.setSize(size.width, size.height);
46981             var touch = el.dom.offsetWidth;
46982             this.layout.layout();
46983             // ie requires a double layout on the first pass
46984             if(Roo.isIE && !this.initialized){
46985                 this.initialized = true;
46986                 this.layout.layout();
46987             }
46988         }
46989     },
46990     
46991     // activate all subpanels if not currently active..
46992     
46993     setActiveState : function(active){
46994         this.active = active;
46995         if(!active){
46996             this.fireEvent("deactivate", this);
46997             return;
46998         }
46999         
47000         this.fireEvent("activate", this);
47001         // not sure if this should happen before or after..
47002         if (!this.layout) {
47003             return; // should not happen..
47004         }
47005         var reg = false;
47006         for (var r in this.layout.regions) {
47007             reg = this.layout.getRegion(r);
47008             if (reg.getActivePanel()) {
47009                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47010                 reg.setActivePanel(reg.getActivePanel());
47011                 continue;
47012             }
47013             if (!reg.panels.length) {
47014                 continue;
47015             }
47016             reg.showPanel(reg.getPanel(0));
47017         }
47018         
47019         
47020         
47021         
47022     },
47023     
47024     /**
47025      * Returns the nested BorderLayout for this panel
47026      * @return {Roo.BorderLayout} 
47027      */
47028     getLayout : function(){
47029         return this.layout;
47030     },
47031     
47032      /**
47033      * Adds a xtype elements to the layout of the nested panel
47034      * <pre><code>
47035
47036 panel.addxtype({
47037        xtype : 'ContentPanel',
47038        region: 'west',
47039        items: [ .... ]
47040    }
47041 );
47042
47043 panel.addxtype({
47044         xtype : 'NestedLayoutPanel',
47045         region: 'west',
47046         layout: {
47047            center: { },
47048            west: { }   
47049         },
47050         items : [ ... list of content panels or nested layout panels.. ]
47051    }
47052 );
47053 </code></pre>
47054      * @param {Object} cfg Xtype definition of item to add.
47055      */
47056     addxtype : function(cfg) {
47057         return this.layout.addxtype(cfg);
47058     
47059     }
47060 });
47061
47062 Roo.ScrollPanel = function(el, config, content){
47063     config = config || {};
47064     config.fitToFrame = true;
47065     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47066     
47067     this.el.dom.style.overflow = "hidden";
47068     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47069     this.el.removeClass("x-layout-inactive-content");
47070     this.el.on("mousewheel", this.onWheel, this);
47071
47072     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47073     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47074     up.unselectable(); down.unselectable();
47075     up.on("click", this.scrollUp, this);
47076     down.on("click", this.scrollDown, this);
47077     up.addClassOnOver("x-scroller-btn-over");
47078     down.addClassOnOver("x-scroller-btn-over");
47079     up.addClassOnClick("x-scroller-btn-click");
47080     down.addClassOnClick("x-scroller-btn-click");
47081     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47082
47083     this.resizeEl = this.el;
47084     this.el = wrap; this.up = up; this.down = down;
47085 };
47086
47087 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47088     increment : 100,
47089     wheelIncrement : 5,
47090     scrollUp : function(){
47091         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47092     },
47093
47094     scrollDown : function(){
47095         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47096     },
47097
47098     afterScroll : function(){
47099         var el = this.resizeEl;
47100         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47101         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47102         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47103     },
47104
47105     setSize : function(){
47106         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47107         this.afterScroll();
47108     },
47109
47110     onWheel : function(e){
47111         var d = e.getWheelDelta();
47112         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47113         this.afterScroll();
47114         e.stopEvent();
47115     },
47116
47117     setContent : function(content, loadScripts){
47118         this.resizeEl.update(content, loadScripts);
47119     }
47120
47121 });
47122
47123
47124
47125
47126
47127
47128
47129
47130
47131 /**
47132  * @class Roo.TreePanel
47133  * @extends Roo.ContentPanel
47134  * @constructor
47135  * Create a new TreePanel. - defaults to fit/scoll contents.
47136  * @param {String/Object} config A string to set only the panel's title, or a config object
47137  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47138  */
47139 Roo.TreePanel = function(config){
47140     var el = config.el;
47141     var tree = config.tree;
47142     delete config.tree; 
47143     delete config.el; // hopefull!
47144     
47145     // wrapper for IE7 strict & safari scroll issue
47146     
47147     var treeEl = el.createChild();
47148     config.resizeEl = treeEl;
47149     
47150     
47151     
47152     Roo.TreePanel.superclass.constructor.call(this, el, config);
47153  
47154  
47155     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47156     //console.log(tree);
47157     this.on('activate', function()
47158     {
47159         if (this.tree.rendered) {
47160             return;
47161         }
47162         //console.log('render tree');
47163         this.tree.render();
47164     });
47165     
47166     this.on('resize',  function (cp, w, h) {
47167             this.tree.innerCt.setWidth(w);
47168             this.tree.innerCt.setHeight(h);
47169             this.tree.innerCt.setStyle('overflow-y', 'auto');
47170     });
47171
47172         
47173     
47174 };
47175
47176 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47177     fitToFrame : true,
47178     autoScroll : true
47179 });
47180
47181
47182
47183
47184
47185
47186
47187
47188
47189
47190
47191 /*
47192  * Based on:
47193  * Ext JS Library 1.1.1
47194  * Copyright(c) 2006-2007, Ext JS, LLC.
47195  *
47196  * Originally Released Under LGPL - original licence link has changed is not relivant.
47197  *
47198  * Fork - LGPL
47199  * <script type="text/javascript">
47200  */
47201  
47202
47203 /**
47204  * @class Roo.ReaderLayout
47205  * @extends Roo.BorderLayout
47206  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47207  * center region containing two nested regions (a top one for a list view and one for item preview below),
47208  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47209  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47210  * expedites the setup of the overall layout and regions for this common application style.
47211  * Example:
47212  <pre><code>
47213 var reader = new Roo.ReaderLayout();
47214 var CP = Roo.ContentPanel;  // shortcut for adding
47215
47216 reader.beginUpdate();
47217 reader.add("north", new CP("north", "North"));
47218 reader.add("west", new CP("west", {title: "West"}));
47219 reader.add("east", new CP("east", {title: "East"}));
47220
47221 reader.regions.listView.add(new CP("listView", "List"));
47222 reader.regions.preview.add(new CP("preview", "Preview"));
47223 reader.endUpdate();
47224 </code></pre>
47225 * @constructor
47226 * Create a new ReaderLayout
47227 * @param {Object} config Configuration options
47228 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47229 * document.body if omitted)
47230 */
47231 Roo.ReaderLayout = function(config, renderTo){
47232     var c = config || {size:{}};
47233     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47234         north: c.north !== false ? Roo.apply({
47235             split:false,
47236             initialSize: 32,
47237             titlebar: false
47238         }, c.north) : false,
47239         west: c.west !== false ? Roo.apply({
47240             split:true,
47241             initialSize: 200,
47242             minSize: 175,
47243             maxSize: 400,
47244             titlebar: true,
47245             collapsible: true,
47246             animate: true,
47247             margins:{left:5,right:0,bottom:5,top:5},
47248             cmargins:{left:5,right:5,bottom:5,top:5}
47249         }, c.west) : false,
47250         east: c.east !== false ? Roo.apply({
47251             split:true,
47252             initialSize: 200,
47253             minSize: 175,
47254             maxSize: 400,
47255             titlebar: true,
47256             collapsible: true,
47257             animate: true,
47258             margins:{left:0,right:5,bottom:5,top:5},
47259             cmargins:{left:5,right:5,bottom:5,top:5}
47260         }, c.east) : false,
47261         center: Roo.apply({
47262             tabPosition: 'top',
47263             autoScroll:false,
47264             closeOnTab: true,
47265             titlebar:false,
47266             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47267         }, c.center)
47268     });
47269
47270     this.el.addClass('x-reader');
47271
47272     this.beginUpdate();
47273
47274     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47275         south: c.preview !== false ? Roo.apply({
47276             split:true,
47277             initialSize: 200,
47278             minSize: 100,
47279             autoScroll:true,
47280             collapsible:true,
47281             titlebar: true,
47282             cmargins:{top:5,left:0, right:0, bottom:0}
47283         }, c.preview) : false,
47284         center: Roo.apply({
47285             autoScroll:false,
47286             titlebar:false,
47287             minHeight:200
47288         }, c.listView)
47289     });
47290     this.add('center', new Roo.NestedLayoutPanel(inner,
47291             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47292
47293     this.endUpdate();
47294
47295     this.regions.preview = inner.getRegion('south');
47296     this.regions.listView = inner.getRegion('center');
47297 };
47298
47299 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47300  * Based on:
47301  * Ext JS Library 1.1.1
47302  * Copyright(c) 2006-2007, Ext JS, LLC.
47303  *
47304  * Originally Released Under LGPL - original licence link has changed is not relivant.
47305  *
47306  * Fork - LGPL
47307  * <script type="text/javascript">
47308  */
47309  
47310 /**
47311  * @class Roo.grid.Grid
47312  * @extends Roo.util.Observable
47313  * This class represents the primary interface of a component based grid control.
47314  * <br><br>Usage:<pre><code>
47315  var grid = new Roo.grid.Grid("my-container-id", {
47316      ds: myDataStore,
47317      cm: myColModel,
47318      selModel: mySelectionModel,
47319      autoSizeColumns: true,
47320      monitorWindowResize: false,
47321      trackMouseOver: true
47322  });
47323  // set any options
47324  grid.render();
47325  * </code></pre>
47326  * <b>Common Problems:</b><br/>
47327  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47328  * element will correct this<br/>
47329  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47330  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47331  * are unpredictable.<br/>
47332  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47333  * grid to calculate dimensions/offsets.<br/>
47334   * @constructor
47335  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47336  * The container MUST have some type of size defined for the grid to fill. The container will be
47337  * automatically set to position relative if it isn't already.
47338  * @param {Object} config A config object that sets properties on this grid.
47339  */
47340 Roo.grid.Grid = function(container, config){
47341         // initialize the container
47342         this.container = Roo.get(container);
47343         this.container.update("");
47344         this.container.setStyle("overflow", "hidden");
47345     this.container.addClass('x-grid-container');
47346
47347     this.id = this.container.id;
47348
47349     Roo.apply(this, config);
47350     // check and correct shorthanded configs
47351     if(this.ds){
47352         this.dataSource = this.ds;
47353         delete this.ds;
47354     }
47355     if(this.cm){
47356         this.colModel = this.cm;
47357         delete this.cm;
47358     }
47359     if(this.sm){
47360         this.selModel = this.sm;
47361         delete this.sm;
47362     }
47363
47364     if (this.selModel) {
47365         this.selModel = Roo.factory(this.selModel, Roo.grid);
47366         this.sm = this.selModel;
47367         this.sm.xmodule = this.xmodule || false;
47368     }
47369     if (typeof(this.colModel.config) == 'undefined') {
47370         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47371         this.cm = this.colModel;
47372         this.cm.xmodule = this.xmodule || false;
47373     }
47374     if (this.dataSource) {
47375         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47376         this.ds = this.dataSource;
47377         this.ds.xmodule = this.xmodule || false;
47378          
47379     }
47380     
47381     
47382     
47383     if(this.width){
47384         this.container.setWidth(this.width);
47385     }
47386
47387     if(this.height){
47388         this.container.setHeight(this.height);
47389     }
47390     /** @private */
47391         this.addEvents({
47392         // raw events
47393         /**
47394          * @event click
47395          * The raw click event for the entire grid.
47396          * @param {Roo.EventObject} e
47397          */
47398         "click" : true,
47399         /**
47400          * @event dblclick
47401          * The raw dblclick event for the entire grid.
47402          * @param {Roo.EventObject} e
47403          */
47404         "dblclick" : true,
47405         /**
47406          * @event contextmenu
47407          * The raw contextmenu event for the entire grid.
47408          * @param {Roo.EventObject} e
47409          */
47410         "contextmenu" : true,
47411         /**
47412          * @event mousedown
47413          * The raw mousedown event for the entire grid.
47414          * @param {Roo.EventObject} e
47415          */
47416         "mousedown" : true,
47417         /**
47418          * @event mouseup
47419          * The raw mouseup event for the entire grid.
47420          * @param {Roo.EventObject} e
47421          */
47422         "mouseup" : true,
47423         /**
47424          * @event mouseover
47425          * The raw mouseover event for the entire grid.
47426          * @param {Roo.EventObject} e
47427          */
47428         "mouseover" : true,
47429         /**
47430          * @event mouseout
47431          * The raw mouseout event for the entire grid.
47432          * @param {Roo.EventObject} e
47433          */
47434         "mouseout" : true,
47435         /**
47436          * @event keypress
47437          * The raw keypress event for the entire grid.
47438          * @param {Roo.EventObject} e
47439          */
47440         "keypress" : true,
47441         /**
47442          * @event keydown
47443          * The raw keydown event for the entire grid.
47444          * @param {Roo.EventObject} e
47445          */
47446         "keydown" : true,
47447
47448         // custom events
47449
47450         /**
47451          * @event cellclick
47452          * Fires when a cell is clicked
47453          * @param {Grid} this
47454          * @param {Number} rowIndex
47455          * @param {Number} columnIndex
47456          * @param {Roo.EventObject} e
47457          */
47458         "cellclick" : true,
47459         /**
47460          * @event celldblclick
47461          * Fires when a cell is double clicked
47462          * @param {Grid} this
47463          * @param {Number} rowIndex
47464          * @param {Number} columnIndex
47465          * @param {Roo.EventObject} e
47466          */
47467         "celldblclick" : true,
47468         /**
47469          * @event rowclick
47470          * Fires when a row is clicked
47471          * @param {Grid} this
47472          * @param {Number} rowIndex
47473          * @param {Roo.EventObject} e
47474          */
47475         "rowclick" : true,
47476         /**
47477          * @event rowdblclick
47478          * Fires when a row is double clicked
47479          * @param {Grid} this
47480          * @param {Number} rowIndex
47481          * @param {Roo.EventObject} e
47482          */
47483         "rowdblclick" : true,
47484         /**
47485          * @event headerclick
47486          * Fires when a header is clicked
47487          * @param {Grid} this
47488          * @param {Number} columnIndex
47489          * @param {Roo.EventObject} e
47490          */
47491         "headerclick" : true,
47492         /**
47493          * @event headerdblclick
47494          * Fires when a header cell is double clicked
47495          * @param {Grid} this
47496          * @param {Number} columnIndex
47497          * @param {Roo.EventObject} e
47498          */
47499         "headerdblclick" : true,
47500         /**
47501          * @event rowcontextmenu
47502          * Fires when a row is right clicked
47503          * @param {Grid} this
47504          * @param {Number} rowIndex
47505          * @param {Roo.EventObject} e
47506          */
47507         "rowcontextmenu" : true,
47508         /**
47509          * @event cellcontextmenu
47510          * Fires when a cell is right clicked
47511          * @param {Grid} this
47512          * @param {Number} rowIndex
47513          * @param {Number} cellIndex
47514          * @param {Roo.EventObject} e
47515          */
47516          "cellcontextmenu" : true,
47517         /**
47518          * @event headercontextmenu
47519          * Fires when a header is right clicked
47520          * @param {Grid} this
47521          * @param {Number} columnIndex
47522          * @param {Roo.EventObject} e
47523          */
47524         "headercontextmenu" : true,
47525         /**
47526          * @event bodyscroll
47527          * Fires when the body element is scrolled
47528          * @param {Number} scrollLeft
47529          * @param {Number} scrollTop
47530          */
47531         "bodyscroll" : true,
47532         /**
47533          * @event columnresize
47534          * Fires when the user resizes a column
47535          * @param {Number} columnIndex
47536          * @param {Number} newSize
47537          */
47538         "columnresize" : true,
47539         /**
47540          * @event columnmove
47541          * Fires when the user moves a column
47542          * @param {Number} oldIndex
47543          * @param {Number} newIndex
47544          */
47545         "columnmove" : true,
47546         /**
47547          * @event startdrag
47548          * Fires when row(s) start being dragged
47549          * @param {Grid} this
47550          * @param {Roo.GridDD} dd The drag drop object
47551          * @param {event} e The raw browser event
47552          */
47553         "startdrag" : true,
47554         /**
47555          * @event enddrag
47556          * Fires when a drag operation is complete
47557          * @param {Grid} this
47558          * @param {Roo.GridDD} dd The drag drop object
47559          * @param {event} e The raw browser event
47560          */
47561         "enddrag" : true,
47562         /**
47563          * @event dragdrop
47564          * Fires when dragged row(s) are dropped on a valid DD target
47565          * @param {Grid} this
47566          * @param {Roo.GridDD} dd The drag drop object
47567          * @param {String} targetId The target drag drop object
47568          * @param {event} e The raw browser event
47569          */
47570         "dragdrop" : true,
47571         /**
47572          * @event dragover
47573          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47574          * @param {Grid} this
47575          * @param {Roo.GridDD} dd The drag drop object
47576          * @param {String} targetId The target drag drop object
47577          * @param {event} e The raw browser event
47578          */
47579         "dragover" : true,
47580         /**
47581          * @event dragenter
47582          *  Fires when the dragged row(s) first cross another DD target while being dragged
47583          * @param {Grid} this
47584          * @param {Roo.GridDD} dd The drag drop object
47585          * @param {String} targetId The target drag drop object
47586          * @param {event} e The raw browser event
47587          */
47588         "dragenter" : true,
47589         /**
47590          * @event dragout
47591          * Fires when the dragged row(s) leave another DD target while being dragged
47592          * @param {Grid} this
47593          * @param {Roo.GridDD} dd The drag drop object
47594          * @param {String} targetId The target drag drop object
47595          * @param {event} e The raw browser event
47596          */
47597         "dragout" : true,
47598         /**
47599          * @event rowclass
47600          * Fires when a row is rendered, so you can change add a style to it.
47601          * @param {GridView} gridview   The grid view
47602          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47603          */
47604         'rowclass' : true,
47605
47606         /**
47607          * @event render
47608          * Fires when the grid is rendered
47609          * @param {Grid} grid
47610          */
47611         'render' : true
47612     });
47613
47614     Roo.grid.Grid.superclass.constructor.call(this);
47615 };
47616 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47617     
47618     /**
47619      * @cfg {String} ddGroup - drag drop group.
47620      */
47621
47622     /**
47623      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47624      */
47625     minColumnWidth : 25,
47626
47627     /**
47628      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47629      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47630      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47631      */
47632     autoSizeColumns : false,
47633
47634     /**
47635      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47636      */
47637     autoSizeHeaders : true,
47638
47639     /**
47640      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47641      */
47642     monitorWindowResize : true,
47643
47644     /**
47645      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47646      * rows measured to get a columns size. Default is 0 (all rows).
47647      */
47648     maxRowsToMeasure : 0,
47649
47650     /**
47651      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47652      */
47653     trackMouseOver : true,
47654
47655     /**
47656     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47657     */
47658     
47659     /**
47660     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47661     */
47662     enableDragDrop : false,
47663     
47664     /**
47665     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47666     */
47667     enableColumnMove : true,
47668     
47669     /**
47670     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47671     */
47672     enableColumnHide : true,
47673     
47674     /**
47675     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47676     */
47677     enableRowHeightSync : false,
47678     
47679     /**
47680     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47681     */
47682     stripeRows : true,
47683     
47684     /**
47685     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47686     */
47687     autoHeight : false,
47688
47689     /**
47690      * @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.
47691      */
47692     autoExpandColumn : false,
47693
47694     /**
47695     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47696     * Default is 50.
47697     */
47698     autoExpandMin : 50,
47699
47700     /**
47701     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47702     */
47703     autoExpandMax : 1000,
47704
47705     /**
47706     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47707     */
47708     view : null,
47709
47710     /**
47711     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47712     */
47713     loadMask : false,
47714     /**
47715     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47716     */
47717     dropTarget: false,
47718     
47719    
47720     
47721     // private
47722     rendered : false,
47723
47724     /**
47725     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47726     * of a fixed width. Default is false.
47727     */
47728     /**
47729     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47730     */
47731     /**
47732      * Called once after all setup has been completed and the grid is ready to be rendered.
47733      * @return {Roo.grid.Grid} this
47734      */
47735     render : function()
47736     {
47737         var c = this.container;
47738         // try to detect autoHeight/width mode
47739         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47740             this.autoHeight = true;
47741         }
47742         var view = this.getView();
47743         view.init(this);
47744
47745         c.on("click", this.onClick, this);
47746         c.on("dblclick", this.onDblClick, this);
47747         c.on("contextmenu", this.onContextMenu, this);
47748         c.on("keydown", this.onKeyDown, this);
47749
47750         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47751
47752         this.getSelectionModel().init(this);
47753
47754         view.render();
47755
47756         if(this.loadMask){
47757             this.loadMask = new Roo.LoadMask(this.container,
47758                     Roo.apply({store:this.dataSource}, this.loadMask));
47759         }
47760         
47761         
47762         if (this.toolbar && this.toolbar.xtype) {
47763             this.toolbar.container = this.getView().getHeaderPanel(true);
47764             this.toolbar = new Roo.Toolbar(this.toolbar);
47765         }
47766         if (this.footer && this.footer.xtype) {
47767             this.footer.dataSource = this.getDataSource();
47768             this.footer.container = this.getView().getFooterPanel(true);
47769             this.footer = Roo.factory(this.footer, Roo);
47770         }
47771         if (this.dropTarget && this.dropTarget.xtype) {
47772             delete this.dropTarget.xtype;
47773             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47774         }
47775         
47776         
47777         this.rendered = true;
47778         this.fireEvent('render', this);
47779         return this;
47780     },
47781
47782         /**
47783          * Reconfigures the grid to use a different Store and Column Model.
47784          * The View will be bound to the new objects and refreshed.
47785          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47786          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47787          */
47788     reconfigure : function(dataSource, colModel){
47789         if(this.loadMask){
47790             this.loadMask.destroy();
47791             this.loadMask = new Roo.LoadMask(this.container,
47792                     Roo.apply({store:dataSource}, this.loadMask));
47793         }
47794         this.view.bind(dataSource, colModel);
47795         this.dataSource = dataSource;
47796         this.colModel = colModel;
47797         this.view.refresh(true);
47798     },
47799
47800     // private
47801     onKeyDown : function(e){
47802         this.fireEvent("keydown", e);
47803     },
47804
47805     /**
47806      * Destroy this grid.
47807      * @param {Boolean} removeEl True to remove the element
47808      */
47809     destroy : function(removeEl, keepListeners){
47810         if(this.loadMask){
47811             this.loadMask.destroy();
47812         }
47813         var c = this.container;
47814         c.removeAllListeners();
47815         this.view.destroy();
47816         this.colModel.purgeListeners();
47817         if(!keepListeners){
47818             this.purgeListeners();
47819         }
47820         c.update("");
47821         if(removeEl === true){
47822             c.remove();
47823         }
47824     },
47825
47826     // private
47827     processEvent : function(name, e){
47828         this.fireEvent(name, e);
47829         var t = e.getTarget();
47830         var v = this.view;
47831         var header = v.findHeaderIndex(t);
47832         if(header !== false){
47833             this.fireEvent("header" + name, this, header, e);
47834         }else{
47835             var row = v.findRowIndex(t);
47836             var cell = v.findCellIndex(t);
47837             if(row !== false){
47838                 this.fireEvent("row" + name, this, row, e);
47839                 if(cell !== false){
47840                     this.fireEvent("cell" + name, this, row, cell, e);
47841                 }
47842             }
47843         }
47844     },
47845
47846     // private
47847     onClick : function(e){
47848         this.processEvent("click", e);
47849     },
47850
47851     // private
47852     onContextMenu : function(e, t){
47853         this.processEvent("contextmenu", e);
47854     },
47855
47856     // private
47857     onDblClick : function(e){
47858         this.processEvent("dblclick", e);
47859     },
47860
47861     // private
47862     walkCells : function(row, col, step, fn, scope){
47863         var cm = this.colModel, clen = cm.getColumnCount();
47864         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47865         if(step < 0){
47866             if(col < 0){
47867                 row--;
47868                 first = false;
47869             }
47870             while(row >= 0){
47871                 if(!first){
47872                     col = clen-1;
47873                 }
47874                 first = false;
47875                 while(col >= 0){
47876                     if(fn.call(scope || this, row, col, cm) === true){
47877                         return [row, col];
47878                     }
47879                     col--;
47880                 }
47881                 row--;
47882             }
47883         } else {
47884             if(col >= clen){
47885                 row++;
47886                 first = false;
47887             }
47888             while(row < rlen){
47889                 if(!first){
47890                     col = 0;
47891                 }
47892                 first = false;
47893                 while(col < clen){
47894                     if(fn.call(scope || this, row, col, cm) === true){
47895                         return [row, col];
47896                     }
47897                     col++;
47898                 }
47899                 row++;
47900             }
47901         }
47902         return null;
47903     },
47904
47905     // private
47906     getSelections : function(){
47907         return this.selModel.getSelections();
47908     },
47909
47910     /**
47911      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47912      * but if manual update is required this method will initiate it.
47913      */
47914     autoSize : function(){
47915         if(this.rendered){
47916             this.view.layout();
47917             if(this.view.adjustForScroll){
47918                 this.view.adjustForScroll();
47919             }
47920         }
47921     },
47922
47923     /**
47924      * Returns the grid's underlying element.
47925      * @return {Element} The element
47926      */
47927     getGridEl : function(){
47928         return this.container;
47929     },
47930
47931     // private for compatibility, overridden by editor grid
47932     stopEditing : function(){},
47933
47934     /**
47935      * Returns the grid's SelectionModel.
47936      * @return {SelectionModel}
47937      */
47938     getSelectionModel : function(){
47939         if(!this.selModel){
47940             this.selModel = new Roo.grid.RowSelectionModel();
47941         }
47942         return this.selModel;
47943     },
47944
47945     /**
47946      * Returns the grid's DataSource.
47947      * @return {DataSource}
47948      */
47949     getDataSource : function(){
47950         return this.dataSource;
47951     },
47952
47953     /**
47954      * Returns the grid's ColumnModel.
47955      * @return {ColumnModel}
47956      */
47957     getColumnModel : function(){
47958         return this.colModel;
47959     },
47960
47961     /**
47962      * Returns the grid's GridView object.
47963      * @return {GridView}
47964      */
47965     getView : function(){
47966         if(!this.view){
47967             this.view = new Roo.grid.GridView(this.viewConfig);
47968         }
47969         return this.view;
47970     },
47971     /**
47972      * Called to get grid's drag proxy text, by default returns this.ddText.
47973      * @return {String}
47974      */
47975     getDragDropText : function(){
47976         var count = this.selModel.getCount();
47977         return String.format(this.ddText, count, count == 1 ? '' : 's');
47978     }
47979 });
47980 /**
47981  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47982  * %0 is replaced with the number of selected rows.
47983  * @type String
47984  */
47985 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47986  * Based on:
47987  * Ext JS Library 1.1.1
47988  * Copyright(c) 2006-2007, Ext JS, LLC.
47989  *
47990  * Originally Released Under LGPL - original licence link has changed is not relivant.
47991  *
47992  * Fork - LGPL
47993  * <script type="text/javascript">
47994  */
47995  
47996 Roo.grid.AbstractGridView = function(){
47997         this.grid = null;
47998         
47999         this.events = {
48000             "beforerowremoved" : true,
48001             "beforerowsinserted" : true,
48002             "beforerefresh" : true,
48003             "rowremoved" : true,
48004             "rowsinserted" : true,
48005             "rowupdated" : true,
48006             "refresh" : true
48007         };
48008     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48009 };
48010
48011 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48012     rowClass : "x-grid-row",
48013     cellClass : "x-grid-cell",
48014     tdClass : "x-grid-td",
48015     hdClass : "x-grid-hd",
48016     splitClass : "x-grid-hd-split",
48017     
48018         init: function(grid){
48019         this.grid = grid;
48020                 var cid = this.grid.getGridEl().id;
48021         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48022         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48023         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48024         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48025         },
48026         
48027         getColumnRenderers : function(){
48028         var renderers = [];
48029         var cm = this.grid.colModel;
48030         var colCount = cm.getColumnCount();
48031         for(var i = 0; i < colCount; i++){
48032             renderers[i] = cm.getRenderer(i);
48033         }
48034         return renderers;
48035     },
48036     
48037     getColumnIds : function(){
48038         var ids = [];
48039         var cm = this.grid.colModel;
48040         var colCount = cm.getColumnCount();
48041         for(var i = 0; i < colCount; i++){
48042             ids[i] = cm.getColumnId(i);
48043         }
48044         return ids;
48045     },
48046     
48047     getDataIndexes : function(){
48048         if(!this.indexMap){
48049             this.indexMap = this.buildIndexMap();
48050         }
48051         return this.indexMap.colToData;
48052     },
48053     
48054     getColumnIndexByDataIndex : function(dataIndex){
48055         if(!this.indexMap){
48056             this.indexMap = this.buildIndexMap();
48057         }
48058         return this.indexMap.dataToCol[dataIndex];
48059     },
48060     
48061     /**
48062      * Set a css style for a column dynamically. 
48063      * @param {Number} colIndex The index of the column
48064      * @param {String} name The css property name
48065      * @param {String} value The css value
48066      */
48067     setCSSStyle : function(colIndex, name, value){
48068         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48069         Roo.util.CSS.updateRule(selector, name, value);
48070     },
48071     
48072     generateRules : function(cm){
48073         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48074         Roo.util.CSS.removeStyleSheet(rulesId);
48075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48076             var cid = cm.getColumnId(i);
48077             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48078                          this.tdSelector, cid, " {\n}\n",
48079                          this.hdSelector, cid, " {\n}\n",
48080                          this.splitSelector, cid, " {\n}\n");
48081         }
48082         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48083     }
48084 });/*
48085  * Based on:
48086  * Ext JS Library 1.1.1
48087  * Copyright(c) 2006-2007, Ext JS, LLC.
48088  *
48089  * Originally Released Under LGPL - original licence link has changed is not relivant.
48090  *
48091  * Fork - LGPL
48092  * <script type="text/javascript">
48093  */
48094
48095 // private
48096 // This is a support class used internally by the Grid components
48097 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48098     this.grid = grid;
48099     this.view = grid.getView();
48100     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48101     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48102     if(hd2){
48103         this.setHandleElId(Roo.id(hd));
48104         this.setOuterHandleElId(Roo.id(hd2));
48105     }
48106     this.scroll = false;
48107 };
48108 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48109     maxDragWidth: 120,
48110     getDragData : function(e){
48111         var t = Roo.lib.Event.getTarget(e);
48112         var h = this.view.findHeaderCell(t);
48113         if(h){
48114             return {ddel: h.firstChild, header:h};
48115         }
48116         return false;
48117     },
48118
48119     onInitDrag : function(e){
48120         this.view.headersDisabled = true;
48121         var clone = this.dragData.ddel.cloneNode(true);
48122         clone.id = Roo.id();
48123         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48124         this.proxy.update(clone);
48125         return true;
48126     },
48127
48128     afterValidDrop : function(){
48129         var v = this.view;
48130         setTimeout(function(){
48131             v.headersDisabled = false;
48132         }, 50);
48133     },
48134
48135     afterInvalidDrop : function(){
48136         var v = this.view;
48137         setTimeout(function(){
48138             v.headersDisabled = false;
48139         }, 50);
48140     }
48141 });
48142 /*
48143  * Based on:
48144  * Ext JS Library 1.1.1
48145  * Copyright(c) 2006-2007, Ext JS, LLC.
48146  *
48147  * Originally Released Under LGPL - original licence link has changed is not relivant.
48148  *
48149  * Fork - LGPL
48150  * <script type="text/javascript">
48151  */
48152 // private
48153 // This is a support class used internally by the Grid components
48154 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48155     this.grid = grid;
48156     this.view = grid.getView();
48157     // split the proxies so they don't interfere with mouse events
48158     this.proxyTop = Roo.DomHelper.append(document.body, {
48159         cls:"col-move-top", html:"&#160;"
48160     }, true);
48161     this.proxyBottom = Roo.DomHelper.append(document.body, {
48162         cls:"col-move-bottom", html:"&#160;"
48163     }, true);
48164     this.proxyTop.hide = this.proxyBottom.hide = function(){
48165         this.setLeftTop(-100,-100);
48166         this.setStyle("visibility", "hidden");
48167     };
48168     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48169     // temporarily disabled
48170     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48171     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48172 };
48173 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48174     proxyOffsets : [-4, -9],
48175     fly: Roo.Element.fly,
48176
48177     getTargetFromEvent : function(e){
48178         var t = Roo.lib.Event.getTarget(e);
48179         var cindex = this.view.findCellIndex(t);
48180         if(cindex !== false){
48181             return this.view.getHeaderCell(cindex);
48182         }
48183         return null;
48184     },
48185
48186     nextVisible : function(h){
48187         var v = this.view, cm = this.grid.colModel;
48188         h = h.nextSibling;
48189         while(h){
48190             if(!cm.isHidden(v.getCellIndex(h))){
48191                 return h;
48192             }
48193             h = h.nextSibling;
48194         }
48195         return null;
48196     },
48197
48198     prevVisible : function(h){
48199         var v = this.view, cm = this.grid.colModel;
48200         h = h.prevSibling;
48201         while(h){
48202             if(!cm.isHidden(v.getCellIndex(h))){
48203                 return h;
48204             }
48205             h = h.prevSibling;
48206         }
48207         return null;
48208     },
48209
48210     positionIndicator : function(h, n, e){
48211         var x = Roo.lib.Event.getPageX(e);
48212         var r = Roo.lib.Dom.getRegion(n.firstChild);
48213         var px, pt, py = r.top + this.proxyOffsets[1];
48214         if((r.right - x) <= (r.right-r.left)/2){
48215             px = r.right+this.view.borderWidth;
48216             pt = "after";
48217         }else{
48218             px = r.left;
48219             pt = "before";
48220         }
48221         var oldIndex = this.view.getCellIndex(h);
48222         var newIndex = this.view.getCellIndex(n);
48223
48224         if(this.grid.colModel.isFixed(newIndex)){
48225             return false;
48226         }
48227
48228         var locked = this.grid.colModel.isLocked(newIndex);
48229
48230         if(pt == "after"){
48231             newIndex++;
48232         }
48233         if(oldIndex < newIndex){
48234             newIndex--;
48235         }
48236         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48237             return false;
48238         }
48239         px +=  this.proxyOffsets[0];
48240         this.proxyTop.setLeftTop(px, py);
48241         this.proxyTop.show();
48242         if(!this.bottomOffset){
48243             this.bottomOffset = this.view.mainHd.getHeight();
48244         }
48245         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48246         this.proxyBottom.show();
48247         return pt;
48248     },
48249
48250     onNodeEnter : function(n, dd, e, data){
48251         if(data.header != n){
48252             this.positionIndicator(data.header, n, e);
48253         }
48254     },
48255
48256     onNodeOver : function(n, dd, e, data){
48257         var result = false;
48258         if(data.header != n){
48259             result = this.positionIndicator(data.header, n, e);
48260         }
48261         if(!result){
48262             this.proxyTop.hide();
48263             this.proxyBottom.hide();
48264         }
48265         return result ? this.dropAllowed : this.dropNotAllowed;
48266     },
48267
48268     onNodeOut : function(n, dd, e, data){
48269         this.proxyTop.hide();
48270         this.proxyBottom.hide();
48271     },
48272
48273     onNodeDrop : function(n, dd, e, data){
48274         var h = data.header;
48275         if(h != n){
48276             var cm = this.grid.colModel;
48277             var x = Roo.lib.Event.getPageX(e);
48278             var r = Roo.lib.Dom.getRegion(n.firstChild);
48279             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48280             var oldIndex = this.view.getCellIndex(h);
48281             var newIndex = this.view.getCellIndex(n);
48282             var locked = cm.isLocked(newIndex);
48283             if(pt == "after"){
48284                 newIndex++;
48285             }
48286             if(oldIndex < newIndex){
48287                 newIndex--;
48288             }
48289             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48290                 return false;
48291             }
48292             cm.setLocked(oldIndex, locked, true);
48293             cm.moveColumn(oldIndex, newIndex);
48294             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48295             return true;
48296         }
48297         return false;
48298     }
48299 });
48300 /*
48301  * Based on:
48302  * Ext JS Library 1.1.1
48303  * Copyright(c) 2006-2007, Ext JS, LLC.
48304  *
48305  * Originally Released Under LGPL - original licence link has changed is not relivant.
48306  *
48307  * Fork - LGPL
48308  * <script type="text/javascript">
48309  */
48310   
48311 /**
48312  * @class Roo.grid.GridView
48313  * @extends Roo.util.Observable
48314  *
48315  * @constructor
48316  * @param {Object} config
48317  */
48318 Roo.grid.GridView = function(config){
48319     Roo.grid.GridView.superclass.constructor.call(this);
48320     this.el = null;
48321
48322     Roo.apply(this, config);
48323 };
48324
48325 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48326
48327     /**
48328      * Override this function to apply custom css classes to rows during rendering
48329      * @param {Record} record The record
48330      * @param {Number} index
48331      * @method getRowClass
48332      */
48333     rowClass : "x-grid-row",
48334
48335     cellClass : "x-grid-col",
48336
48337     tdClass : "x-grid-td",
48338
48339     hdClass : "x-grid-hd",
48340
48341     splitClass : "x-grid-split",
48342
48343     sortClasses : ["sort-asc", "sort-desc"],
48344
48345     enableMoveAnim : false,
48346
48347     hlColor: "C3DAF9",
48348
48349     dh : Roo.DomHelper,
48350
48351     fly : Roo.Element.fly,
48352
48353     css : Roo.util.CSS,
48354
48355     borderWidth: 1,
48356
48357     splitOffset: 3,
48358
48359     scrollIncrement : 22,
48360
48361     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48362
48363     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48364
48365     bind : function(ds, cm){
48366         if(this.ds){
48367             this.ds.un("load", this.onLoad, this);
48368             this.ds.un("datachanged", this.onDataChange, this);
48369             this.ds.un("add", this.onAdd, this);
48370             this.ds.un("remove", this.onRemove, this);
48371             this.ds.un("update", this.onUpdate, this);
48372             this.ds.un("clear", this.onClear, this);
48373         }
48374         if(ds){
48375             ds.on("load", this.onLoad, this);
48376             ds.on("datachanged", this.onDataChange, this);
48377             ds.on("add", this.onAdd, this);
48378             ds.on("remove", this.onRemove, this);
48379             ds.on("update", this.onUpdate, this);
48380             ds.on("clear", this.onClear, this);
48381         }
48382         this.ds = ds;
48383
48384         if(this.cm){
48385             this.cm.un("widthchange", this.onColWidthChange, this);
48386             this.cm.un("headerchange", this.onHeaderChange, this);
48387             this.cm.un("hiddenchange", this.onHiddenChange, this);
48388             this.cm.un("columnmoved", this.onColumnMove, this);
48389             this.cm.un("columnlockchange", this.onColumnLock, this);
48390         }
48391         if(cm){
48392             this.generateRules(cm);
48393             cm.on("widthchange", this.onColWidthChange, this);
48394             cm.on("headerchange", this.onHeaderChange, this);
48395             cm.on("hiddenchange", this.onHiddenChange, this);
48396             cm.on("columnmoved", this.onColumnMove, this);
48397             cm.on("columnlockchange", this.onColumnLock, this);
48398         }
48399         this.cm = cm;
48400     },
48401
48402     init: function(grid){
48403         Roo.grid.GridView.superclass.init.call(this, grid);
48404
48405         this.bind(grid.dataSource, grid.colModel);
48406
48407         grid.on("headerclick", this.handleHeaderClick, this);
48408
48409         if(grid.trackMouseOver){
48410             grid.on("mouseover", this.onRowOver, this);
48411             grid.on("mouseout", this.onRowOut, this);
48412         }
48413         grid.cancelTextSelection = function(){};
48414         this.gridId = grid.id;
48415
48416         var tpls = this.templates || {};
48417
48418         if(!tpls.master){
48419             tpls.master = new Roo.Template(
48420                '<div class="x-grid" hidefocus="true">',
48421                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48422                   '<div class="x-grid-topbar"></div>',
48423                   '<div class="x-grid-scroller"><div></div></div>',
48424                   '<div class="x-grid-locked">',
48425                       '<div class="x-grid-header">{lockedHeader}</div>',
48426                       '<div class="x-grid-body">{lockedBody}</div>',
48427                   "</div>",
48428                   '<div class="x-grid-viewport">',
48429                       '<div class="x-grid-header">{header}</div>',
48430                       '<div class="x-grid-body">{body}</div>',
48431                   "</div>",
48432                   '<div class="x-grid-bottombar"></div>',
48433                  
48434                   '<div class="x-grid-resize-proxy">&#160;</div>',
48435                "</div>"
48436             );
48437             tpls.master.disableformats = true;
48438         }
48439
48440         if(!tpls.header){
48441             tpls.header = new Roo.Template(
48442                '<table border="0" cellspacing="0" cellpadding="0">',
48443                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48444                "</table>{splits}"
48445             );
48446             tpls.header.disableformats = true;
48447         }
48448         tpls.header.compile();
48449
48450         if(!tpls.hcell){
48451             tpls.hcell = new Roo.Template(
48452                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48453                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48454                 "</div></td>"
48455              );
48456              tpls.hcell.disableFormats = true;
48457         }
48458         tpls.hcell.compile();
48459
48460         if(!tpls.hsplit){
48461             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48462             tpls.hsplit.disableFormats = true;
48463         }
48464         tpls.hsplit.compile();
48465
48466         if(!tpls.body){
48467             tpls.body = new Roo.Template(
48468                '<table border="0" cellspacing="0" cellpadding="0">',
48469                "<tbody>{rows}</tbody>",
48470                "</table>"
48471             );
48472             tpls.body.disableFormats = true;
48473         }
48474         tpls.body.compile();
48475
48476         if(!tpls.row){
48477             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48478             tpls.row.disableFormats = true;
48479         }
48480         tpls.row.compile();
48481
48482         if(!tpls.cell){
48483             tpls.cell = new Roo.Template(
48484                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48485                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48486                 "</td>"
48487             );
48488             tpls.cell.disableFormats = true;
48489         }
48490         tpls.cell.compile();
48491
48492         this.templates = tpls;
48493     },
48494
48495     // remap these for backwards compat
48496     onColWidthChange : function(){
48497         this.updateColumns.apply(this, arguments);
48498     },
48499     onHeaderChange : function(){
48500         this.updateHeaders.apply(this, arguments);
48501     }, 
48502     onHiddenChange : function(){
48503         this.handleHiddenChange.apply(this, arguments);
48504     },
48505     onColumnMove : function(){
48506         this.handleColumnMove.apply(this, arguments);
48507     },
48508     onColumnLock : function(){
48509         this.handleLockChange.apply(this, arguments);
48510     },
48511
48512     onDataChange : function(){
48513         this.refresh();
48514         this.updateHeaderSortState();
48515     },
48516
48517     onClear : function(){
48518         this.refresh();
48519     },
48520
48521     onUpdate : function(ds, record){
48522         this.refreshRow(record);
48523     },
48524
48525     refreshRow : function(record){
48526         var ds = this.ds, index;
48527         if(typeof record == 'number'){
48528             index = record;
48529             record = ds.getAt(index);
48530         }else{
48531             index = ds.indexOf(record);
48532         }
48533         this.insertRows(ds, index, index, true);
48534         this.onRemove(ds, record, index+1, true);
48535         this.syncRowHeights(index, index);
48536         this.layout();
48537         this.fireEvent("rowupdated", this, index, record);
48538     },
48539
48540     onAdd : function(ds, records, index){
48541         this.insertRows(ds, index, index + (records.length-1));
48542     },
48543
48544     onRemove : function(ds, record, index, isUpdate){
48545         if(isUpdate !== true){
48546             this.fireEvent("beforerowremoved", this, index, record);
48547         }
48548         var bt = this.getBodyTable(), lt = this.getLockedTable();
48549         if(bt.rows[index]){
48550             bt.firstChild.removeChild(bt.rows[index]);
48551         }
48552         if(lt.rows[index]){
48553             lt.firstChild.removeChild(lt.rows[index]);
48554         }
48555         if(isUpdate !== true){
48556             this.stripeRows(index);
48557             this.syncRowHeights(index, index);
48558             this.layout();
48559             this.fireEvent("rowremoved", this, index, record);
48560         }
48561     },
48562
48563     onLoad : function(){
48564         this.scrollToTop();
48565     },
48566
48567     /**
48568      * Scrolls the grid to the top
48569      */
48570     scrollToTop : function(){
48571         if(this.scroller){
48572             this.scroller.dom.scrollTop = 0;
48573             this.syncScroll();
48574         }
48575     },
48576
48577     /**
48578      * Gets a panel in the header of the grid that can be used for toolbars etc.
48579      * After modifying the contents of this panel a call to grid.autoSize() may be
48580      * required to register any changes in size.
48581      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48582      * @return Roo.Element
48583      */
48584     getHeaderPanel : function(doShow){
48585         if(doShow){
48586             this.headerPanel.show();
48587         }
48588         return this.headerPanel;
48589     },
48590
48591     /**
48592      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48593      * After modifying the contents of this panel a call to grid.autoSize() may be
48594      * required to register any changes in size.
48595      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48596      * @return Roo.Element
48597      */
48598     getFooterPanel : function(doShow){
48599         if(doShow){
48600             this.footerPanel.show();
48601         }
48602         return this.footerPanel;
48603     },
48604
48605     initElements : function(){
48606         var E = Roo.Element;
48607         var el = this.grid.getGridEl().dom.firstChild;
48608         var cs = el.childNodes;
48609
48610         this.el = new E(el);
48611         
48612          this.focusEl = new E(el.firstChild);
48613         this.focusEl.swallowEvent("click", true);
48614         
48615         this.headerPanel = new E(cs[1]);
48616         this.headerPanel.enableDisplayMode("block");
48617
48618         this.scroller = new E(cs[2]);
48619         this.scrollSizer = new E(this.scroller.dom.firstChild);
48620
48621         this.lockedWrap = new E(cs[3]);
48622         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48623         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48624
48625         this.mainWrap = new E(cs[4]);
48626         this.mainHd = new E(this.mainWrap.dom.firstChild);
48627         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48628
48629         this.footerPanel = new E(cs[5]);
48630         this.footerPanel.enableDisplayMode("block");
48631
48632         this.resizeProxy = new E(cs[6]);
48633
48634         this.headerSelector = String.format(
48635            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48636            this.lockedHd.id, this.mainHd.id
48637         );
48638
48639         this.splitterSelector = String.format(
48640            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48641            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48642         );
48643     },
48644     idToCssName : function(s)
48645     {
48646         return s.replace(/[^a-z0-9]+/ig, '-');
48647     },
48648
48649     getHeaderCell : function(index){
48650         return Roo.DomQuery.select(this.headerSelector)[index];
48651     },
48652
48653     getHeaderCellMeasure : function(index){
48654         return this.getHeaderCell(index).firstChild;
48655     },
48656
48657     getHeaderCellText : function(index){
48658         return this.getHeaderCell(index).firstChild.firstChild;
48659     },
48660
48661     getLockedTable : function(){
48662         return this.lockedBody.dom.firstChild;
48663     },
48664
48665     getBodyTable : function(){
48666         return this.mainBody.dom.firstChild;
48667     },
48668
48669     getLockedRow : function(index){
48670         return this.getLockedTable().rows[index];
48671     },
48672
48673     getRow : function(index){
48674         return this.getBodyTable().rows[index];
48675     },
48676
48677     getRowComposite : function(index){
48678         if(!this.rowEl){
48679             this.rowEl = new Roo.CompositeElementLite();
48680         }
48681         var els = [], lrow, mrow;
48682         if(lrow = this.getLockedRow(index)){
48683             els.push(lrow);
48684         }
48685         if(mrow = this.getRow(index)){
48686             els.push(mrow);
48687         }
48688         this.rowEl.elements = els;
48689         return this.rowEl;
48690     },
48691     /**
48692      * Gets the 'td' of the cell
48693      * 
48694      * @param {Integer} rowIndex row to select
48695      * @param {Integer} colIndex column to select
48696      * 
48697      * @return {Object} 
48698      */
48699     getCell : function(rowIndex, colIndex){
48700         var locked = this.cm.getLockedCount();
48701         var source;
48702         if(colIndex < locked){
48703             source = this.lockedBody.dom.firstChild;
48704         }else{
48705             source = this.mainBody.dom.firstChild;
48706             colIndex -= locked;
48707         }
48708         return source.rows[rowIndex].childNodes[colIndex];
48709     },
48710
48711     getCellText : function(rowIndex, colIndex){
48712         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48713     },
48714
48715     getCellBox : function(cell){
48716         var b = this.fly(cell).getBox();
48717         if(Roo.isOpera){ // opera fails to report the Y
48718             b.y = cell.offsetTop + this.mainBody.getY();
48719         }
48720         return b;
48721     },
48722
48723     getCellIndex : function(cell){
48724         var id = String(cell.className).match(this.cellRE);
48725         if(id){
48726             return parseInt(id[1], 10);
48727         }
48728         return 0;
48729     },
48730
48731     findHeaderIndex : function(n){
48732         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48733         return r ? this.getCellIndex(r) : false;
48734     },
48735
48736     findHeaderCell : function(n){
48737         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48738         return r ? r : false;
48739     },
48740
48741     findRowIndex : function(n){
48742         if(!n){
48743             return false;
48744         }
48745         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48746         return r ? r.rowIndex : false;
48747     },
48748
48749     findCellIndex : function(node){
48750         var stop = this.el.dom;
48751         while(node && node != stop){
48752             if(this.findRE.test(node.className)){
48753                 return this.getCellIndex(node);
48754             }
48755             node = node.parentNode;
48756         }
48757         return false;
48758     },
48759
48760     getColumnId : function(index){
48761         return this.cm.getColumnId(index);
48762     },
48763
48764     getSplitters : function()
48765     {
48766         if(this.splitterSelector){
48767            return Roo.DomQuery.select(this.splitterSelector);
48768         }else{
48769             return null;
48770       }
48771     },
48772
48773     getSplitter : function(index){
48774         return this.getSplitters()[index];
48775     },
48776
48777     onRowOver : function(e, t){
48778         var row;
48779         if((row = this.findRowIndex(t)) !== false){
48780             this.getRowComposite(row).addClass("x-grid-row-over");
48781         }
48782     },
48783
48784     onRowOut : function(e, t){
48785         var row;
48786         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48787             this.getRowComposite(row).removeClass("x-grid-row-over");
48788         }
48789     },
48790
48791     renderHeaders : function(){
48792         var cm = this.cm;
48793         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48794         var cb = [], lb = [], sb = [], lsb = [], p = {};
48795         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48796             p.cellId = "x-grid-hd-0-" + i;
48797             p.splitId = "x-grid-csplit-0-" + i;
48798             p.id = cm.getColumnId(i);
48799             p.title = cm.getColumnTooltip(i) || "";
48800             p.value = cm.getColumnHeader(i) || "";
48801             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48802             if(!cm.isLocked(i)){
48803                 cb[cb.length] = ct.apply(p);
48804                 sb[sb.length] = st.apply(p);
48805             }else{
48806                 lb[lb.length] = ct.apply(p);
48807                 lsb[lsb.length] = st.apply(p);
48808             }
48809         }
48810         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48811                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48812     },
48813
48814     updateHeaders : function(){
48815         var html = this.renderHeaders();
48816         this.lockedHd.update(html[0]);
48817         this.mainHd.update(html[1]);
48818     },
48819
48820     /**
48821      * Focuses the specified row.
48822      * @param {Number} row The row index
48823      */
48824     focusRow : function(row)
48825     {
48826         //Roo.log('GridView.focusRow');
48827         var x = this.scroller.dom.scrollLeft;
48828         this.focusCell(row, 0, false);
48829         this.scroller.dom.scrollLeft = x;
48830     },
48831
48832     /**
48833      * Focuses the specified cell.
48834      * @param {Number} row The row index
48835      * @param {Number} col The column index
48836      * @param {Boolean} hscroll false to disable horizontal scrolling
48837      */
48838     focusCell : function(row, col, hscroll)
48839     {
48840         //Roo.log('GridView.focusCell');
48841         var el = this.ensureVisible(row, col, hscroll);
48842         this.focusEl.alignTo(el, "tl-tl");
48843         if(Roo.isGecko){
48844             this.focusEl.focus();
48845         }else{
48846             this.focusEl.focus.defer(1, this.focusEl);
48847         }
48848     },
48849
48850     /**
48851      * Scrolls the specified cell into view
48852      * @param {Number} row The row index
48853      * @param {Number} col The column index
48854      * @param {Boolean} hscroll false to disable horizontal scrolling
48855      */
48856     ensureVisible : function(row, col, hscroll)
48857     {
48858         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48859         //return null; //disable for testing.
48860         if(typeof row != "number"){
48861             row = row.rowIndex;
48862         }
48863         if(row < 0 && row >= this.ds.getCount()){
48864             return  null;
48865         }
48866         col = (col !== undefined ? col : 0);
48867         var cm = this.grid.colModel;
48868         while(cm.isHidden(col)){
48869             col++;
48870         }
48871
48872         var el = this.getCell(row, col);
48873         if(!el){
48874             return null;
48875         }
48876         var c = this.scroller.dom;
48877
48878         var ctop = parseInt(el.offsetTop, 10);
48879         var cleft = parseInt(el.offsetLeft, 10);
48880         var cbot = ctop + el.offsetHeight;
48881         var cright = cleft + el.offsetWidth;
48882         
48883         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48884         var stop = parseInt(c.scrollTop, 10);
48885         var sleft = parseInt(c.scrollLeft, 10);
48886         var sbot = stop + ch;
48887         var sright = sleft + c.clientWidth;
48888         /*
48889         Roo.log('GridView.ensureVisible:' +
48890                 ' ctop:' + ctop +
48891                 ' c.clientHeight:' + c.clientHeight +
48892                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48893                 ' stop:' + stop +
48894                 ' cbot:' + cbot +
48895                 ' sbot:' + sbot +
48896                 ' ch:' + ch  
48897                 );
48898         */
48899         if(ctop < stop){
48900              c.scrollTop = ctop;
48901             //Roo.log("set scrolltop to ctop DISABLE?");
48902         }else if(cbot > sbot){
48903             //Roo.log("set scrolltop to cbot-ch");
48904             c.scrollTop = cbot-ch;
48905         }
48906         
48907         if(hscroll !== false){
48908             if(cleft < sleft){
48909                 c.scrollLeft = cleft;
48910             }else if(cright > sright){
48911                 c.scrollLeft = cright-c.clientWidth;
48912             }
48913         }
48914          
48915         return el;
48916     },
48917
48918     updateColumns : function(){
48919         this.grid.stopEditing();
48920         var cm = this.grid.colModel, colIds = this.getColumnIds();
48921         //var totalWidth = cm.getTotalWidth();
48922         var pos = 0;
48923         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48924             //if(cm.isHidden(i)) continue;
48925             var w = cm.getColumnWidth(i);
48926             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48927             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48928         }
48929         this.updateSplitters();
48930     },
48931
48932     generateRules : function(cm){
48933         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48934         Roo.util.CSS.removeStyleSheet(rulesId);
48935         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48936             var cid = cm.getColumnId(i);
48937             var align = '';
48938             if(cm.config[i].align){
48939                 align = 'text-align:'+cm.config[i].align+';';
48940             }
48941             var hidden = '';
48942             if(cm.isHidden(i)){
48943                 hidden = 'display:none;';
48944             }
48945             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48946             ruleBuf.push(
48947                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48948                     this.hdSelector, cid, " {\n", align, width, "}\n",
48949                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48950                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48951         }
48952         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48953     },
48954
48955     updateSplitters : function(){
48956         var cm = this.cm, s = this.getSplitters();
48957         if(s){ // splitters not created yet
48958             var pos = 0, locked = true;
48959             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48960                 if(cm.isHidden(i)) continue;
48961                 var w = cm.getColumnWidth(i); // make sure it's a number
48962                 if(!cm.isLocked(i) && locked){
48963                     pos = 0;
48964                     locked = false;
48965                 }
48966                 pos += w;
48967                 s[i].style.left = (pos-this.splitOffset) + "px";
48968             }
48969         }
48970     },
48971
48972     handleHiddenChange : function(colModel, colIndex, hidden){
48973         if(hidden){
48974             this.hideColumn(colIndex);
48975         }else{
48976             this.unhideColumn(colIndex);
48977         }
48978     },
48979
48980     hideColumn : function(colIndex){
48981         var cid = this.getColumnId(colIndex);
48982         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48983         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48984         if(Roo.isSafari){
48985             this.updateHeaders();
48986         }
48987         this.updateSplitters();
48988         this.layout();
48989     },
48990
48991     unhideColumn : function(colIndex){
48992         var cid = this.getColumnId(colIndex);
48993         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48994         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48995
48996         if(Roo.isSafari){
48997             this.updateHeaders();
48998         }
48999         this.updateSplitters();
49000         this.layout();
49001     },
49002
49003     insertRows : function(dm, firstRow, lastRow, isUpdate){
49004         if(firstRow == 0 && lastRow == dm.getCount()-1){
49005             this.refresh();
49006         }else{
49007             if(!isUpdate){
49008                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49009             }
49010             var s = this.getScrollState();
49011             var markup = this.renderRows(firstRow, lastRow);
49012             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49013             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49014             this.restoreScroll(s);
49015             if(!isUpdate){
49016                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49017                 this.syncRowHeights(firstRow, lastRow);
49018                 this.stripeRows(firstRow);
49019                 this.layout();
49020             }
49021         }
49022     },
49023
49024     bufferRows : function(markup, target, index){
49025         var before = null, trows = target.rows, tbody = target.tBodies[0];
49026         if(index < trows.length){
49027             before = trows[index];
49028         }
49029         var b = document.createElement("div");
49030         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49031         var rows = b.firstChild.rows;
49032         for(var i = 0, len = rows.length; i < len; i++){
49033             if(before){
49034                 tbody.insertBefore(rows[0], before);
49035             }else{
49036                 tbody.appendChild(rows[0]);
49037             }
49038         }
49039         b.innerHTML = "";
49040         b = null;
49041     },
49042
49043     deleteRows : function(dm, firstRow, lastRow){
49044         if(dm.getRowCount()<1){
49045             this.fireEvent("beforerefresh", this);
49046             this.mainBody.update("");
49047             this.lockedBody.update("");
49048             this.fireEvent("refresh", this);
49049         }else{
49050             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49051             var bt = this.getBodyTable();
49052             var tbody = bt.firstChild;
49053             var rows = bt.rows;
49054             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49055                 tbody.removeChild(rows[firstRow]);
49056             }
49057             this.stripeRows(firstRow);
49058             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49059         }
49060     },
49061
49062     updateRows : function(dataSource, firstRow, lastRow){
49063         var s = this.getScrollState();
49064         this.refresh();
49065         this.restoreScroll(s);
49066     },
49067
49068     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49069         if(!noRefresh){
49070            this.refresh();
49071         }
49072         this.updateHeaderSortState();
49073     },
49074
49075     getScrollState : function(){
49076         
49077         var sb = this.scroller.dom;
49078         return {left: sb.scrollLeft, top: sb.scrollTop};
49079     },
49080
49081     stripeRows : function(startRow){
49082         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49083             return;
49084         }
49085         startRow = startRow || 0;
49086         var rows = this.getBodyTable().rows;
49087         var lrows = this.getLockedTable().rows;
49088         var cls = ' x-grid-row-alt ';
49089         for(var i = startRow, len = rows.length; i < len; i++){
49090             var row = rows[i], lrow = lrows[i];
49091             var isAlt = ((i+1) % 2 == 0);
49092             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49093             if(isAlt == hasAlt){
49094                 continue;
49095             }
49096             if(isAlt){
49097                 row.className += " x-grid-row-alt";
49098             }else{
49099                 row.className = row.className.replace("x-grid-row-alt", "");
49100             }
49101             if(lrow){
49102                 lrow.className = row.className;
49103             }
49104         }
49105     },
49106
49107     restoreScroll : function(state){
49108         //Roo.log('GridView.restoreScroll');
49109         var sb = this.scroller.dom;
49110         sb.scrollLeft = state.left;
49111         sb.scrollTop = state.top;
49112         this.syncScroll();
49113     },
49114
49115     syncScroll : function(){
49116         //Roo.log('GridView.syncScroll');
49117         var sb = this.scroller.dom;
49118         var sh = this.mainHd.dom;
49119         var bs = this.mainBody.dom;
49120         var lv = this.lockedBody.dom;
49121         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49122         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49123     },
49124
49125     handleScroll : function(e){
49126         this.syncScroll();
49127         var sb = this.scroller.dom;
49128         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49129         e.stopEvent();
49130     },
49131
49132     handleWheel : function(e){
49133         var d = e.getWheelDelta();
49134         this.scroller.dom.scrollTop -= d*22;
49135         // set this here to prevent jumpy scrolling on large tables
49136         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49137         e.stopEvent();
49138     },
49139
49140     renderRows : function(startRow, endRow){
49141         // pull in all the crap needed to render rows
49142         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49143         var colCount = cm.getColumnCount();
49144
49145         if(ds.getCount() < 1){
49146             return ["", ""];
49147         }
49148
49149         // build a map for all the columns
49150         var cs = [];
49151         for(var i = 0; i < colCount; i++){
49152             var name = cm.getDataIndex(i);
49153             cs[i] = {
49154                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49155                 renderer : cm.getRenderer(i),
49156                 id : cm.getColumnId(i),
49157                 locked : cm.isLocked(i)
49158             };
49159         }
49160
49161         startRow = startRow || 0;
49162         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49163
49164         // records to render
49165         var rs = ds.getRange(startRow, endRow);
49166
49167         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49168     },
49169
49170     // As much as I hate to duplicate code, this was branched because FireFox really hates
49171     // [].join("") on strings. The performance difference was substantial enough to
49172     // branch this function
49173     doRender : Roo.isGecko ?
49174             function(cs, rs, ds, startRow, colCount, stripe){
49175                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49176                 // buffers
49177                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49178                 
49179                 var hasListener = this.grid.hasListener('rowclass');
49180                 var rowcfg = {};
49181                 for(var j = 0, len = rs.length; j < len; j++){
49182                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49183                     for(var i = 0; i < colCount; i++){
49184                         c = cs[i];
49185                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49186                         p.id = c.id;
49187                         p.css = p.attr = "";
49188                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49189                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49190                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49191                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49192                         }
49193                         var markup = ct.apply(p);
49194                         if(!c.locked){
49195                             cb+= markup;
49196                         }else{
49197                             lcb+= markup;
49198                         }
49199                     }
49200                     var alt = [];
49201                     if(stripe && ((rowIndex+1) % 2 == 0)){
49202                         alt.push("x-grid-row-alt")
49203                     }
49204                     if(r.dirty){
49205                         alt.push(  " x-grid-dirty-row");
49206                     }
49207                     rp.cells = lcb;
49208                     if(this.getRowClass){
49209                         alt.push(this.getRowClass(r, rowIndex));
49210                     }
49211                     if (hasListener) {
49212                         rowcfg = {
49213                              
49214                             record: r,
49215                             rowIndex : rowIndex,
49216                             rowClass : ''
49217                         }
49218                         this.grid.fireEvent('rowclass', this, rowcfg);
49219                         alt.push(rowcfg.rowClass);
49220                     }
49221                     rp.alt = alt.join(" ");
49222                     lbuf+= rt.apply(rp);
49223                     rp.cells = cb;
49224                     buf+=  rt.apply(rp);
49225                 }
49226                 return [lbuf, buf];
49227             } :
49228             function(cs, rs, ds, startRow, colCount, stripe){
49229                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49230                 // buffers
49231                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49232                 var hasListener = this.grid.hasListener('rowclass');
49233                 var rowcfg = {};
49234                 for(var j = 0, len = rs.length; j < len; j++){
49235                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49236                     for(var i = 0; i < colCount; i++){
49237                         c = cs[i];
49238                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49239                         p.id = c.id;
49240                         p.css = p.attr = "";
49241                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49242                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49243                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49244                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49245                         }
49246                         var markup = ct.apply(p);
49247                         if(!c.locked){
49248                             cb[cb.length] = markup;
49249                         }else{
49250                             lcb[lcb.length] = markup;
49251                         }
49252                     }
49253                     var alt = [];
49254                     if(stripe && ((rowIndex+1) % 2 == 0)){
49255                         alt.push( "x-grid-row-alt");
49256                     }
49257                     if(r.dirty){
49258                         alt.push(" x-grid-dirty-row");
49259                     }
49260                     rp.cells = lcb;
49261                     if(this.getRowClass){
49262                         alt.push( this.getRowClass(r, rowIndex));
49263                     }
49264                     if (hasListener) {
49265                         rowcfg = {
49266                              
49267                             record: r,
49268                             rowIndex : rowIndex,
49269                             rowClass : ''
49270                         }
49271                         this.grid.fireEvent('rowclass', this, rowcfg);
49272                         alt.push(rowcfg.rowClass);
49273                     }
49274                     rp.alt = alt.join(" ");
49275                     rp.cells = lcb.join("");
49276                     lbuf[lbuf.length] = rt.apply(rp);
49277                     rp.cells = cb.join("");
49278                     buf[buf.length] =  rt.apply(rp);
49279                 }
49280                 return [lbuf.join(""), buf.join("")];
49281             },
49282
49283     renderBody : function(){
49284         var markup = this.renderRows();
49285         var bt = this.templates.body;
49286         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49287     },
49288
49289     /**
49290      * Refreshes the grid
49291      * @param {Boolean} headersToo
49292      */
49293     refresh : function(headersToo){
49294         this.fireEvent("beforerefresh", this);
49295         this.grid.stopEditing();
49296         var result = this.renderBody();
49297         this.lockedBody.update(result[0]);
49298         this.mainBody.update(result[1]);
49299         if(headersToo === true){
49300             this.updateHeaders();
49301             this.updateColumns();
49302             this.updateSplitters();
49303             this.updateHeaderSortState();
49304         }
49305         this.syncRowHeights();
49306         this.layout();
49307         this.fireEvent("refresh", this);
49308     },
49309
49310     handleColumnMove : function(cm, oldIndex, newIndex){
49311         this.indexMap = null;
49312         var s = this.getScrollState();
49313         this.refresh(true);
49314         this.restoreScroll(s);
49315         this.afterMove(newIndex);
49316     },
49317
49318     afterMove : function(colIndex){
49319         if(this.enableMoveAnim && Roo.enableFx){
49320             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49321         }
49322         // if multisort - fix sortOrder, and reload..
49323         if (this.grid.dataSource.multiSort) {
49324             // the we can call sort again..
49325             var dm = this.grid.dataSource;
49326             var cm = this.grid.colModel;
49327             var so = [];
49328             for(var i = 0; i < cm.config.length; i++ ) {
49329                 
49330                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49331                     continue; // dont' bother, it's not in sort list or being set.
49332                 }
49333                 
49334                 so.push(cm.config[i].dataIndex);
49335             };
49336             dm.sortOrder = so;
49337             dm.load(dm.lastOptions);
49338             
49339             
49340         }
49341         
49342     },
49343
49344     updateCell : function(dm, rowIndex, dataIndex){
49345         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49346         if(typeof colIndex == "undefined"){ // not present in grid
49347             return;
49348         }
49349         var cm = this.grid.colModel;
49350         var cell = this.getCell(rowIndex, colIndex);
49351         var cellText = this.getCellText(rowIndex, colIndex);
49352
49353         var p = {
49354             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49355             id : cm.getColumnId(colIndex),
49356             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49357         };
49358         var renderer = cm.getRenderer(colIndex);
49359         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49360         if(typeof val == "undefined" || val === "") val = "&#160;";
49361         cellText.innerHTML = val;
49362         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49363         this.syncRowHeights(rowIndex, rowIndex);
49364     },
49365
49366     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49367         var maxWidth = 0;
49368         if(this.grid.autoSizeHeaders){
49369             var h = this.getHeaderCellMeasure(colIndex);
49370             maxWidth = Math.max(maxWidth, h.scrollWidth);
49371         }
49372         var tb, index;
49373         if(this.cm.isLocked(colIndex)){
49374             tb = this.getLockedTable();
49375             index = colIndex;
49376         }else{
49377             tb = this.getBodyTable();
49378             index = colIndex - this.cm.getLockedCount();
49379         }
49380         if(tb && tb.rows){
49381             var rows = tb.rows;
49382             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49383             for(var i = 0; i < stopIndex; i++){
49384                 var cell = rows[i].childNodes[index].firstChild;
49385                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49386             }
49387         }
49388         return maxWidth + /*margin for error in IE*/ 5;
49389     },
49390     /**
49391      * Autofit a column to its content.
49392      * @param {Number} colIndex
49393      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49394      */
49395      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49396          if(this.cm.isHidden(colIndex)){
49397              return; // can't calc a hidden column
49398          }
49399         if(forceMinSize){
49400             var cid = this.cm.getColumnId(colIndex);
49401             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49402            if(this.grid.autoSizeHeaders){
49403                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49404            }
49405         }
49406         var newWidth = this.calcColumnWidth(colIndex);
49407         this.cm.setColumnWidth(colIndex,
49408             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49409         if(!suppressEvent){
49410             this.grid.fireEvent("columnresize", colIndex, newWidth);
49411         }
49412     },
49413
49414     /**
49415      * Autofits all columns to their content and then expands to fit any extra space in the grid
49416      */
49417      autoSizeColumns : function(){
49418         var cm = this.grid.colModel;
49419         var colCount = cm.getColumnCount();
49420         for(var i = 0; i < colCount; i++){
49421             this.autoSizeColumn(i, true, true);
49422         }
49423         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49424             this.fitColumns();
49425         }else{
49426             this.updateColumns();
49427             this.layout();
49428         }
49429     },
49430
49431     /**
49432      * Autofits all columns to the grid's width proportionate with their current size
49433      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49434      */
49435     fitColumns : function(reserveScrollSpace){
49436         var cm = this.grid.colModel;
49437         var colCount = cm.getColumnCount();
49438         var cols = [];
49439         var width = 0;
49440         var i, w;
49441         for (i = 0; i < colCount; i++){
49442             if(!cm.isHidden(i) && !cm.isFixed(i)){
49443                 w = cm.getColumnWidth(i);
49444                 cols.push(i);
49445                 cols.push(w);
49446                 width += w;
49447             }
49448         }
49449         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49450         if(reserveScrollSpace){
49451             avail -= 17;
49452         }
49453         var frac = (avail - cm.getTotalWidth())/width;
49454         while (cols.length){
49455             w = cols.pop();
49456             i = cols.pop();
49457             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49458         }
49459         this.updateColumns();
49460         this.layout();
49461     },
49462
49463     onRowSelect : function(rowIndex){
49464         var row = this.getRowComposite(rowIndex);
49465         row.addClass("x-grid-row-selected");
49466     },
49467
49468     onRowDeselect : function(rowIndex){
49469         var row = this.getRowComposite(rowIndex);
49470         row.removeClass("x-grid-row-selected");
49471     },
49472
49473     onCellSelect : function(row, col){
49474         var cell = this.getCell(row, col);
49475         if(cell){
49476             Roo.fly(cell).addClass("x-grid-cell-selected");
49477         }
49478     },
49479
49480     onCellDeselect : function(row, col){
49481         var cell = this.getCell(row, col);
49482         if(cell){
49483             Roo.fly(cell).removeClass("x-grid-cell-selected");
49484         }
49485     },
49486
49487     updateHeaderSortState : function(){
49488         
49489         // sort state can be single { field: xxx, direction : yyy}
49490         // or   { xxx=>ASC , yyy : DESC ..... }
49491         
49492         var mstate = {};
49493         if (!this.ds.multiSort) { 
49494             var state = this.ds.getSortState();
49495             if(!state){
49496                 return;
49497             }
49498             mstate[state.field] = state.direction;
49499             // FIXME... - this is not used here.. but might be elsewhere..
49500             this.sortState = state;
49501             
49502         } else {
49503             mstate = this.ds.sortToggle;
49504         }
49505         //remove existing sort classes..
49506         
49507         var sc = this.sortClasses;
49508         var hds = this.el.select(this.headerSelector).removeClass(sc);
49509         
49510         for(var f in mstate) {
49511         
49512             var sortColumn = this.cm.findColumnIndex(f);
49513             
49514             if(sortColumn != -1){
49515                 var sortDir = mstate[f];        
49516                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49517             }
49518         }
49519         
49520          
49521         
49522     },
49523
49524
49525     handleHeaderClick : function(g, index){
49526         if(this.headersDisabled){
49527             return;
49528         }
49529         var dm = g.dataSource, cm = g.colModel;
49530         if(!cm.isSortable(index)){
49531             return;
49532         }
49533         g.stopEditing();
49534         
49535         if (dm.multiSort) {
49536             // update the sortOrder
49537             var so = [];
49538             for(var i = 0; i < cm.config.length; i++ ) {
49539                 
49540                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49541                     continue; // dont' bother, it's not in sort list or being set.
49542                 }
49543                 
49544                 so.push(cm.config[i].dataIndex);
49545             };
49546             dm.sortOrder = so;
49547         }
49548         
49549         
49550         dm.sort(cm.getDataIndex(index));
49551     },
49552
49553
49554     destroy : function(){
49555         if(this.colMenu){
49556             this.colMenu.removeAll();
49557             Roo.menu.MenuMgr.unregister(this.colMenu);
49558             this.colMenu.getEl().remove();
49559             delete this.colMenu;
49560         }
49561         if(this.hmenu){
49562             this.hmenu.removeAll();
49563             Roo.menu.MenuMgr.unregister(this.hmenu);
49564             this.hmenu.getEl().remove();
49565             delete this.hmenu;
49566         }
49567         if(this.grid.enableColumnMove){
49568             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49569             if(dds){
49570                 for(var dd in dds){
49571                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49572                         var elid = dds[dd].dragElId;
49573                         dds[dd].unreg();
49574                         Roo.get(elid).remove();
49575                     } else if(dds[dd].config.isTarget){
49576                         dds[dd].proxyTop.remove();
49577                         dds[dd].proxyBottom.remove();
49578                         dds[dd].unreg();
49579                     }
49580                     if(Roo.dd.DDM.locationCache[dd]){
49581                         delete Roo.dd.DDM.locationCache[dd];
49582                     }
49583                 }
49584                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49585             }
49586         }
49587         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49588         this.bind(null, null);
49589         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49590     },
49591
49592     handleLockChange : function(){
49593         this.refresh(true);
49594     },
49595
49596     onDenyColumnLock : function(){
49597
49598     },
49599
49600     onDenyColumnHide : function(){
49601
49602     },
49603
49604     handleHdMenuClick : function(item){
49605         var index = this.hdCtxIndex;
49606         var cm = this.cm, ds = this.ds;
49607         switch(item.id){
49608             case "asc":
49609                 ds.sort(cm.getDataIndex(index), "ASC");
49610                 break;
49611             case "desc":
49612                 ds.sort(cm.getDataIndex(index), "DESC");
49613                 break;
49614             case "lock":
49615                 var lc = cm.getLockedCount();
49616                 if(cm.getColumnCount(true) <= lc+1){
49617                     this.onDenyColumnLock();
49618                     return;
49619                 }
49620                 if(lc != index){
49621                     cm.setLocked(index, true, true);
49622                     cm.moveColumn(index, lc);
49623                     this.grid.fireEvent("columnmove", index, lc);
49624                 }else{
49625                     cm.setLocked(index, true);
49626                 }
49627             break;
49628             case "unlock":
49629                 var lc = cm.getLockedCount();
49630                 if((lc-1) != index){
49631                     cm.setLocked(index, false, true);
49632                     cm.moveColumn(index, lc-1);
49633                     this.grid.fireEvent("columnmove", index, lc-1);
49634                 }else{
49635                     cm.setLocked(index, false);
49636                 }
49637             break;
49638             default:
49639                 index = cm.getIndexById(item.id.substr(4));
49640                 if(index != -1){
49641                     if(item.checked && cm.getColumnCount(true) <= 1){
49642                         this.onDenyColumnHide();
49643                         return false;
49644                     }
49645                     cm.setHidden(index, item.checked);
49646                 }
49647         }
49648         return true;
49649     },
49650
49651     beforeColMenuShow : function(){
49652         var cm = this.cm,  colCount = cm.getColumnCount();
49653         this.colMenu.removeAll();
49654         for(var i = 0; i < colCount; i++){
49655             this.colMenu.add(new Roo.menu.CheckItem({
49656                 id: "col-"+cm.getColumnId(i),
49657                 text: cm.getColumnHeader(i),
49658                 checked: !cm.isHidden(i),
49659                 hideOnClick:false
49660             }));
49661         }
49662     },
49663
49664     handleHdCtx : function(g, index, e){
49665         e.stopEvent();
49666         var hd = this.getHeaderCell(index);
49667         this.hdCtxIndex = index;
49668         var ms = this.hmenu.items, cm = this.cm;
49669         ms.get("asc").setDisabled(!cm.isSortable(index));
49670         ms.get("desc").setDisabled(!cm.isSortable(index));
49671         if(this.grid.enableColLock !== false){
49672             ms.get("lock").setDisabled(cm.isLocked(index));
49673             ms.get("unlock").setDisabled(!cm.isLocked(index));
49674         }
49675         this.hmenu.show(hd, "tl-bl");
49676     },
49677
49678     handleHdOver : function(e){
49679         var hd = this.findHeaderCell(e.getTarget());
49680         if(hd && !this.headersDisabled){
49681             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49682                this.fly(hd).addClass("x-grid-hd-over");
49683             }
49684         }
49685     },
49686
49687     handleHdOut : function(e){
49688         var hd = this.findHeaderCell(e.getTarget());
49689         if(hd){
49690             this.fly(hd).removeClass("x-grid-hd-over");
49691         }
49692     },
49693
49694     handleSplitDblClick : function(e, t){
49695         var i = this.getCellIndex(t);
49696         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49697             this.autoSizeColumn(i, true);
49698             this.layout();
49699         }
49700     },
49701
49702     render : function(){
49703
49704         var cm = this.cm;
49705         var colCount = cm.getColumnCount();
49706
49707         if(this.grid.monitorWindowResize === true){
49708             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49709         }
49710         var header = this.renderHeaders();
49711         var body = this.templates.body.apply({rows:""});
49712         var html = this.templates.master.apply({
49713             lockedBody: body,
49714             body: body,
49715             lockedHeader: header[0],
49716             header: header[1]
49717         });
49718
49719         //this.updateColumns();
49720
49721         this.grid.getGridEl().dom.innerHTML = html;
49722
49723         this.initElements();
49724         
49725         // a kludge to fix the random scolling effect in webkit
49726         this.el.on("scroll", function() {
49727             this.el.dom.scrollTop=0; // hopefully not recursive..
49728         },this);
49729
49730         this.scroller.on("scroll", this.handleScroll, this);
49731         this.lockedBody.on("mousewheel", this.handleWheel, this);
49732         this.mainBody.on("mousewheel", this.handleWheel, this);
49733
49734         this.mainHd.on("mouseover", this.handleHdOver, this);
49735         this.mainHd.on("mouseout", this.handleHdOut, this);
49736         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49737                 {delegate: "."+this.splitClass});
49738
49739         this.lockedHd.on("mouseover", this.handleHdOver, this);
49740         this.lockedHd.on("mouseout", this.handleHdOut, this);
49741         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49742                 {delegate: "."+this.splitClass});
49743
49744         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49745             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49746         }
49747
49748         this.updateSplitters();
49749
49750         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49751             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49752             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49753         }
49754
49755         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49756             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49757             this.hmenu.add(
49758                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49759                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49760             );
49761             if(this.grid.enableColLock !== false){
49762                 this.hmenu.add('-',
49763                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49764                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49765                 );
49766             }
49767             if(this.grid.enableColumnHide !== false){
49768
49769                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49770                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49771                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49772
49773                 this.hmenu.add('-',
49774                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49775                 );
49776             }
49777             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49778
49779             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49780         }
49781
49782         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49783             this.dd = new Roo.grid.GridDragZone(this.grid, {
49784                 ddGroup : this.grid.ddGroup || 'GridDD'
49785             });
49786         }
49787
49788         /*
49789         for(var i = 0; i < colCount; i++){
49790             if(cm.isHidden(i)){
49791                 this.hideColumn(i);
49792             }
49793             if(cm.config[i].align){
49794                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49795                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49796             }
49797         }*/
49798         
49799         this.updateHeaderSortState();
49800
49801         this.beforeInitialResize();
49802         this.layout(true);
49803
49804         // two part rendering gives faster view to the user
49805         this.renderPhase2.defer(1, this);
49806     },
49807
49808     renderPhase2 : function(){
49809         // render the rows now
49810         this.refresh();
49811         if(this.grid.autoSizeColumns){
49812             this.autoSizeColumns();
49813         }
49814     },
49815
49816     beforeInitialResize : function(){
49817
49818     },
49819
49820     onColumnSplitterMoved : function(i, w){
49821         this.userResized = true;
49822         var cm = this.grid.colModel;
49823         cm.setColumnWidth(i, w, true);
49824         var cid = cm.getColumnId(i);
49825         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49826         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49827         this.updateSplitters();
49828         this.layout();
49829         this.grid.fireEvent("columnresize", i, w);
49830     },
49831
49832     syncRowHeights : function(startIndex, endIndex){
49833         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49834             startIndex = startIndex || 0;
49835             var mrows = this.getBodyTable().rows;
49836             var lrows = this.getLockedTable().rows;
49837             var len = mrows.length-1;
49838             endIndex = Math.min(endIndex || len, len);
49839             for(var i = startIndex; i <= endIndex; i++){
49840                 var m = mrows[i], l = lrows[i];
49841                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49842                 m.style.height = l.style.height = h + "px";
49843             }
49844         }
49845     },
49846
49847     layout : function(initialRender, is2ndPass){
49848         var g = this.grid;
49849         var auto = g.autoHeight;
49850         var scrollOffset = 16;
49851         var c = g.getGridEl(), cm = this.cm,
49852                 expandCol = g.autoExpandColumn,
49853                 gv = this;
49854         //c.beginMeasure();
49855
49856         if(!c.dom.offsetWidth){ // display:none?
49857             if(initialRender){
49858                 this.lockedWrap.show();
49859                 this.mainWrap.show();
49860             }
49861             return;
49862         }
49863
49864         var hasLock = this.cm.isLocked(0);
49865
49866         var tbh = this.headerPanel.getHeight();
49867         var bbh = this.footerPanel.getHeight();
49868
49869         if(auto){
49870             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49871             var newHeight = ch + c.getBorderWidth("tb");
49872             if(g.maxHeight){
49873                 newHeight = Math.min(g.maxHeight, newHeight);
49874             }
49875             c.setHeight(newHeight);
49876         }
49877
49878         if(g.autoWidth){
49879             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49880         }
49881
49882         var s = this.scroller;
49883
49884         var csize = c.getSize(true);
49885
49886         this.el.setSize(csize.width, csize.height);
49887
49888         this.headerPanel.setWidth(csize.width);
49889         this.footerPanel.setWidth(csize.width);
49890
49891         var hdHeight = this.mainHd.getHeight();
49892         var vw = csize.width;
49893         var vh = csize.height - (tbh + bbh);
49894
49895         s.setSize(vw, vh);
49896
49897         var bt = this.getBodyTable();
49898         var ltWidth = hasLock ?
49899                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49900
49901         var scrollHeight = bt.offsetHeight;
49902         var scrollWidth = ltWidth + bt.offsetWidth;
49903         var vscroll = false, hscroll = false;
49904
49905         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49906
49907         var lw = this.lockedWrap, mw = this.mainWrap;
49908         var lb = this.lockedBody, mb = this.mainBody;
49909
49910         setTimeout(function(){
49911             var t = s.dom.offsetTop;
49912             var w = s.dom.clientWidth,
49913                 h = s.dom.clientHeight;
49914
49915             lw.setTop(t);
49916             lw.setSize(ltWidth, h);
49917
49918             mw.setLeftTop(ltWidth, t);
49919             mw.setSize(w-ltWidth, h);
49920
49921             lb.setHeight(h-hdHeight);
49922             mb.setHeight(h-hdHeight);
49923
49924             if(is2ndPass !== true && !gv.userResized && expandCol){
49925                 // high speed resize without full column calculation
49926                 
49927                 var ci = cm.getIndexById(expandCol);
49928                 if (ci < 0) {
49929                     ci = cm.findColumnIndex(expandCol);
49930                 }
49931                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49932                 var expandId = cm.getColumnId(ci);
49933                 var  tw = cm.getTotalWidth(false);
49934                 var currentWidth = cm.getColumnWidth(ci);
49935                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49936                 if(currentWidth != cw){
49937                     cm.setColumnWidth(ci, cw, true);
49938                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49939                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49940                     gv.updateSplitters();
49941                     gv.layout(false, true);
49942                 }
49943             }
49944
49945             if(initialRender){
49946                 lw.show();
49947                 mw.show();
49948             }
49949             //c.endMeasure();
49950         }, 10);
49951     },
49952
49953     onWindowResize : function(){
49954         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49955             return;
49956         }
49957         this.layout();
49958     },
49959
49960     appendFooter : function(parentEl){
49961         return null;
49962     },
49963
49964     sortAscText : "Sort Ascending",
49965     sortDescText : "Sort Descending",
49966     lockText : "Lock Column",
49967     unlockText : "Unlock Column",
49968     columnsText : "Columns"
49969 });
49970
49971
49972 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49973     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49974     this.proxy.el.addClass('x-grid3-col-dd');
49975 };
49976
49977 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49978     handleMouseDown : function(e){
49979
49980     },
49981
49982     callHandleMouseDown : function(e){
49983         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49984     }
49985 });
49986 /*
49987  * Based on:
49988  * Ext JS Library 1.1.1
49989  * Copyright(c) 2006-2007, Ext JS, LLC.
49990  *
49991  * Originally Released Under LGPL - original licence link has changed is not relivant.
49992  *
49993  * Fork - LGPL
49994  * <script type="text/javascript">
49995  */
49996  
49997 // private
49998 // This is a support class used internally by the Grid components
49999 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50000     this.grid = grid;
50001     this.view = grid.getView();
50002     this.proxy = this.view.resizeProxy;
50003     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50004         "gridSplitters" + this.grid.getGridEl().id, {
50005         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50006     });
50007     this.setHandleElId(Roo.id(hd));
50008     this.setOuterHandleElId(Roo.id(hd2));
50009     this.scroll = false;
50010 };
50011 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50012     fly: Roo.Element.fly,
50013
50014     b4StartDrag : function(x, y){
50015         this.view.headersDisabled = true;
50016         this.proxy.setHeight(this.view.mainWrap.getHeight());
50017         var w = this.cm.getColumnWidth(this.cellIndex);
50018         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50019         this.resetConstraints();
50020         this.setXConstraint(minw, 1000);
50021         this.setYConstraint(0, 0);
50022         this.minX = x - minw;
50023         this.maxX = x + 1000;
50024         this.startPos = x;
50025         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50026     },
50027
50028
50029     handleMouseDown : function(e){
50030         ev = Roo.EventObject.setEvent(e);
50031         var t = this.fly(ev.getTarget());
50032         if(t.hasClass("x-grid-split")){
50033             this.cellIndex = this.view.getCellIndex(t.dom);
50034             this.split = t.dom;
50035             this.cm = this.grid.colModel;
50036             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50037                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50038             }
50039         }
50040     },
50041
50042     endDrag : function(e){
50043         this.view.headersDisabled = false;
50044         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50045         var diff = endX - this.startPos;
50046         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50047     },
50048
50049     autoOffset : function(){
50050         this.setDelta(0,0);
50051     }
50052 });/*
50053  * Based on:
50054  * Ext JS Library 1.1.1
50055  * Copyright(c) 2006-2007, Ext JS, LLC.
50056  *
50057  * Originally Released Under LGPL - original licence link has changed is not relivant.
50058  *
50059  * Fork - LGPL
50060  * <script type="text/javascript">
50061  */
50062  
50063 // private
50064 // This is a support class used internally by the Grid components
50065 Roo.grid.GridDragZone = function(grid, config){
50066     this.view = grid.getView();
50067     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50068     if(this.view.lockedBody){
50069         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50070         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50071     }
50072     this.scroll = false;
50073     this.grid = grid;
50074     this.ddel = document.createElement('div');
50075     this.ddel.className = 'x-grid-dd-wrap';
50076 };
50077
50078 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50079     ddGroup : "GridDD",
50080
50081     getDragData : function(e){
50082         var t = Roo.lib.Event.getTarget(e);
50083         var rowIndex = this.view.findRowIndex(t);
50084         if(rowIndex !== false){
50085             var sm = this.grid.selModel;
50086             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50087               //  sm.mouseDown(e, t);
50088             //}
50089             if (e.hasModifier()){
50090                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50091             }
50092             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50093         }
50094         return false;
50095     },
50096
50097     onInitDrag : function(e){
50098         var data = this.dragData;
50099         this.ddel.innerHTML = this.grid.getDragDropText();
50100         this.proxy.update(this.ddel);
50101         // fire start drag?
50102     },
50103
50104     afterRepair : function(){
50105         this.dragging = false;
50106     },
50107
50108     getRepairXY : function(e, data){
50109         return false;
50110     },
50111
50112     onEndDrag : function(data, e){
50113         // fire end drag?
50114     },
50115
50116     onValidDrop : function(dd, e, id){
50117         // fire drag drop?
50118         this.hideProxy();
50119     },
50120
50121     beforeInvalidDrop : function(e, id){
50122
50123     }
50124 });/*
50125  * Based on:
50126  * Ext JS Library 1.1.1
50127  * Copyright(c) 2006-2007, Ext JS, LLC.
50128  *
50129  * Originally Released Under LGPL - original licence link has changed is not relivant.
50130  *
50131  * Fork - LGPL
50132  * <script type="text/javascript">
50133  */
50134  
50135
50136 /**
50137  * @class Roo.grid.ColumnModel
50138  * @extends Roo.util.Observable
50139  * This is the default implementation of a ColumnModel used by the Grid. It defines
50140  * the columns in the grid.
50141  * <br>Usage:<br>
50142  <pre><code>
50143  var colModel = new Roo.grid.ColumnModel([
50144         {header: "Ticker", width: 60, sortable: true, locked: true},
50145         {header: "Company Name", width: 150, sortable: true},
50146         {header: "Market Cap.", width: 100, sortable: true},
50147         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50148         {header: "Employees", width: 100, sortable: true, resizable: false}
50149  ]);
50150  </code></pre>
50151  * <p>
50152  
50153  * The config options listed for this class are options which may appear in each
50154  * individual column definition.
50155  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50156  * @constructor
50157  * @param {Object} config An Array of column config objects. See this class's
50158  * config objects for details.
50159 */
50160 Roo.grid.ColumnModel = function(config){
50161         /**
50162      * The config passed into the constructor
50163      */
50164     this.config = config;
50165     this.lookup = {};
50166
50167     // if no id, create one
50168     // if the column does not have a dataIndex mapping,
50169     // map it to the order it is in the config
50170     for(var i = 0, len = config.length; i < len; i++){
50171         var c = config[i];
50172         if(typeof c.dataIndex == "undefined"){
50173             c.dataIndex = i;
50174         }
50175         if(typeof c.renderer == "string"){
50176             c.renderer = Roo.util.Format[c.renderer];
50177         }
50178         if(typeof c.id == "undefined"){
50179             c.id = Roo.id();
50180         }
50181         if(c.editor && c.editor.xtype){
50182             c.editor  = Roo.factory(c.editor, Roo.grid);
50183         }
50184         if(c.editor && c.editor.isFormField){
50185             c.editor = new Roo.grid.GridEditor(c.editor);
50186         }
50187         this.lookup[c.id] = c;
50188     }
50189
50190     /**
50191      * The width of columns which have no width specified (defaults to 100)
50192      * @type Number
50193      */
50194     this.defaultWidth = 100;
50195
50196     /**
50197      * Default sortable of columns which have no sortable specified (defaults to false)
50198      * @type Boolean
50199      */
50200     this.defaultSortable = false;
50201
50202     this.addEvents({
50203         /**
50204              * @event widthchange
50205              * Fires when the width of a column changes.
50206              * @param {ColumnModel} this
50207              * @param {Number} columnIndex The column index
50208              * @param {Number} newWidth The new width
50209              */
50210             "widthchange": true,
50211         /**
50212              * @event headerchange
50213              * Fires when the text of a header changes.
50214              * @param {ColumnModel} this
50215              * @param {Number} columnIndex The column index
50216              * @param {Number} newText The new header text
50217              */
50218             "headerchange": true,
50219         /**
50220              * @event hiddenchange
50221              * Fires when a column is hidden or "unhidden".
50222              * @param {ColumnModel} this
50223              * @param {Number} columnIndex The column index
50224              * @param {Boolean} hidden true if hidden, false otherwise
50225              */
50226             "hiddenchange": true,
50227             /**
50228          * @event columnmoved
50229          * Fires when a column is moved.
50230          * @param {ColumnModel} this
50231          * @param {Number} oldIndex
50232          * @param {Number} newIndex
50233          */
50234         "columnmoved" : true,
50235         /**
50236          * @event columlockchange
50237          * Fires when a column's locked state is changed
50238          * @param {ColumnModel} this
50239          * @param {Number} colIndex
50240          * @param {Boolean} locked true if locked
50241          */
50242         "columnlockchange" : true
50243     });
50244     Roo.grid.ColumnModel.superclass.constructor.call(this);
50245 };
50246 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50247     /**
50248      * @cfg {String} header The header text to display in the Grid view.
50249      */
50250     /**
50251      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50252      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50253      * specified, the column's index is used as an index into the Record's data Array.
50254      */
50255     /**
50256      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50257      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50258      */
50259     /**
50260      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50261      * Defaults to the value of the {@link #defaultSortable} property.
50262      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50263      */
50264     /**
50265      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50266      */
50267     /**
50268      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50269      */
50270     /**
50271      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50272      */
50273     /**
50274      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50275      */
50276     /**
50277      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50278      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50279      * default renderer uses the raw data value.
50280      */
50281        /**
50282      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50283      */
50284     /**
50285      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50286      */
50287
50288     /**
50289      * Returns the id of the column at the specified index.
50290      * @param {Number} index The column index
50291      * @return {String} the id
50292      */
50293     getColumnId : function(index){
50294         return this.config[index].id;
50295     },
50296
50297     /**
50298      * Returns the column for a specified id.
50299      * @param {String} id The column id
50300      * @return {Object} the column
50301      */
50302     getColumnById : function(id){
50303         return this.lookup[id];
50304     },
50305
50306     
50307     /**
50308      * Returns the column for a specified dataIndex.
50309      * @param {String} dataIndex The column dataIndex
50310      * @return {Object|Boolean} the column or false if not found
50311      */
50312     getColumnByDataIndex: function(dataIndex){
50313         var index = this.findColumnIndex(dataIndex);
50314         return index > -1 ? this.config[index] : false;
50315     },
50316     
50317     /**
50318      * Returns the index for a specified column id.
50319      * @param {String} id The column id
50320      * @return {Number} the index, or -1 if not found
50321      */
50322     getIndexById : function(id){
50323         for(var i = 0, len = this.config.length; i < len; i++){
50324             if(this.config[i].id == id){
50325                 return i;
50326             }
50327         }
50328         return -1;
50329     },
50330     
50331     /**
50332      * Returns the index for a specified column dataIndex.
50333      * @param {String} dataIndex The column dataIndex
50334      * @return {Number} the index, or -1 if not found
50335      */
50336     
50337     findColumnIndex : function(dataIndex){
50338         for(var i = 0, len = this.config.length; i < len; i++){
50339             if(this.config[i].dataIndex == dataIndex){
50340                 return i;
50341             }
50342         }
50343         return -1;
50344     },
50345     
50346     
50347     moveColumn : function(oldIndex, newIndex){
50348         var c = this.config[oldIndex];
50349         this.config.splice(oldIndex, 1);
50350         this.config.splice(newIndex, 0, c);
50351         this.dataMap = null;
50352         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50353     },
50354
50355     isLocked : function(colIndex){
50356         return this.config[colIndex].locked === true;
50357     },
50358
50359     setLocked : function(colIndex, value, suppressEvent){
50360         if(this.isLocked(colIndex) == value){
50361             return;
50362         }
50363         this.config[colIndex].locked = value;
50364         if(!suppressEvent){
50365             this.fireEvent("columnlockchange", this, colIndex, value);
50366         }
50367     },
50368
50369     getTotalLockedWidth : function(){
50370         var totalWidth = 0;
50371         for(var i = 0; i < this.config.length; i++){
50372             if(this.isLocked(i) && !this.isHidden(i)){
50373                 this.totalWidth += this.getColumnWidth(i);
50374             }
50375         }
50376         return totalWidth;
50377     },
50378
50379     getLockedCount : function(){
50380         for(var i = 0, len = this.config.length; i < len; i++){
50381             if(!this.isLocked(i)){
50382                 return i;
50383             }
50384         }
50385     },
50386
50387     /**
50388      * Returns the number of columns.
50389      * @return {Number}
50390      */
50391     getColumnCount : function(visibleOnly){
50392         if(visibleOnly === true){
50393             var c = 0;
50394             for(var i = 0, len = this.config.length; i < len; i++){
50395                 if(!this.isHidden(i)){
50396                     c++;
50397                 }
50398             }
50399             return c;
50400         }
50401         return this.config.length;
50402     },
50403
50404     /**
50405      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50406      * @param {Function} fn
50407      * @param {Object} scope (optional)
50408      * @return {Array} result
50409      */
50410     getColumnsBy : function(fn, scope){
50411         var r = [];
50412         for(var i = 0, len = this.config.length; i < len; i++){
50413             var c = this.config[i];
50414             if(fn.call(scope||this, c, i) === true){
50415                 r[r.length] = c;
50416             }
50417         }
50418         return r;
50419     },
50420
50421     /**
50422      * Returns true if the specified column is sortable.
50423      * @param {Number} col The column index
50424      * @return {Boolean}
50425      */
50426     isSortable : function(col){
50427         if(typeof this.config[col].sortable == "undefined"){
50428             return this.defaultSortable;
50429         }
50430         return this.config[col].sortable;
50431     },
50432
50433     /**
50434      * Returns the rendering (formatting) function defined for the column.
50435      * @param {Number} col The column index.
50436      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50437      */
50438     getRenderer : function(col){
50439         if(!this.config[col].renderer){
50440             return Roo.grid.ColumnModel.defaultRenderer;
50441         }
50442         return this.config[col].renderer;
50443     },
50444
50445     /**
50446      * Sets the rendering (formatting) function for a column.
50447      * @param {Number} col The column index
50448      * @param {Function} fn The function to use to process the cell's raw data
50449      * to return HTML markup for the grid view. The render function is called with
50450      * the following parameters:<ul>
50451      * <li>Data value.</li>
50452      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50453      * <li>css A CSS style string to apply to the table cell.</li>
50454      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50455      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50456      * <li>Row index</li>
50457      * <li>Column index</li>
50458      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50459      */
50460     setRenderer : function(col, fn){
50461         this.config[col].renderer = fn;
50462     },
50463
50464     /**
50465      * Returns the width for the specified column.
50466      * @param {Number} col The column index
50467      * @return {Number}
50468      */
50469     getColumnWidth : function(col){
50470         return this.config[col].width * 1 || this.defaultWidth;
50471     },
50472
50473     /**
50474      * Sets the width for a column.
50475      * @param {Number} col The column index
50476      * @param {Number} width The new width
50477      */
50478     setColumnWidth : function(col, width, suppressEvent){
50479         this.config[col].width = width;
50480         this.totalWidth = null;
50481         if(!suppressEvent){
50482              this.fireEvent("widthchange", this, col, width);
50483         }
50484     },
50485
50486     /**
50487      * Returns the total width of all columns.
50488      * @param {Boolean} includeHidden True to include hidden column widths
50489      * @return {Number}
50490      */
50491     getTotalWidth : function(includeHidden){
50492         if(!this.totalWidth){
50493             this.totalWidth = 0;
50494             for(var i = 0, len = this.config.length; i < len; i++){
50495                 if(includeHidden || !this.isHidden(i)){
50496                     this.totalWidth += this.getColumnWidth(i);
50497                 }
50498             }
50499         }
50500         return this.totalWidth;
50501     },
50502
50503     /**
50504      * Returns the header for the specified column.
50505      * @param {Number} col The column index
50506      * @return {String}
50507      */
50508     getColumnHeader : function(col){
50509         return this.config[col].header;
50510     },
50511
50512     /**
50513      * Sets the header for a column.
50514      * @param {Number} col The column index
50515      * @param {String} header The new header
50516      */
50517     setColumnHeader : function(col, header){
50518         this.config[col].header = header;
50519         this.fireEvent("headerchange", this, col, header);
50520     },
50521
50522     /**
50523      * Returns the tooltip for the specified column.
50524      * @param {Number} col The column index
50525      * @return {String}
50526      */
50527     getColumnTooltip : function(col){
50528             return this.config[col].tooltip;
50529     },
50530     /**
50531      * Sets the tooltip for a column.
50532      * @param {Number} col The column index
50533      * @param {String} tooltip The new tooltip
50534      */
50535     setColumnTooltip : function(col, tooltip){
50536             this.config[col].tooltip = tooltip;
50537     },
50538
50539     /**
50540      * Returns the dataIndex for the specified column.
50541      * @param {Number} col The column index
50542      * @return {Number}
50543      */
50544     getDataIndex : function(col){
50545         return this.config[col].dataIndex;
50546     },
50547
50548     /**
50549      * Sets the dataIndex for a column.
50550      * @param {Number} col The column index
50551      * @param {Number} dataIndex The new dataIndex
50552      */
50553     setDataIndex : function(col, dataIndex){
50554         this.config[col].dataIndex = dataIndex;
50555     },
50556
50557     
50558     
50559     /**
50560      * Returns true if the cell is editable.
50561      * @param {Number} colIndex The column index
50562      * @param {Number} rowIndex The row index
50563      * @return {Boolean}
50564      */
50565     isCellEditable : function(colIndex, rowIndex){
50566         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50567     },
50568
50569     /**
50570      * Returns the editor defined for the cell/column.
50571      * return false or null to disable editing.
50572      * @param {Number} colIndex The column index
50573      * @param {Number} rowIndex The row index
50574      * @return {Object}
50575      */
50576     getCellEditor : function(colIndex, rowIndex){
50577         return this.config[colIndex].editor;
50578     },
50579
50580     /**
50581      * Sets if a column is editable.
50582      * @param {Number} col The column index
50583      * @param {Boolean} editable True if the column is editable
50584      */
50585     setEditable : function(col, editable){
50586         this.config[col].editable = editable;
50587     },
50588
50589
50590     /**
50591      * Returns true if the column is hidden.
50592      * @param {Number} colIndex The column index
50593      * @return {Boolean}
50594      */
50595     isHidden : function(colIndex){
50596         return this.config[colIndex].hidden;
50597     },
50598
50599
50600     /**
50601      * Returns true if the column width cannot be changed
50602      */
50603     isFixed : function(colIndex){
50604         return this.config[colIndex].fixed;
50605     },
50606
50607     /**
50608      * Returns true if the column can be resized
50609      * @return {Boolean}
50610      */
50611     isResizable : function(colIndex){
50612         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50613     },
50614     /**
50615      * Sets if a column is hidden.
50616      * @param {Number} colIndex The column index
50617      * @param {Boolean} hidden True if the column is hidden
50618      */
50619     setHidden : function(colIndex, hidden){
50620         this.config[colIndex].hidden = hidden;
50621         this.totalWidth = null;
50622         this.fireEvent("hiddenchange", this, colIndex, hidden);
50623     },
50624
50625     /**
50626      * Sets the editor for a column.
50627      * @param {Number} col The column index
50628      * @param {Object} editor The editor object
50629      */
50630     setEditor : function(col, editor){
50631         this.config[col].editor = editor;
50632     }
50633 });
50634
50635 Roo.grid.ColumnModel.defaultRenderer = function(value){
50636         if(typeof value == "string" && value.length < 1){
50637             return "&#160;";
50638         }
50639         return value;
50640 };
50641
50642 // Alias for backwards compatibility
50643 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50644 /*
50645  * Based on:
50646  * Ext JS Library 1.1.1
50647  * Copyright(c) 2006-2007, Ext JS, LLC.
50648  *
50649  * Originally Released Under LGPL - original licence link has changed is not relivant.
50650  *
50651  * Fork - LGPL
50652  * <script type="text/javascript">
50653  */
50654
50655 /**
50656  * @class Roo.grid.AbstractSelectionModel
50657  * @extends Roo.util.Observable
50658  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50659  * implemented by descendant classes.  This class should not be directly instantiated.
50660  * @constructor
50661  */
50662 Roo.grid.AbstractSelectionModel = function(){
50663     this.locked = false;
50664     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50665 };
50666
50667 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50668     /** @ignore Called by the grid automatically. Do not call directly. */
50669     init : function(grid){
50670         this.grid = grid;
50671         this.initEvents();
50672     },
50673
50674     /**
50675      * Locks the selections.
50676      */
50677     lock : function(){
50678         this.locked = true;
50679     },
50680
50681     /**
50682      * Unlocks the selections.
50683      */
50684     unlock : function(){
50685         this.locked = false;
50686     },
50687
50688     /**
50689      * Returns true if the selections are locked.
50690      * @return {Boolean}
50691      */
50692     isLocked : function(){
50693         return this.locked;
50694     }
50695 });/*
50696  * Based on:
50697  * Ext JS Library 1.1.1
50698  * Copyright(c) 2006-2007, Ext JS, LLC.
50699  *
50700  * Originally Released Under LGPL - original licence link has changed is not relivant.
50701  *
50702  * Fork - LGPL
50703  * <script type="text/javascript">
50704  */
50705 /**
50706  * @extends Roo.grid.AbstractSelectionModel
50707  * @class Roo.grid.RowSelectionModel
50708  * The default SelectionModel used by {@link Roo.grid.Grid}.
50709  * It supports multiple selections and keyboard selection/navigation. 
50710  * @constructor
50711  * @param {Object} config
50712  */
50713 Roo.grid.RowSelectionModel = function(config){
50714     Roo.apply(this, config);
50715     this.selections = new Roo.util.MixedCollection(false, function(o){
50716         return o.id;
50717     });
50718
50719     this.last = false;
50720     this.lastActive = false;
50721
50722     this.addEvents({
50723         /**
50724              * @event selectionchange
50725              * Fires when the selection changes
50726              * @param {SelectionModel} this
50727              */
50728             "selectionchange" : true,
50729         /**
50730              * @event afterselectionchange
50731              * Fires after the selection changes (eg. by key press or clicking)
50732              * @param {SelectionModel} this
50733              */
50734             "afterselectionchange" : true,
50735         /**
50736              * @event beforerowselect
50737              * Fires when a row is selected being selected, return false to cancel.
50738              * @param {SelectionModel} this
50739              * @param {Number} rowIndex The selected index
50740              * @param {Boolean} keepExisting False if other selections will be cleared
50741              */
50742             "beforerowselect" : true,
50743         /**
50744              * @event rowselect
50745              * Fires when a row is selected.
50746              * @param {SelectionModel} this
50747              * @param {Number} rowIndex The selected index
50748              * @param {Roo.data.Record} r The record
50749              */
50750             "rowselect" : true,
50751         /**
50752              * @event rowdeselect
50753              * Fires when a row is deselected.
50754              * @param {SelectionModel} this
50755              * @param {Number} rowIndex The selected index
50756              */
50757         "rowdeselect" : true
50758     });
50759     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50760     this.locked = false;
50761 };
50762
50763 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50764     /**
50765      * @cfg {Boolean} singleSelect
50766      * True to allow selection of only one row at a time (defaults to false)
50767      */
50768     singleSelect : false,
50769
50770     // private
50771     initEvents : function(){
50772
50773         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50774             this.grid.on("mousedown", this.handleMouseDown, this);
50775         }else{ // allow click to work like normal
50776             this.grid.on("rowclick", this.handleDragableRowClick, this);
50777         }
50778
50779         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50780             "up" : function(e){
50781                 if(!e.shiftKey){
50782                     this.selectPrevious(e.shiftKey);
50783                 }else if(this.last !== false && this.lastActive !== false){
50784                     var last = this.last;
50785                     this.selectRange(this.last,  this.lastActive-1);
50786                     this.grid.getView().focusRow(this.lastActive);
50787                     if(last !== false){
50788                         this.last = last;
50789                     }
50790                 }else{
50791                     this.selectFirstRow();
50792                 }
50793                 this.fireEvent("afterselectionchange", this);
50794             },
50795             "down" : function(e){
50796                 if(!e.shiftKey){
50797                     this.selectNext(e.shiftKey);
50798                 }else if(this.last !== false && this.lastActive !== false){
50799                     var last = this.last;
50800                     this.selectRange(this.last,  this.lastActive+1);
50801                     this.grid.getView().focusRow(this.lastActive);
50802                     if(last !== false){
50803                         this.last = last;
50804                     }
50805                 }else{
50806                     this.selectFirstRow();
50807                 }
50808                 this.fireEvent("afterselectionchange", this);
50809             },
50810             scope: this
50811         });
50812
50813         var view = this.grid.view;
50814         view.on("refresh", this.onRefresh, this);
50815         view.on("rowupdated", this.onRowUpdated, this);
50816         view.on("rowremoved", this.onRemove, this);
50817     },
50818
50819     // private
50820     onRefresh : function(){
50821         var ds = this.grid.dataSource, i, v = this.grid.view;
50822         var s = this.selections;
50823         s.each(function(r){
50824             if((i = ds.indexOfId(r.id)) != -1){
50825                 v.onRowSelect(i);
50826             }else{
50827                 s.remove(r);
50828             }
50829         });
50830     },
50831
50832     // private
50833     onRemove : function(v, index, r){
50834         this.selections.remove(r);
50835     },
50836
50837     // private
50838     onRowUpdated : function(v, index, r){
50839         if(this.isSelected(r)){
50840             v.onRowSelect(index);
50841         }
50842     },
50843
50844     /**
50845      * Select records.
50846      * @param {Array} records The records to select
50847      * @param {Boolean} keepExisting (optional) True to keep existing selections
50848      */
50849     selectRecords : function(records, keepExisting){
50850         if(!keepExisting){
50851             this.clearSelections();
50852         }
50853         var ds = this.grid.dataSource;
50854         for(var i = 0, len = records.length; i < len; i++){
50855             this.selectRow(ds.indexOf(records[i]), true);
50856         }
50857     },
50858
50859     /**
50860      * Gets the number of selected rows.
50861      * @return {Number}
50862      */
50863     getCount : function(){
50864         return this.selections.length;
50865     },
50866
50867     /**
50868      * Selects the first row in the grid.
50869      */
50870     selectFirstRow : function(){
50871         this.selectRow(0);
50872     },
50873
50874     /**
50875      * Select the last row.
50876      * @param {Boolean} keepExisting (optional) True to keep existing selections
50877      */
50878     selectLastRow : function(keepExisting){
50879         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50880     },
50881
50882     /**
50883      * Selects the row immediately following the last selected row.
50884      * @param {Boolean} keepExisting (optional) True to keep existing selections
50885      */
50886     selectNext : function(keepExisting){
50887         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50888             this.selectRow(this.last+1, keepExisting);
50889             this.grid.getView().focusRow(this.last);
50890         }
50891     },
50892
50893     /**
50894      * Selects the row that precedes the last selected row.
50895      * @param {Boolean} keepExisting (optional) True to keep existing selections
50896      */
50897     selectPrevious : function(keepExisting){
50898         if(this.last){
50899             this.selectRow(this.last-1, keepExisting);
50900             this.grid.getView().focusRow(this.last);
50901         }
50902     },
50903
50904     /**
50905      * Returns the selected records
50906      * @return {Array} Array of selected records
50907      */
50908     getSelections : function(){
50909         return [].concat(this.selections.items);
50910     },
50911
50912     /**
50913      * Returns the first selected record.
50914      * @return {Record}
50915      */
50916     getSelected : function(){
50917         return this.selections.itemAt(0);
50918     },
50919
50920
50921     /**
50922      * Clears all selections.
50923      */
50924     clearSelections : function(fast){
50925         if(this.locked) return;
50926         if(fast !== true){
50927             var ds = this.grid.dataSource;
50928             var s = this.selections;
50929             s.each(function(r){
50930                 this.deselectRow(ds.indexOfId(r.id));
50931             }, this);
50932             s.clear();
50933         }else{
50934             this.selections.clear();
50935         }
50936         this.last = false;
50937     },
50938
50939
50940     /**
50941      * Selects all rows.
50942      */
50943     selectAll : function(){
50944         if(this.locked) return;
50945         this.selections.clear();
50946         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50947             this.selectRow(i, true);
50948         }
50949     },
50950
50951     /**
50952      * Returns True if there is a selection.
50953      * @return {Boolean}
50954      */
50955     hasSelection : function(){
50956         return this.selections.length > 0;
50957     },
50958
50959     /**
50960      * Returns True if the specified row is selected.
50961      * @param {Number/Record} record The record or index of the record to check
50962      * @return {Boolean}
50963      */
50964     isSelected : function(index){
50965         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50966         return (r && this.selections.key(r.id) ? true : false);
50967     },
50968
50969     /**
50970      * Returns True if the specified record id is selected.
50971      * @param {String} id The id of record to check
50972      * @return {Boolean}
50973      */
50974     isIdSelected : function(id){
50975         return (this.selections.key(id) ? true : false);
50976     },
50977
50978     // private
50979     handleMouseDown : function(e, t){
50980         var view = this.grid.getView(), rowIndex;
50981         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50982             return;
50983         };
50984         if(e.shiftKey && this.last !== false){
50985             var last = this.last;
50986             this.selectRange(last, rowIndex, e.ctrlKey);
50987             this.last = last; // reset the last
50988             view.focusRow(rowIndex);
50989         }else{
50990             var isSelected = this.isSelected(rowIndex);
50991             if(e.button !== 0 && isSelected){
50992                 view.focusRow(rowIndex);
50993             }else if(e.ctrlKey && isSelected){
50994                 this.deselectRow(rowIndex);
50995             }else if(!isSelected){
50996                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50997                 view.focusRow(rowIndex);
50998             }
50999         }
51000         this.fireEvent("afterselectionchange", this);
51001     },
51002     // private
51003     handleDragableRowClick :  function(grid, rowIndex, e) 
51004     {
51005         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51006             this.selectRow(rowIndex, false);
51007             grid.view.focusRow(rowIndex);
51008              this.fireEvent("afterselectionchange", this);
51009         }
51010     },
51011     
51012     /**
51013      * Selects multiple rows.
51014      * @param {Array} rows Array of the indexes of the row to select
51015      * @param {Boolean} keepExisting (optional) True to keep existing selections
51016      */
51017     selectRows : function(rows, keepExisting){
51018         if(!keepExisting){
51019             this.clearSelections();
51020         }
51021         for(var i = 0, len = rows.length; i < len; i++){
51022             this.selectRow(rows[i], true);
51023         }
51024     },
51025
51026     /**
51027      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51028      * @param {Number} startRow The index of the first row in the range
51029      * @param {Number} endRow The index of the last row in the range
51030      * @param {Boolean} keepExisting (optional) True to retain existing selections
51031      */
51032     selectRange : function(startRow, endRow, keepExisting){
51033         if(this.locked) return;
51034         if(!keepExisting){
51035             this.clearSelections();
51036         }
51037         if(startRow <= endRow){
51038             for(var i = startRow; i <= endRow; i++){
51039                 this.selectRow(i, true);
51040             }
51041         }else{
51042             for(var i = startRow; i >= endRow; i--){
51043                 this.selectRow(i, true);
51044             }
51045         }
51046     },
51047
51048     /**
51049      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51050      * @param {Number} startRow The index of the first row in the range
51051      * @param {Number} endRow The index of the last row in the range
51052      */
51053     deselectRange : function(startRow, endRow, preventViewNotify){
51054         if(this.locked) return;
51055         for(var i = startRow; i <= endRow; i++){
51056             this.deselectRow(i, preventViewNotify);
51057         }
51058     },
51059
51060     /**
51061      * Selects a row.
51062      * @param {Number} row The index of the row to select
51063      * @param {Boolean} keepExisting (optional) True to keep existing selections
51064      */
51065     selectRow : function(index, keepExisting, preventViewNotify){
51066         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51067         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51068             if(!keepExisting || this.singleSelect){
51069                 this.clearSelections();
51070             }
51071             var r = this.grid.dataSource.getAt(index);
51072             this.selections.add(r);
51073             this.last = this.lastActive = index;
51074             if(!preventViewNotify){
51075                 this.grid.getView().onRowSelect(index);
51076             }
51077             this.fireEvent("rowselect", this, index, r);
51078             this.fireEvent("selectionchange", this);
51079         }
51080     },
51081
51082     /**
51083      * Deselects a row.
51084      * @param {Number} row The index of the row to deselect
51085      */
51086     deselectRow : function(index, preventViewNotify){
51087         if(this.locked) return;
51088         if(this.last == index){
51089             this.last = false;
51090         }
51091         if(this.lastActive == index){
51092             this.lastActive = false;
51093         }
51094         var r = this.grid.dataSource.getAt(index);
51095         this.selections.remove(r);
51096         if(!preventViewNotify){
51097             this.grid.getView().onRowDeselect(index);
51098         }
51099         this.fireEvent("rowdeselect", this, index);
51100         this.fireEvent("selectionchange", this);
51101     },
51102
51103     // private
51104     restoreLast : function(){
51105         if(this._last){
51106             this.last = this._last;
51107         }
51108     },
51109
51110     // private
51111     acceptsNav : function(row, col, cm){
51112         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51113     },
51114
51115     // private
51116     onEditorKey : function(field, e){
51117         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51118         if(k == e.TAB){
51119             e.stopEvent();
51120             ed.completeEdit();
51121             if(e.shiftKey){
51122                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51123             }else{
51124                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51125             }
51126         }else if(k == e.ENTER && !e.ctrlKey){
51127             e.stopEvent();
51128             ed.completeEdit();
51129             if(e.shiftKey){
51130                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51131             }else{
51132                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51133             }
51134         }else if(k == e.ESC){
51135             ed.cancelEdit();
51136         }
51137         if(newCell){
51138             g.startEditing(newCell[0], newCell[1]);
51139         }
51140     }
51141 });/*
51142  * Based on:
51143  * Ext JS Library 1.1.1
51144  * Copyright(c) 2006-2007, Ext JS, LLC.
51145  *
51146  * Originally Released Under LGPL - original licence link has changed is not relivant.
51147  *
51148  * Fork - LGPL
51149  * <script type="text/javascript">
51150  */
51151 /**
51152  * @class Roo.grid.CellSelectionModel
51153  * @extends Roo.grid.AbstractSelectionModel
51154  * This class provides the basic implementation for cell selection in a grid.
51155  * @constructor
51156  * @param {Object} config The object containing the configuration of this model.
51157  */
51158 Roo.grid.CellSelectionModel = function(config){
51159     Roo.apply(this, config);
51160
51161     this.selection = null;
51162
51163     this.addEvents({
51164         /**
51165              * @event beforerowselect
51166              * Fires before a cell is selected.
51167              * @param {SelectionModel} this
51168              * @param {Number} rowIndex The selected row index
51169              * @param {Number} colIndex The selected cell index
51170              */
51171             "beforecellselect" : true,
51172         /**
51173              * @event cellselect
51174              * Fires when a cell is selected.
51175              * @param {SelectionModel} this
51176              * @param {Number} rowIndex The selected row index
51177              * @param {Number} colIndex The selected cell index
51178              */
51179             "cellselect" : true,
51180         /**
51181              * @event selectionchange
51182              * Fires when the active selection changes.
51183              * @param {SelectionModel} this
51184              * @param {Object} selection null for no selection or an object (o) with two properties
51185                 <ul>
51186                 <li>o.record: the record object for the row the selection is in</li>
51187                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51188                 </ul>
51189              */
51190             "selectionchange" : true
51191     });
51192     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51193 };
51194
51195 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51196
51197     /** @ignore */
51198     initEvents : function(){
51199         this.grid.on("mousedown", this.handleMouseDown, this);
51200         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51201         var view = this.grid.view;
51202         view.on("refresh", this.onViewChange, this);
51203         view.on("rowupdated", this.onRowUpdated, this);
51204         view.on("beforerowremoved", this.clearSelections, this);
51205         view.on("beforerowsinserted", this.clearSelections, this);
51206         if(this.grid.isEditor){
51207             this.grid.on("beforeedit", this.beforeEdit,  this);
51208         }
51209     },
51210
51211         //private
51212     beforeEdit : function(e){
51213         this.select(e.row, e.column, false, true, e.record);
51214     },
51215
51216         //private
51217     onRowUpdated : function(v, index, r){
51218         if(this.selection && this.selection.record == r){
51219             v.onCellSelect(index, this.selection.cell[1]);
51220         }
51221     },
51222
51223         //private
51224     onViewChange : function(){
51225         this.clearSelections(true);
51226     },
51227
51228         /**
51229          * Returns the currently selected cell,.
51230          * @return {Array} The selected cell (row, column) or null if none selected.
51231          */
51232     getSelectedCell : function(){
51233         return this.selection ? this.selection.cell : null;
51234     },
51235
51236     /**
51237      * Clears all selections.
51238      * @param {Boolean} true to prevent the gridview from being notified about the change.
51239      */
51240     clearSelections : function(preventNotify){
51241         var s = this.selection;
51242         if(s){
51243             if(preventNotify !== true){
51244                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51245             }
51246             this.selection = null;
51247             this.fireEvent("selectionchange", this, null);
51248         }
51249     },
51250
51251     /**
51252      * Returns true if there is a selection.
51253      * @return {Boolean}
51254      */
51255     hasSelection : function(){
51256         return this.selection ? true : false;
51257     },
51258
51259     /** @ignore */
51260     handleMouseDown : function(e, t){
51261         var v = this.grid.getView();
51262         if(this.isLocked()){
51263             return;
51264         };
51265         var row = v.findRowIndex(t);
51266         var cell = v.findCellIndex(t);
51267         if(row !== false && cell !== false){
51268             this.select(row, cell);
51269         }
51270     },
51271
51272     /**
51273      * Selects a cell.
51274      * @param {Number} rowIndex
51275      * @param {Number} collIndex
51276      */
51277     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51278         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51279             this.clearSelections();
51280             r = r || this.grid.dataSource.getAt(rowIndex);
51281             this.selection = {
51282                 record : r,
51283                 cell : [rowIndex, colIndex]
51284             };
51285             if(!preventViewNotify){
51286                 var v = this.grid.getView();
51287                 v.onCellSelect(rowIndex, colIndex);
51288                 if(preventFocus !== true){
51289                     v.focusCell(rowIndex, colIndex);
51290                 }
51291             }
51292             this.fireEvent("cellselect", this, rowIndex, colIndex);
51293             this.fireEvent("selectionchange", this, this.selection);
51294         }
51295     },
51296
51297         //private
51298     isSelectable : function(rowIndex, colIndex, cm){
51299         return !cm.isHidden(colIndex);
51300     },
51301
51302     /** @ignore */
51303     handleKeyDown : function(e){
51304         //Roo.log('Cell Sel Model handleKeyDown');
51305         if(!e.isNavKeyPress()){
51306             return;
51307         }
51308         var g = this.grid, s = this.selection;
51309         if(!s){
51310             e.stopEvent();
51311             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51312             if(cell){
51313                 this.select(cell[0], cell[1]);
51314             }
51315             return;
51316         }
51317         var sm = this;
51318         var walk = function(row, col, step){
51319             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51320         };
51321         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51322         var newCell;
51323
51324         switch(k){
51325             case e.TAB:
51326                 // handled by onEditorKey
51327                 if (g.isEditor && g.editing) {
51328                     return;
51329                 }
51330                 if(e.shiftKey){
51331                      newCell = walk(r, c-1, -1);
51332                 }else{
51333                      newCell = walk(r, c+1, 1);
51334                 }
51335              break;
51336              case e.DOWN:
51337                  newCell = walk(r+1, c, 1);
51338              break;
51339              case e.UP:
51340                  newCell = walk(r-1, c, -1);
51341              break;
51342              case e.RIGHT:
51343                  newCell = walk(r, c+1, 1);
51344              break;
51345              case e.LEFT:
51346                  newCell = walk(r, c-1, -1);
51347              break;
51348              case e.ENTER:
51349                  if(g.isEditor && !g.editing){
51350                     g.startEditing(r, c);
51351                     e.stopEvent();
51352                     return;
51353                 }
51354              break;
51355         };
51356         if(newCell){
51357             this.select(newCell[0], newCell[1]);
51358             e.stopEvent();
51359         }
51360     },
51361
51362     acceptsNav : function(row, col, cm){
51363         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51364     },
51365     /**
51366      * Selects a cell.
51367      * @param {Number} field (not used) - as it's normally used as a listener
51368      * @param {Number} e - event - fake it by using
51369      *
51370      * var e = Roo.EventObjectImpl.prototype;
51371      * e.keyCode = e.TAB
51372      *
51373      * 
51374      */
51375     onEditorKey : function(field, e){
51376         
51377         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51378         ///Roo.log('onEditorKey' + k);
51379         if (!ed) {
51380             
51381             
51382             
51383         }
51384         if(k == e.TAB){
51385             if(e.shiftKey){
51386                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51387             }else{
51388                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51389             }
51390             
51391             e.stopEvent();
51392             
51393         }else if(k == e.ENTER &&  !e.ctrlKey){
51394             ed.completeEdit();
51395             e.stopEvent();
51396             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51397         }else if(k == e.ESC){
51398             ed.cancelEdit();
51399         }
51400         
51401         
51402         if(newCell){
51403             //Roo.log('next cell after edit');
51404             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51405         }
51406     }
51407 });/*
51408  * Based on:
51409  * Ext JS Library 1.1.1
51410  * Copyright(c) 2006-2007, Ext JS, LLC.
51411  *
51412  * Originally Released Under LGPL - original licence link has changed is not relivant.
51413  *
51414  * Fork - LGPL
51415  * <script type="text/javascript">
51416  */
51417  
51418 /**
51419  * @class Roo.grid.EditorGrid
51420  * @extends Roo.grid.Grid
51421  * Class for creating and editable grid.
51422  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51423  * The container MUST have some type of size defined for the grid to fill. The container will be 
51424  * automatically set to position relative if it isn't already.
51425  * @param {Object} dataSource The data model to bind to
51426  * @param {Object} colModel The column model with info about this grid's columns
51427  */
51428 Roo.grid.EditorGrid = function(container, config){
51429     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51430     this.getGridEl().addClass("xedit-grid");
51431
51432     if(!this.selModel){
51433         this.selModel = new Roo.grid.CellSelectionModel();
51434     }
51435
51436     this.activeEditor = null;
51437
51438         this.addEvents({
51439             /**
51440              * @event beforeedit
51441              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51442              * <ul style="padding:5px;padding-left:16px;">
51443              * <li>grid - This grid</li>
51444              * <li>record - The record being edited</li>
51445              * <li>field - The field name being edited</li>
51446              * <li>value - The value for the field being edited.</li>
51447              * <li>row - The grid row index</li>
51448              * <li>column - The grid column index</li>
51449              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51450              * </ul>
51451              * @param {Object} e An edit event (see above for description)
51452              */
51453             "beforeedit" : true,
51454             /**
51455              * @event afteredit
51456              * Fires after a cell is edited. <br />
51457              * <ul style="padding:5px;padding-left:16px;">
51458              * <li>grid - This grid</li>
51459              * <li>record - The record being edited</li>
51460              * <li>field - The field name being edited</li>
51461              * <li>value - The value being set</li>
51462              * <li>originalValue - The original value for the field, before the edit.</li>
51463              * <li>row - The grid row index</li>
51464              * <li>column - The grid column index</li>
51465              * </ul>
51466              * @param {Object} e An edit event (see above for description)
51467              */
51468             "afteredit" : true,
51469             /**
51470              * @event validateedit
51471              * Fires after a cell is edited, but before the value is set in the record. 
51472          * You can use this to modify the value being set in the field, Return false
51473              * to cancel the change. The edit event object has the following properties <br />
51474              * <ul style="padding:5px;padding-left:16px;">
51475          * <li>editor - This editor</li>
51476              * <li>grid - This grid</li>
51477              * <li>record - The record being edited</li>
51478              * <li>field - The field name being edited</li>
51479              * <li>value - The value being set</li>
51480              * <li>originalValue - The original value for the field, before the edit.</li>
51481              * <li>row - The grid row index</li>
51482              * <li>column - The grid column index</li>
51483              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51484              * </ul>
51485              * @param {Object} e An edit event (see above for description)
51486              */
51487             "validateedit" : true
51488         });
51489     this.on("bodyscroll", this.stopEditing,  this);
51490     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51491 };
51492
51493 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51494     /**
51495      * @cfg {Number} clicksToEdit
51496      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51497      */
51498     clicksToEdit: 2,
51499
51500     // private
51501     isEditor : true,
51502     // private
51503     trackMouseOver: false, // causes very odd FF errors
51504
51505     onCellDblClick : function(g, row, col){
51506         this.startEditing(row, col);
51507     },
51508
51509     onEditComplete : function(ed, value, startValue){
51510         this.editing = false;
51511         this.activeEditor = null;
51512         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51513         var r = ed.record;
51514         var field = this.colModel.getDataIndex(ed.col);
51515         var e = {
51516             grid: this,
51517             record: r,
51518             field: field,
51519             originalValue: startValue,
51520             value: value,
51521             row: ed.row,
51522             column: ed.col,
51523             cancel:false,
51524             editor: ed
51525         };
51526         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51527         cell.show();
51528           
51529         if(String(value) !== String(startValue)){
51530             
51531             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51532                 r.set(field, e.value);
51533                 // if we are dealing with a combo box..
51534                 // then we also set the 'name' colum to be the displayField
51535                 if (ed.field.displayField && ed.field.name) {
51536                     r.set(ed.field.name, ed.field.el.dom.value);
51537                 }
51538                 
51539                 delete e.cancel; //?? why!!!
51540                 this.fireEvent("afteredit", e);
51541             }
51542         } else {
51543             this.fireEvent("afteredit", e); // always fire it!
51544         }
51545         this.view.focusCell(ed.row, ed.col);
51546     },
51547
51548     /**
51549      * Starts editing the specified for the specified row/column
51550      * @param {Number} rowIndex
51551      * @param {Number} colIndex
51552      */
51553     startEditing : function(row, col){
51554         this.stopEditing();
51555         if(this.colModel.isCellEditable(col, row)){
51556             this.view.ensureVisible(row, col, true);
51557           
51558             var r = this.dataSource.getAt(row);
51559             var field = this.colModel.getDataIndex(col);
51560             var cell = Roo.get(this.view.getCell(row,col));
51561             var e = {
51562                 grid: this,
51563                 record: r,
51564                 field: field,
51565                 value: r.data[field],
51566                 row: row,
51567                 column: col,
51568                 cancel:false 
51569             };
51570             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51571                 this.editing = true;
51572                 var ed = this.colModel.getCellEditor(col, row);
51573                 
51574                 if (!ed) {
51575                     return;
51576                 }
51577                 if(!ed.rendered){
51578                     ed.render(ed.parentEl || document.body);
51579                 }
51580                 ed.field.reset();
51581                
51582                 cell.hide();
51583                 
51584                 (function(){ // complex but required for focus issues in safari, ie and opera
51585                     ed.row = row;
51586                     ed.col = col;
51587                     ed.record = r;
51588                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51589                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51590                     this.activeEditor = ed;
51591                     var v = r.data[field];
51592                     ed.startEdit(this.view.getCell(row, col), v);
51593                     // combo's with 'displayField and name set
51594                     if (ed.field.displayField && ed.field.name) {
51595                         ed.field.el.dom.value = r.data[ed.field.name];
51596                     }
51597                     
51598                     
51599                 }).defer(50, this);
51600             }
51601         }
51602     },
51603         
51604     /**
51605      * Stops any active editing
51606      */
51607     stopEditing : function(){
51608         if(this.activeEditor){
51609             this.activeEditor.completeEdit();
51610         }
51611         this.activeEditor = null;
51612     }
51613 });/*
51614  * Based on:
51615  * Ext JS Library 1.1.1
51616  * Copyright(c) 2006-2007, Ext JS, LLC.
51617  *
51618  * Originally Released Under LGPL - original licence link has changed is not relivant.
51619  *
51620  * Fork - LGPL
51621  * <script type="text/javascript">
51622  */
51623
51624 // private - not really -- you end up using it !
51625 // This is a support class used internally by the Grid components
51626
51627 /**
51628  * @class Roo.grid.GridEditor
51629  * @extends Roo.Editor
51630  * Class for creating and editable grid elements.
51631  * @param {Object} config any settings (must include field)
51632  */
51633 Roo.grid.GridEditor = function(field, config){
51634     if (!config && field.field) {
51635         config = field;
51636         field = Roo.factory(config.field, Roo.form);
51637     }
51638     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51639     field.monitorTab = false;
51640 };
51641
51642 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51643     
51644     /**
51645      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51646      */
51647     
51648     alignment: "tl-tl",
51649     autoSize: "width",
51650     hideEl : false,
51651     cls: "x-small-editor x-grid-editor",
51652     shim:false,
51653     shadow:"frame"
51654 });/*
51655  * Based on:
51656  * Ext JS Library 1.1.1
51657  * Copyright(c) 2006-2007, Ext JS, LLC.
51658  *
51659  * Originally Released Under LGPL - original licence link has changed is not relivant.
51660  *
51661  * Fork - LGPL
51662  * <script type="text/javascript">
51663  */
51664   
51665
51666   
51667 Roo.grid.PropertyRecord = Roo.data.Record.create([
51668     {name:'name',type:'string'},  'value'
51669 ]);
51670
51671
51672 Roo.grid.PropertyStore = function(grid, source){
51673     this.grid = grid;
51674     this.store = new Roo.data.Store({
51675         recordType : Roo.grid.PropertyRecord
51676     });
51677     this.store.on('update', this.onUpdate,  this);
51678     if(source){
51679         this.setSource(source);
51680     }
51681     Roo.grid.PropertyStore.superclass.constructor.call(this);
51682 };
51683
51684
51685
51686 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51687     setSource : function(o){
51688         this.source = o;
51689         this.store.removeAll();
51690         var data = [];
51691         for(var k in o){
51692             if(this.isEditableValue(o[k])){
51693                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51694             }
51695         }
51696         this.store.loadRecords({records: data}, {}, true);
51697     },
51698
51699     onUpdate : function(ds, record, type){
51700         if(type == Roo.data.Record.EDIT){
51701             var v = record.data['value'];
51702             var oldValue = record.modified['value'];
51703             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51704                 this.source[record.id] = v;
51705                 record.commit();
51706                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51707             }else{
51708                 record.reject();
51709             }
51710         }
51711     },
51712
51713     getProperty : function(row){
51714        return this.store.getAt(row);
51715     },
51716
51717     isEditableValue: function(val){
51718         if(val && val instanceof Date){
51719             return true;
51720         }else if(typeof val == 'object' || typeof val == 'function'){
51721             return false;
51722         }
51723         return true;
51724     },
51725
51726     setValue : function(prop, value){
51727         this.source[prop] = value;
51728         this.store.getById(prop).set('value', value);
51729     },
51730
51731     getSource : function(){
51732         return this.source;
51733     }
51734 });
51735
51736 Roo.grid.PropertyColumnModel = function(grid, store){
51737     this.grid = grid;
51738     var g = Roo.grid;
51739     g.PropertyColumnModel.superclass.constructor.call(this, [
51740         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51741         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51742     ]);
51743     this.store = store;
51744     this.bselect = Roo.DomHelper.append(document.body, {
51745         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51746             {tag: 'option', value: 'true', html: 'true'},
51747             {tag: 'option', value: 'false', html: 'false'}
51748         ]
51749     });
51750     Roo.id(this.bselect);
51751     var f = Roo.form;
51752     this.editors = {
51753         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51754         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51755         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51756         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51757         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51758     };
51759     this.renderCellDelegate = this.renderCell.createDelegate(this);
51760     this.renderPropDelegate = this.renderProp.createDelegate(this);
51761 };
51762
51763 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51764     
51765     
51766     nameText : 'Name',
51767     valueText : 'Value',
51768     
51769     dateFormat : 'm/j/Y',
51770     
51771     
51772     renderDate : function(dateVal){
51773         return dateVal.dateFormat(this.dateFormat);
51774     },
51775
51776     renderBool : function(bVal){
51777         return bVal ? 'true' : 'false';
51778     },
51779
51780     isCellEditable : function(colIndex, rowIndex){
51781         return colIndex == 1;
51782     },
51783
51784     getRenderer : function(col){
51785         return col == 1 ?
51786             this.renderCellDelegate : this.renderPropDelegate;
51787     },
51788
51789     renderProp : function(v){
51790         return this.getPropertyName(v);
51791     },
51792
51793     renderCell : function(val){
51794         var rv = val;
51795         if(val instanceof Date){
51796             rv = this.renderDate(val);
51797         }else if(typeof val == 'boolean'){
51798             rv = this.renderBool(val);
51799         }
51800         return Roo.util.Format.htmlEncode(rv);
51801     },
51802
51803     getPropertyName : function(name){
51804         var pn = this.grid.propertyNames;
51805         return pn && pn[name] ? pn[name] : name;
51806     },
51807
51808     getCellEditor : function(colIndex, rowIndex){
51809         var p = this.store.getProperty(rowIndex);
51810         var n = p.data['name'], val = p.data['value'];
51811         
51812         if(typeof(this.grid.customEditors[n]) == 'string'){
51813             return this.editors[this.grid.customEditors[n]];
51814         }
51815         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51816             return this.grid.customEditors[n];
51817         }
51818         if(val instanceof Date){
51819             return this.editors['date'];
51820         }else if(typeof val == 'number'){
51821             return this.editors['number'];
51822         }else if(typeof val == 'boolean'){
51823             return this.editors['boolean'];
51824         }else{
51825             return this.editors['string'];
51826         }
51827     }
51828 });
51829
51830 /**
51831  * @class Roo.grid.PropertyGrid
51832  * @extends Roo.grid.EditorGrid
51833  * This class represents the  interface of a component based property grid control.
51834  * <br><br>Usage:<pre><code>
51835  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51836       
51837  });
51838  // set any options
51839  grid.render();
51840  * </code></pre>
51841   
51842  * @constructor
51843  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51844  * The container MUST have some type of size defined for the grid to fill. The container will be
51845  * automatically set to position relative if it isn't already.
51846  * @param {Object} config A config object that sets properties on this grid.
51847  */
51848 Roo.grid.PropertyGrid = function(container, config){
51849     config = config || {};
51850     var store = new Roo.grid.PropertyStore(this);
51851     this.store = store;
51852     var cm = new Roo.grid.PropertyColumnModel(this, store);
51853     store.store.sort('name', 'ASC');
51854     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51855         ds: store.store,
51856         cm: cm,
51857         enableColLock:false,
51858         enableColumnMove:false,
51859         stripeRows:false,
51860         trackMouseOver: false,
51861         clicksToEdit:1
51862     }, config));
51863     this.getGridEl().addClass('x-props-grid');
51864     this.lastEditRow = null;
51865     this.on('columnresize', this.onColumnResize, this);
51866     this.addEvents({
51867          /**
51868              * @event beforepropertychange
51869              * Fires before a property changes (return false to stop?)
51870              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51871              * @param {String} id Record Id
51872              * @param {String} newval New Value
51873          * @param {String} oldval Old Value
51874              */
51875         "beforepropertychange": true,
51876         /**
51877              * @event propertychange
51878              * Fires after a property changes
51879              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51880              * @param {String} id Record Id
51881              * @param {String} newval New Value
51882          * @param {String} oldval Old Value
51883              */
51884         "propertychange": true
51885     });
51886     this.customEditors = this.customEditors || {};
51887 };
51888 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51889     
51890      /**
51891      * @cfg {Object} customEditors map of colnames=> custom editors.
51892      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51893      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51894      * false disables editing of the field.
51895          */
51896     
51897       /**
51898      * @cfg {Object} propertyNames map of property Names to their displayed value
51899          */
51900     
51901     render : function(){
51902         Roo.grid.PropertyGrid.superclass.render.call(this);
51903         this.autoSize.defer(100, this);
51904     },
51905
51906     autoSize : function(){
51907         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51908         if(this.view){
51909             this.view.fitColumns();
51910         }
51911     },
51912
51913     onColumnResize : function(){
51914         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51915         this.autoSize();
51916     },
51917     /**
51918      * Sets the data for the Grid
51919      * accepts a Key => Value object of all the elements avaiable.
51920      * @param {Object} data  to appear in grid.
51921      */
51922     setSource : function(source){
51923         this.store.setSource(source);
51924         //this.autoSize();
51925     },
51926     /**
51927      * Gets all the data from the grid.
51928      * @return {Object} data  data stored in grid
51929      */
51930     getSource : function(){
51931         return this.store.getSource();
51932     }
51933 });/*
51934  * Based on:
51935  * Ext JS Library 1.1.1
51936  * Copyright(c) 2006-2007, Ext JS, LLC.
51937  *
51938  * Originally Released Under LGPL - original licence link has changed is not relivant.
51939  *
51940  * Fork - LGPL
51941  * <script type="text/javascript">
51942  */
51943  
51944 /**
51945  * @class Roo.LoadMask
51946  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51947  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51948  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51949  * element's UpdateManager load indicator and will be destroyed after the initial load.
51950  * @constructor
51951  * Create a new LoadMask
51952  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51953  * @param {Object} config The config object
51954  */
51955 Roo.LoadMask = function(el, config){
51956     this.el = Roo.get(el);
51957     Roo.apply(this, config);
51958     if(this.store){
51959         this.store.on('beforeload', this.onBeforeLoad, this);
51960         this.store.on('load', this.onLoad, this);
51961         this.store.on('loadexception', this.onLoadException, this);
51962         this.removeMask = false;
51963     }else{
51964         var um = this.el.getUpdateManager();
51965         um.showLoadIndicator = false; // disable the default indicator
51966         um.on('beforeupdate', this.onBeforeLoad, this);
51967         um.on('update', this.onLoad, this);
51968         um.on('failure', this.onLoad, this);
51969         this.removeMask = true;
51970     }
51971 };
51972
51973 Roo.LoadMask.prototype = {
51974     /**
51975      * @cfg {Boolean} removeMask
51976      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51977      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51978      */
51979     /**
51980      * @cfg {String} msg
51981      * The text to display in a centered loading message box (defaults to 'Loading...')
51982      */
51983     msg : 'Loading...',
51984     /**
51985      * @cfg {String} msgCls
51986      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51987      */
51988     msgCls : 'x-mask-loading',
51989
51990     /**
51991      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51992      * @type Boolean
51993      */
51994     disabled: false,
51995
51996     /**
51997      * Disables the mask to prevent it from being displayed
51998      */
51999     disable : function(){
52000        this.disabled = true;
52001     },
52002
52003     /**
52004      * Enables the mask so that it can be displayed
52005      */
52006     enable : function(){
52007         this.disabled = false;
52008     },
52009     
52010     onLoadException : function()
52011     {
52012         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52013             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52014         }
52015         this.el.unmask(this.removeMask);
52016     },
52017     // private
52018     onLoad : function()
52019     {
52020         this.el.unmask(this.removeMask);
52021     },
52022
52023     // private
52024     onBeforeLoad : function(){
52025         if(!this.disabled){
52026             this.el.mask(this.msg, this.msgCls);
52027         }
52028     },
52029
52030     // private
52031     destroy : function(){
52032         if(this.store){
52033             this.store.un('beforeload', this.onBeforeLoad, this);
52034             this.store.un('load', this.onLoad, this);
52035             this.store.un('loadexception', this.onLoadException, this);
52036         }else{
52037             var um = this.el.getUpdateManager();
52038             um.un('beforeupdate', this.onBeforeLoad, this);
52039             um.un('update', this.onLoad, this);
52040             um.un('failure', this.onLoad, this);
52041         }
52042     }
52043 };/*
52044  * Based on:
52045  * Ext JS Library 1.1.1
52046  * Copyright(c) 2006-2007, Ext JS, LLC.
52047  *
52048  * Originally Released Under LGPL - original licence link has changed is not relivant.
52049  *
52050  * Fork - LGPL
52051  * <script type="text/javascript">
52052  */
52053 Roo.XTemplate = function(){
52054     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52055     var s = this.html;
52056
52057     s = ['<tpl>', s, '</tpl>'].join('');
52058
52059     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52060
52061     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52062     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52063     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52064     var m, id = 0;
52065     var tpls = [];
52066
52067     while(m = s.match(re)){
52068        var m2 = m[0].match(nameRe);
52069        var m3 = m[0].match(ifRe);
52070        var m4 = m[0].match(execRe);
52071        var exp = null, fn = null, exec = null;
52072        var name = m2 && m2[1] ? m2[1] : '';
52073        if(m3){
52074            exp = m3 && m3[1] ? m3[1] : null;
52075            if(exp){
52076                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52077            }
52078        }
52079        if(m4){
52080            exp = m4 && m4[1] ? m4[1] : null;
52081            if(exp){
52082                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52083            }
52084        }
52085        if(name){
52086            switch(name){
52087                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52088                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52089                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52090            }
52091        }
52092        tpls.push({
52093             id: id,
52094             target: name,
52095             exec: exec,
52096             test: fn,
52097             body: m[1]||''
52098         });
52099        s = s.replace(m[0], '{xtpl'+ id + '}');
52100        ++id;
52101     }
52102     for(var i = tpls.length-1; i >= 0; --i){
52103         this.compileTpl(tpls[i]);
52104     }
52105     this.master = tpls[tpls.length-1];
52106     this.tpls = tpls;
52107 };
52108 Roo.extend(Roo.XTemplate, Roo.Template, {
52109
52110     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52111
52112     applySubTemplate : function(id, values, parent){
52113         var t = this.tpls[id];
52114         if(t.test && !t.test.call(this, values, parent)){
52115             return '';
52116         }
52117         if(t.exec && t.exec.call(this, values, parent)){
52118             return '';
52119         }
52120         var vs = t.target ? t.target.call(this, values, parent) : values;
52121         parent = t.target ? values : parent;
52122         if(t.target && vs instanceof Array){
52123             var buf = [];
52124             for(var i = 0, len = vs.length; i < len; i++){
52125                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52126             }
52127             return buf.join('');
52128         }
52129         return t.compiled.call(this, vs, parent);
52130     },
52131
52132     compileTpl : function(tpl){
52133         var fm = Roo.util.Format;
52134         var useF = this.disableFormats !== true;
52135         var sep = Roo.isGecko ? "+" : ",";
52136         var fn = function(m, name, format, args){
52137             if(name.substr(0, 4) == 'xtpl'){
52138                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52139             }
52140             var v;
52141             if(name.indexOf('.') != -1){
52142                 v = name;
52143             }else{
52144                 v = "values['" + name + "']";
52145             }
52146             if(format && useF){
52147                 args = args ? ',' + args : "";
52148                 if(format.substr(0, 5) != "this."){
52149                     format = "fm." + format + '(';
52150                 }else{
52151                     format = 'this.call("'+ format.substr(5) + '", ';
52152                     args = ", values";
52153                 }
52154             }else{
52155                 args= ''; format = "("+v+" === undefined ? '' : ";
52156             }
52157             return "'"+ sep + format + v + args + ")"+sep+"'";
52158         };
52159         var body;
52160         // branched to use + in gecko and [].join() in others
52161         if(Roo.isGecko){
52162             body = "tpl.compiled = function(values, parent){ return '" +
52163                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52164                     "';};";
52165         }else{
52166             body = ["tpl.compiled = function(values, parent){ return ['"];
52167             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52168             body.push("'].join('');};");
52169             body = body.join('');
52170         }
52171         /** eval:var:zzzzzzz */
52172         eval(body);
52173         return this;
52174     },
52175
52176     applyTemplate : function(values){
52177         return this.master.compiled.call(this, values, {});
52178         var s = this.subs;
52179     },
52180
52181     apply : function(){
52182         return this.applyTemplate.apply(this, arguments);
52183     },
52184
52185     compile : function(){return this;}
52186 });
52187
52188 Roo.XTemplate.from = function(el){
52189     el = Roo.getDom(el);
52190     return new Roo.XTemplate(el.value || el.innerHTML);
52191 };/*
52192  * Original code for Roojs - LGPL
52193  * <script type="text/javascript">
52194  */
52195  
52196 /**
52197  * @class Roo.XComponent
52198  * A delayed Element creator...
52199  * Or a way to group chunks of interface together.
52200  * 
52201  * Mypart.xyx = new Roo.XComponent({
52202
52203     parent : 'Mypart.xyz', // empty == document.element.!!
52204     order : '001',
52205     name : 'xxxx'
52206     region : 'xxxx'
52207     disabled : function() {} 
52208      
52209     tree : function() { // return an tree of xtype declared components
52210         var MODULE = this;
52211         return 
52212         {
52213             xtype : 'NestedLayoutPanel',
52214             // technicall
52215         }
52216      ]
52217  *})
52218  *
52219  *
52220  * It can be used to build a big heiracy, with parent etc.
52221  * or you can just use this to render a single compoent to a dom element
52222  * MYPART.render(Roo.Element | String(id) | dom_element )
52223  * 
52224  * @extends Roo.util.Observable
52225  * @constructor
52226  * @param cfg {Object} configuration of component
52227  * 
52228  */
52229 Roo.XComponent = function(cfg) {
52230     Roo.apply(this, cfg);
52231     this.addEvents({ 
52232         /**
52233              * @event built
52234              * Fires when this the componnt is built
52235              * @param {Roo.XComponent} c the component
52236              */
52237         'built' : true,
52238         /**
52239              * @event buildcomplete
52240              * Fires on the top level element when all elements have been built
52241              * @param {Roo.XComponent} c the top level component.
52242          */
52243         'buildcomplete' : true
52244         
52245     });
52246     this.region = this.region || 'center'; // default..
52247     Roo.XComponent.register(this);
52248     this.modules = false;
52249     this.el = false; // where the layout goes..
52250     
52251     
52252 }
52253 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52254     /**
52255      * @property el
52256      * The created element (with Roo.factory())
52257      * @type {Roo.Layout}
52258      */
52259     el  : false,
52260     
52261     /**
52262      * @property el
52263      * for BC  - use el in new code
52264      * @type {Roo.Layout}
52265      */
52266     panel : false,
52267     
52268     /**
52269      * @property layout
52270      * for BC  - use el in new code
52271      * @type {Roo.Layout}
52272      */
52273     layout : false,
52274     
52275      /**
52276      * @cfg {Function|boolean} disabled
52277      * If this module is disabled by some rule, return true from the funtion
52278      */
52279     disabled : false,
52280     
52281     /**
52282      * @cfg {String} parent 
52283      * Name of parent element which it get xtype added to..
52284      */
52285     parent: false,
52286     
52287     /**
52288      * @cfg {String} order
52289      * Used to set the order in which elements are created (usefull for multiple tabs)
52290      */
52291     
52292     order : false,
52293     /**
52294      * @cfg {String} name
52295      * String to display while loading.
52296      */
52297     name : false,
52298     /**
52299      * @cfg {String} region
52300      * Region to render component to (defaults to center)
52301      */
52302     region : 'center',
52303     
52304     /**
52305      * @cfg {Array} items
52306      * A single item array - the first element is the root of the tree..
52307      * It's done this way to stay compatible with the Xtype system...
52308      */
52309     items : false,
52310     
52311     
52312      /**
52313      * render
52314      * render element to dom or tree
52315      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52316      */
52317     
52318     render : function(el)
52319     {
52320         
52321         el = el || false;
52322         var hp = this.parent ? 1 : 0;
52323         
52324         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52325             // if parent is a '#.....' string, then let's use that..
52326             var ename = this.parent.substr(1)
52327             this.parent = false;
52328             el = Roo.get(ename);
52329             if (!el) {
52330                 Roo.log("Warning - element can not be found :#" + ename );
52331                 return;
52332             }
52333         }
52334         
52335         
52336         if (!this.parent) {
52337             
52338             el = el ? Roo.get(el) : false;
52339             
52340             // it's a top level one..
52341             this.parent =  {
52342                 el : new Roo.BorderLayout(el || document.body, {
52343                 
52344                      center: {
52345                          titlebar: false,
52346                          autoScroll:false,
52347                          closeOnTab: true,
52348                          tabPosition: 'top',
52349                           //resizeTabs: true,
52350                          alwaysShowTabs: el && hp? false :  true,
52351                          hideTabs: el || !hp ? true :  false,
52352                          minTabWidth: 140
52353                      }
52354                  })
52355             }
52356         }
52357         
52358         
52359             
52360         var tree = this.tree();
52361         tree.region = tree.region || this.region;
52362         this.el = this.parent.el.addxtype(tree);
52363         this.fireEvent('built', this);
52364         
52365         this.panel = this.el;
52366         this.layout = this.panel.layout;    
52367          
52368     }
52369     
52370 });
52371
52372 Roo.apply(Roo.XComponent, {
52373     
52374     /**
52375      * @property  buildCompleted
52376      * True when the builder has completed building the interface.
52377      * @type Boolean
52378      */
52379     buildCompleted : false,
52380      
52381     /**
52382      * @property  topModule
52383      * the upper most module - uses document.element as it's constructor.
52384      * @type Object
52385      */
52386      
52387     topModule  : false,
52388       
52389     /**
52390      * @property  modules
52391      * array of modules to be created by registration system.
52392      * @type {Array} of Roo.XComponent
52393      */
52394     
52395     modules : [],
52396     /**
52397      * @property  elmodules
52398      * array of modules to be created by which use #ID 
52399      * @type {Array} of Roo.XComponent
52400      */
52401      
52402     elmodules : [],
52403
52404     
52405     /**
52406      * Register components to be built later.
52407      *
52408      * This solves the following issues
52409      * - Building is not done on page load, but after an authentication process has occured.
52410      * - Interface elements are registered on page load
52411      * - Parent Interface elements may not be loaded before child, so this handles that..
52412      * 
52413      *
52414      * example:
52415      * 
52416      * MyApp.register({
52417           order : '000001',
52418           module : 'Pman.Tab.projectMgr',
52419           region : 'center',
52420           parent : 'Pman.layout',
52421           disabled : false,  // or use a function..
52422         })
52423      
52424      * * @param {Object} details about module
52425      */
52426     register : function(obj) {
52427         this.modules.push(obj);
52428          
52429     },
52430     /**
52431      * convert a string to an object..
52432      * eg. 'AAA.BBB' -> finds AAA.BBB
52433
52434      */
52435     
52436     toObject : function(str)
52437     {
52438         if (!str || typeof(str) == 'object') {
52439             return str;
52440         }
52441         if (str.substring(0,1) == '#') {
52442             return str;
52443         }
52444
52445         var ar = str.split('.');
52446         var rt, o;
52447         rt = ar.shift();
52448             /** eval:var:o */
52449         try {
52450             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52451         } catch (e) {
52452             throw "Module not found : " + str;
52453         }
52454         
52455         if (o === false) {
52456             throw "Module not found : " + str;
52457         }
52458         Roo.each(ar, function(e) {
52459             if (typeof(o[e]) == 'undefined') {
52460                 throw "Module not found : " + str;
52461             }
52462             o = o[e];
52463         });
52464         
52465         return o;
52466         
52467     },
52468     
52469     
52470     /**
52471      * move modules into their correct place in the tree..
52472      * 
52473      */
52474     preBuild : function ()
52475     {
52476         var _t = this;
52477         Roo.each(this.modules , function (obj)
52478         {
52479             var opar = obj.parent;
52480             try { 
52481                 obj.parent = this.toObject(opar);
52482             } catch(e) {
52483                 Roo.log(e.toString());
52484                 return;
52485             }
52486             
52487             if (!obj.parent) {
52488                 this.topModule = obj;
52489                 return;
52490             }
52491             if (typeof(obj.parent) == 'string') {
52492                 this.elmodules.push(obj);
52493                 return;
52494             }
52495             if (obj.parent.constructor != Roo.XComponent) {
52496                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52497             }
52498             if (!obj.parent.modules) {
52499                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52500                     function(o) { return o.order + '' }
52501                 );
52502             }
52503             
52504             obj.parent.modules.add(obj);
52505         }, this);
52506     },
52507     
52508      /**
52509      * make a list of modules to build.
52510      * @return {Array} list of modules. 
52511      */ 
52512     
52513     buildOrder : function()
52514     {
52515         var _this = this;
52516         var cmp = function(a,b) {   
52517             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52518         };
52519         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52520             throw "No top level modules to build";
52521         }
52522         
52523         // make a flat list in order of modules to build.
52524         var mods = this.topModule ? [ this.topModule ] : [];
52525         Roo.each(this.elmodules,function(e) { mods.push(e) });
52526
52527         
52528         // add modules to their parents..
52529         var addMod = function(m) {
52530            // Roo.debug && Roo.log(m.modKey);
52531             
52532             mods.push(m);
52533             if (m.modules) {
52534                 m.modules.keySort('ASC',  cmp );
52535                 m.modules.each(addMod);
52536             }
52537             // not sure if this is used any more..
52538             if (m.finalize) {
52539                 m.finalize.name = m.name + " (clean up) ";
52540                 mods.push(m.finalize);
52541             }
52542             
52543         }
52544         if (this.topModule) { 
52545             this.topModule.modules.keySort('ASC',  cmp );
52546             this.topModule.modules.each(addMod);
52547         }
52548         return mods;
52549     },
52550     
52551      /**
52552      * Build the registered modules.
52553      * @param {Object} parent element.
52554      * @param {Function} optional method to call after module has been added.
52555      * 
52556      */ 
52557    
52558     build : function() 
52559     {
52560         
52561         this.preBuild();
52562         var mods = this.buildOrder();
52563       
52564         //this.allmods = mods;
52565         //Roo.debug && Roo.log(mods);
52566         //return;
52567         if (!mods.length) { // should not happen
52568             throw "NO modules!!!";
52569         }
52570         
52571         
52572         
52573         // flash it up as modal - so we store the mask!?
52574         Roo.MessageBox.show({ title: 'loading' });
52575         Roo.MessageBox.show({
52576            title: "Please wait...",
52577            msg: "Building Interface...",
52578            width:450,
52579            progress:true,
52580            closable:false,
52581            modal: false
52582           
52583         });
52584         var total = mods.length;
52585         
52586         var _this = this;
52587         var progressRun = function() {
52588             if (!mods.length) {
52589                 Roo.debug && Roo.log('hide?');
52590                 Roo.MessageBox.hide();
52591                 if (_this.topModule) { 
52592                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52593                 }
52594                 // THE END...
52595                 return false;   
52596             }
52597             
52598             var m = mods.shift();
52599             
52600             
52601             Roo.debug && Roo.log(m);
52602             // not sure if this is supported any more.. - modules that are are just function
52603             if (typeof(m) == 'function') { 
52604                 m.call(this);
52605                 return progressRun.defer(10, _this);
52606             } 
52607             
52608             
52609             
52610             Roo.MessageBox.updateProgress(
52611                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52612                     " of " + total + 
52613                     (m.name ? (' - ' + m.name) : '')
52614                     );
52615             
52616          
52617             // is the module disabled?
52618             var disabled = (typeof(m.disabled) == 'function') ?
52619                 m.disabled.call(m.module.disabled) : m.disabled;    
52620             
52621             
52622             if (disabled) {
52623                 return progressRun(); // we do not update the display!
52624             }
52625             
52626             // now build 
52627             
52628             m.render();
52629             // it's 10 on top level, and 1 on others??? why...
52630             return progressRun.defer(10, _this);
52631              
52632         }
52633         progressRun.defer(1, _this);
52634      
52635         
52636         
52637     }
52638     
52639      
52640    
52641     
52642     
52643 });
52644  //<script type="text/javascript">
52645
52646
52647 /**
52648  * @class Roo.Login
52649  * @extends Roo.LayoutDialog
52650  * A generic Login Dialog..... - only one needed in theory!?!?
52651  *
52652  * Fires XComponent builder on success...
52653  * 
52654  * Sends 
52655  *    username,password, lang = for login actions.
52656  *    check = 1 for periodic checking that sesion is valid.
52657  *    passwordRequest = email request password
52658  *    logout = 1 = to logout
52659  * 
52660  * Affects: (this id="????" elements)
52661  *   loading  (removed) (used to indicate application is loading)
52662  *   loading-mask (hides) (used to hide application when it's building loading)
52663  *   
52664  * 
52665  * Usage: 
52666  *    
52667  * 
52668  * Myapp.login = Roo.Login({
52669      url: xxxx,
52670    
52671      realm : 'Myapp', 
52672      
52673      
52674      method : 'POST',
52675      
52676      
52677      * 
52678  })
52679  * 
52680  * 
52681  * 
52682  **/
52683  
52684 Roo.Login = function(cfg)
52685 {
52686     this.addEvents({
52687         'refreshed' : true
52688     });
52689     
52690     Roo.apply(this,cfg);
52691     
52692     Roo.onReady(function() {
52693         this.onLoad();
52694     }, this);
52695     // call parent..
52696     
52697    
52698     Roo.Login.superclass.constructor.call(this, this);
52699     //this.addxtype(this.items[0]);
52700     
52701     
52702 }
52703
52704
52705 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52706     
52707     /**
52708      * @cfg {String} method
52709      * Method used to query for login details.
52710      */
52711     
52712     method : 'POST',
52713     /**
52714      * @cfg {String} url
52715      * URL to query login data. - eg. baseURL + '/Login.php'
52716      */
52717     url : '',
52718     
52719     /**
52720      * @property user
52721      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52722      * @type {Object} 
52723      */
52724     user : false,
52725     /**
52726      * @property checkFails
52727      * Number of times we have attempted to get authentication check, and failed.
52728      * @type {Number} 
52729      */
52730     checkFails : 0,
52731       /**
52732      * @property intervalID
52733      * The window interval that does the constant login checking.
52734      * @type {Number} 
52735      */
52736     intervalID : 0,
52737     
52738     
52739     onLoad : function() // called on page load...
52740     {
52741         // load 
52742          
52743         if (Roo.get('loading')) { // clear any loading indicator..
52744             Roo.get('loading').remove();
52745         }
52746         
52747         //this.switchLang('en'); // set the language to english..
52748        
52749         this.check({
52750             success:  function(response, opts)  {  // check successfull...
52751             
52752                 var res = this.processResponse(response);
52753                 this.checkFails =0;
52754                 if (!res.success) { // error!
52755                     this.checkFails = 5;
52756                     //console.log('call failure');
52757                     return this.failure(response,opts);
52758                 }
52759                 
52760                 if (!res.data.id) { // id=0 == login failure.
52761                     return this.show();
52762                 }
52763                 
52764                               
52765                         //console.log(success);
52766                 this.fillAuth(res.data);   
52767                 this.checkFails =0;
52768                 Roo.XComponent.build();
52769             },
52770             failure : this.show
52771         });
52772         
52773     }, 
52774     
52775     
52776     check: function(cfg) // called every so often to refresh cookie etc..
52777     {
52778         if (cfg.again) { // could be undefined..
52779             this.checkFails++;
52780         } else {
52781             this.checkFails = 0;
52782         }
52783         var _this = this;
52784         if (this.sending) {
52785             if ( this.checkFails > 4) {
52786                 Roo.MessageBox.alert("Error",  
52787                     "Error getting authentication status. - try reloading, or wait a while", function() {
52788                         _this.sending = false;
52789                     }); 
52790                 return;
52791             }
52792             cfg.again = true;
52793             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52794             return;
52795         }
52796         this.sending = true;
52797         
52798         Roo.Ajax.request({  
52799             url: this.url,
52800             params: {
52801                 getAuthUser: true
52802             },  
52803             method: this.method,
52804             success:  cfg.success || this.success,
52805             failure : cfg.failure || this.failure,
52806             scope : this,
52807             callCfg : cfg
52808               
52809         });  
52810     }, 
52811     
52812     
52813     logout: function()
52814     {
52815         window.onbeforeunload = function() { }; // false does not work for IE..
52816         this.user = false;
52817         var _this = this;
52818         
52819         Roo.Ajax.request({  
52820             url: this.url,
52821             params: {
52822                 logout: 1
52823             },  
52824             method: 'GET',
52825             failure : function() {
52826                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52827                     document.location = document.location.toString() + '?ts=' + Math.random();
52828                 });
52829                 
52830             },
52831             success : function() {
52832                 _this.user = false;
52833                 this.checkFails =0;
52834                 // fixme..
52835                 document.location = document.location.toString() + '?ts=' + Math.random();
52836             }
52837               
52838               
52839         }); 
52840     },
52841     
52842     processResponse : function (response)
52843     {
52844         var res = '';
52845         try {
52846             res = Roo.decode(response.responseText);
52847             // oops...
52848             if (typeof(res) != 'object') {
52849                 res = { success : false, errorMsg : res, errors : true };
52850             }
52851             if (typeof(res.success) == 'undefined') {
52852                 res.success = false;
52853             }
52854             
52855         } catch(e) {
52856             res = { success : false,  errorMsg : response.responseText, errors : true };
52857         }
52858         return res;
52859     },
52860     
52861     success : function(response, opts)  // check successfull...
52862     {  
52863         this.sending = false;
52864         var res = this.processResponse(response);
52865         if (!res.success) {
52866             return this.failure(response, opts);
52867         }
52868         if (!res.data || !res.data.id) {
52869             return this.failure(response,opts);
52870         }
52871         //console.log(res);
52872         this.fillAuth(res.data);
52873         
52874         this.checkFails =0;
52875         
52876     },
52877     
52878     
52879     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52880     {
52881         this.authUser = -1;
52882         this.sending = false;
52883         var res = this.processResponse(response);
52884         //console.log(res);
52885         if ( this.checkFails > 2) {
52886         
52887             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52888                 "Error getting authentication status. - try reloading"); 
52889             return;
52890         }
52891         opts.callCfg.again = true;
52892         this.check.defer(1000, this, [ opts.callCfg ]);
52893         return;  
52894     },
52895     
52896     
52897     
52898     fillAuth: function(au) {
52899         this.startAuthCheck();
52900         this.authUserId = au.id;
52901         this.authUser = au;
52902         this.lastChecked = new Date();
52903         this.fireEvent('refreshed', au);
52904         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52905         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52906         au.lang = au.lang || 'en';
52907         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52908         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52909         this.switchLang(au.lang );
52910         
52911      
52912         // open system... - -on setyp..
52913         if (this.authUserId  < 0) {
52914             Roo.MessageBox.alert("Warning", 
52915                 "This is an open system - please set up a admin user with a password.");  
52916         }
52917          
52918         //Pman.onload(); // which should do nothing if it's a re-auth result...
52919         
52920              
52921     },
52922     
52923     startAuthCheck : function() // starter for timeout checking..
52924     {
52925         if (this.intervalID) { // timer already in place...
52926             return false;
52927         }
52928         var _this = this;
52929         this.intervalID =  window.setInterval(function() {
52930               _this.check(false);
52931             }, 120000); // every 120 secs = 2mins..
52932         
52933         
52934     },
52935          
52936     
52937     switchLang : function (lang) 
52938     {
52939         _T = typeof(_T) == 'undefined' ? false : _T;
52940           if (!_T || !lang.length) {
52941             return;
52942         }
52943         
52944         if (!_T && lang != 'en') {
52945             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52946             return;
52947         }
52948         
52949         if (typeof(_T.en) == 'undefined') {
52950             _T.en = {};
52951             Roo.apply(_T.en, _T);
52952         }
52953         
52954         if (typeof(_T[lang]) == 'undefined') {
52955             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52956             return;
52957         }
52958         
52959         
52960         Roo.apply(_T, _T[lang]);
52961         // just need to set the text values for everything...
52962         var _this = this;
52963         /* this will not work ...
52964         if (this.form) { 
52965             
52966                
52967             function formLabel(name, val) {
52968                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52969             }
52970             
52971             formLabel('password', "Password"+':');
52972             formLabel('username', "Email Address"+':');
52973             formLabel('lang', "Language"+':');
52974             this.dialog.setTitle("Login");
52975             this.dialog.buttons[0].setText("Forgot Password");
52976             this.dialog.buttons[1].setText("Login");
52977         }
52978         */
52979         
52980         
52981     },
52982     
52983     
52984     title: "Login",
52985     modal: true,
52986     width:  350,
52987     //height: 230,
52988     height: 180,
52989     shadow: true,
52990     minWidth:200,
52991     minHeight:180,
52992     //proxyDrag: true,
52993     closable: false,
52994     draggable: false,
52995     collapsible: false,
52996     resizable: false,
52997     center: {  // needed??
52998         autoScroll:false,
52999         titlebar: false,
53000        // tabPosition: 'top',
53001         hideTabs: true,
53002         closeOnTab: true,
53003         alwaysShowTabs: false
53004     } ,
53005     listeners : {
53006         
53007         show  : function(dlg)
53008         {
53009             //console.log(this);
53010             this.form = this.layout.getRegion('center').activePanel.form;
53011             this.form.dialog = dlg;
53012             this.buttons[0].form = this.form;
53013             this.buttons[0].dialog = dlg;
53014             this.buttons[1].form = this.form;
53015             this.buttons[1].dialog = dlg;
53016            
53017            //this.resizeToLogo.defer(1000,this);
53018             // this is all related to resizing for logos..
53019             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53020            //// if (!sz) {
53021              //   this.resizeToLogo.defer(1000,this);
53022              //   return;
53023            // }
53024             //var w = Ext.lib.Dom.getViewWidth() - 100;
53025             //var h = Ext.lib.Dom.getViewHeight() - 100;
53026             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53027             //this.center();
53028             if (this.disabled) {
53029                 this.hide();
53030                 return;
53031             }
53032             
53033             if (this.user.id < 0) { // used for inital setup situations.
53034                 return;
53035             }
53036             
53037             if (this.intervalID) {
53038                 // remove the timer
53039                 window.clearInterval(this.intervalID);
53040                 this.intervalID = false;
53041             }
53042             
53043             
53044             if (Roo.get('loading')) {
53045                 Roo.get('loading').remove();
53046             }
53047             if (Roo.get('loading-mask')) {
53048                 Roo.get('loading-mask').hide();
53049             }
53050             
53051             //incomming._node = tnode;
53052             this.form.reset();
53053             //this.dialog.modal = !modal;
53054             //this.dialog.show();
53055             this.el.unmask(); 
53056             
53057             
53058             this.form.setValues({
53059                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53060                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53061             });
53062             
53063             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53064             if (this.form.findField('username').getValue().length > 0 ){
53065                 this.form.findField('password').focus();
53066             } else {
53067                this.form.findField('username').focus();
53068             }
53069     
53070         }
53071     },
53072     items : [
53073          {
53074        
53075             xtype : 'ContentPanel',
53076             xns : Roo,
53077             region: 'center',
53078             fitToFrame : true,
53079             
53080             items : [
53081     
53082                 {
53083                
53084                     xtype : 'Form',
53085                     xns : Roo.form,
53086                     labelWidth: 100,
53087                     style : 'margin: 10px;',
53088                     
53089                     listeners : {
53090                         actionfailed : function(f, act) {
53091                             // form can return { errors: .... }
53092                                 
53093                             //act.result.errors // invalid form element list...
53094                             //act.result.errorMsg// invalid form element list...
53095                             
53096                             this.dialog.el.unmask();
53097                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53098                                         "Login failed - communication error - try again.");
53099                                       
53100                         },
53101                         actioncomplete: function(re, act) {
53102                              
53103                             Roo.state.Manager.set(
53104                                 this.dialog.realm + '.username',  
53105                                     this.findField('username').getValue()
53106                             );
53107                             Roo.state.Manager.set(
53108                                 this.dialog.realm + '.lang',  
53109                                 this.findField('lang').getValue() 
53110                             );
53111                             
53112                             this.dialog.fillAuth(act.result.data);
53113                               
53114                             this.dialog.hide();
53115                             
53116                             if (Roo.get('loading-mask')) {
53117                                 Roo.get('loading-mask').show();
53118                             }
53119                             Roo.XComponent.build();
53120                             
53121                              
53122                             
53123                         }
53124                     },
53125                     items : [
53126                         {
53127                             xtype : 'TextField',
53128                             xns : Roo.form,
53129                             fieldLabel: "Email Address",
53130                             name: 'username',
53131                             width:200,
53132                             autoCreate : {tag: "input", type: "text", size: "20"}
53133                         },
53134                         {
53135                             xtype : 'TextField',
53136                             xns : Roo.form,
53137                             fieldLabel: "Password",
53138                             inputType: 'password',
53139                             name: 'password',
53140                             width:200,
53141                             autoCreate : {tag: "input", type: "text", size: "20"},
53142                             listeners : {
53143                                 specialkey : function(e,ev) {
53144                                     if (ev.keyCode == 13) {
53145                                         this.form.dialog.el.mask("Logging in");
53146                                         this.form.doAction('submit', {
53147                                             url: this.form.dialog.url,
53148                                             method: this.form.dialog.method
53149                                         });
53150                                     }
53151                                 }
53152                             }  
53153                         },
53154                         {
53155                             xtype : 'ComboBox',
53156                             xns : Roo.form,
53157                             fieldLabel: "Language",
53158                             name : 'langdisp',
53159                             store: {
53160                                 xtype : 'SimpleStore',
53161                                 fields: ['lang', 'ldisp'],
53162                                 data : [
53163                                     [ 'en', 'English' ],
53164                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53165                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53166                                 ]
53167                             },
53168                             
53169                             valueField : 'lang',
53170                             hiddenName:  'lang',
53171                             width: 200,
53172                             displayField:'ldisp',
53173                             typeAhead: false,
53174                             editable: false,
53175                             mode: 'local',
53176                             triggerAction: 'all',
53177                             emptyText:'Select a Language...',
53178                             selectOnFocus:true,
53179                             listeners : {
53180                                 select :  function(cb, rec, ix) {
53181                                     this.form.switchLang(rec.data.lang);
53182                                 }
53183                             }
53184                         
53185                         }
53186                     ]
53187                 }
53188                   
53189                 
53190             ]
53191         }
53192     ],
53193     buttons : [
53194         {
53195             xtype : 'Button',
53196             xns : 'Roo',
53197             text : "Forgot Password",
53198             listeners : {
53199                 click : function() {
53200                     //console.log(this);
53201                     var n = this.form.findField('username').getValue();
53202                     if (!n.length) {
53203                         Roo.MessageBox.alert("Error", "Fill in your email address");
53204                         return;
53205                     }
53206                     Roo.Ajax.request({
53207                         url: this.dialog.url,
53208                         params: {
53209                             passwordRequest: n
53210                         },
53211                         method: this.dialog.method,
53212                         success:  function(response, opts)  {  // check successfull...
53213                         
53214                             var res = this.dialog.processResponse(response);
53215                             if (!res.success) { // error!
53216                                Roo.MessageBox.alert("Error" ,
53217                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53218                                return;
53219                             }
53220                             Roo.MessageBox.alert("Notice" ,
53221                                 "Please check you email for the Password Reset message");
53222                         },
53223                         failure : function() {
53224                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53225                         }
53226                         
53227                     });
53228                 }
53229             }
53230         },
53231         {
53232             xtype : 'Button',
53233             xns : 'Roo',
53234             text : "Login",
53235             listeners : {
53236                 
53237                 click : function () {
53238                         
53239                     this.dialog.el.mask("Logging in");
53240                     this.form.doAction('submit', {
53241                             url: this.dialog.url,
53242                             method: this.dialog.method
53243                     });
53244                 }
53245             }
53246         }
53247     ]
53248   
53249   
53250 })
53251  
53252
53253
53254