roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls)
8864         {
8865             if(this.getStyle("position") == "static"){
8866                 this.setStyle("position", "relative");
8867             }
8868             if(!this._mask){
8869                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8870             }
8871             this.addClass("x-masked");
8872             this._mask.setDisplayed(true);
8873             
8874             // we wander
8875             var z = 0;
8876             var dom = this.dom
8877             while (dom && dom.style) {
8878                 if (!isNaN(parseInt(dom.style.zIndex))) {
8879                     z = Math.max(z, parseInt(dom.style.zIndex));
8880                 }
8881                 dom = dom.parentNode;
8882             }
8883             // if we are masking the body - then it hides everything..
8884             if (this.dom == document.body) {
8885                 z = 1000000;
8886                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8887                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8888             }
8889            
8890             if(typeof msg == 'string'){
8891                 if(!this._maskMsg){
8892                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8893                 }
8894                 var mm = this._maskMsg;
8895                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8896                 mm.dom.firstChild.innerHTML = msg;
8897                 mm.setDisplayed(true);
8898                 mm.center(this);
8899                 mm.setStyle('z-index', z + 102);
8900             }
8901             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8902                 this._mask.setHeight(this.getHeight());
8903             }
8904             this._mask.setStyle('z-index', z + 100);
8905             
8906             return this._mask;
8907         },
8908
8909         /**
8910          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8911          * it is cached for reuse.
8912          */
8913         unmask : function(removeEl){
8914             if(this._mask){
8915                 if(removeEl === true){
8916                     this._mask.remove();
8917                     delete this._mask;
8918                     if(this._maskMsg){
8919                         this._maskMsg.remove();
8920                         delete this._maskMsg;
8921                     }
8922                 }else{
8923                     this._mask.setDisplayed(false);
8924                     if(this._maskMsg){
8925                         this._maskMsg.setDisplayed(false);
8926                     }
8927                 }
8928             }
8929             this.removeClass("x-masked");
8930         },
8931
8932         /**
8933          * Returns true if this element is masked
8934          * @return {Boolean}
8935          */
8936         isMasked : function(){
8937             return this._mask && this._mask.isVisible();
8938         },
8939
8940         /**
8941          * Creates an iframe shim for this element to keep selects and other windowed objects from
8942          * showing through.
8943          * @return {Roo.Element} The new shim element
8944          */
8945         createShim : function(){
8946             var el = document.createElement('iframe');
8947             el.frameBorder = 'no';
8948             el.className = 'roo-shim';
8949             if(Roo.isIE && Roo.isSecure){
8950                 el.src = Roo.SSL_SECURE_URL;
8951             }
8952             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8953             shim.autoBoxAdjust = false;
8954             return shim;
8955         },
8956
8957         /**
8958          * Removes this element from the DOM and deletes it from the cache
8959          */
8960         remove : function(){
8961             if(this.dom.parentNode){
8962                 this.dom.parentNode.removeChild(this.dom);
8963             }
8964             delete El.cache[this.dom.id];
8965         },
8966
8967         /**
8968          * Sets up event handlers to add and remove a css class when the mouse is over this element
8969          * @param {String} className
8970          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8971          * mouseout events for children elements
8972          * @return {Roo.Element} this
8973          */
8974         addClassOnOver : function(className, preventFlicker){
8975             this.on("mouseover", function(){
8976                 Roo.fly(this, '_internal').addClass(className);
8977             }, this.dom);
8978             var removeFn = function(e){
8979                 if(preventFlicker !== true || !e.within(this, true)){
8980                     Roo.fly(this, '_internal').removeClass(className);
8981                 }
8982             };
8983             this.on("mouseout", removeFn, this.dom);
8984             return this;
8985         },
8986
8987         /**
8988          * Sets up event handlers to add and remove a css class when this element has the focus
8989          * @param {String} className
8990          * @return {Roo.Element} this
8991          */
8992         addClassOnFocus : function(className){
8993             this.on("focus", function(){
8994                 Roo.fly(this, '_internal').addClass(className);
8995             }, this.dom);
8996             this.on("blur", function(){
8997                 Roo.fly(this, '_internal').removeClass(className);
8998             }, this.dom);
8999             return this;
9000         },
9001         /**
9002          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9003          * @param {String} className
9004          * @return {Roo.Element} this
9005          */
9006         addClassOnClick : function(className){
9007             var dom = this.dom;
9008             this.on("mousedown", function(){
9009                 Roo.fly(dom, '_internal').addClass(className);
9010                 var d = Roo.get(document);
9011                 var fn = function(){
9012                     Roo.fly(dom, '_internal').removeClass(className);
9013                     d.removeListener("mouseup", fn);
9014                 };
9015                 d.on("mouseup", fn);
9016             });
9017             return this;
9018         },
9019
9020         /**
9021          * Stops the specified event from bubbling and optionally prevents the default action
9022          * @param {String} eventName
9023          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9024          * @return {Roo.Element} this
9025          */
9026         swallowEvent : function(eventName, preventDefault){
9027             var fn = function(e){
9028                 e.stopPropagation();
9029                 if(preventDefault){
9030                     e.preventDefault();
9031                 }
9032             };
9033             if(eventName instanceof Array){
9034                 for(var i = 0, len = eventName.length; i < len; i++){
9035                      this.on(eventName[i], fn);
9036                 }
9037                 return this;
9038             }
9039             this.on(eventName, fn);
9040             return this;
9041         },
9042
9043         /**
9044          * @private
9045          */
9046       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9047
9048         /**
9049          * Sizes this element to its parent element's dimensions performing
9050          * neccessary box adjustments.
9051          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9052          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9053          * @return {Roo.Element} this
9054          */
9055         fitToParent : function(monitorResize, targetParent) {
9056           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9057           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9058           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9059             return;
9060           }
9061           var p = Roo.get(targetParent || this.dom.parentNode);
9062           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9063           if (monitorResize === true) {
9064             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9065             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9066           }
9067           return this;
9068         },
9069
9070         /**
9071          * Gets the next sibling, skipping text nodes
9072          * @return {HTMLElement} The next sibling or null
9073          */
9074         getNextSibling : function(){
9075             var n = this.dom.nextSibling;
9076             while(n && n.nodeType != 1){
9077                 n = n.nextSibling;
9078             }
9079             return n;
9080         },
9081
9082         /**
9083          * Gets the previous sibling, skipping text nodes
9084          * @return {HTMLElement} The previous sibling or null
9085          */
9086         getPrevSibling : function(){
9087             var n = this.dom.previousSibling;
9088             while(n && n.nodeType != 1){
9089                 n = n.previousSibling;
9090             }
9091             return n;
9092         },
9093
9094
9095         /**
9096          * Appends the passed element(s) to this element
9097          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9098          * @return {Roo.Element} this
9099          */
9100         appendChild: function(el){
9101             el = Roo.get(el);
9102             el.appendTo(this);
9103             return this;
9104         },
9105
9106         /**
9107          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9108          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9109          * automatically generated with the specified attributes.
9110          * @param {HTMLElement} insertBefore (optional) a child element of this element
9111          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9112          * @return {Roo.Element} The new child element
9113          */
9114         createChild: function(config, insertBefore, returnDom){
9115             config = config || {tag:'div'};
9116             if(insertBefore){
9117                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9118             }
9119             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9120         },
9121
9122         /**
9123          * Appends this element to the passed element
9124          * @param {String/HTMLElement/Element} el The new parent element
9125          * @return {Roo.Element} this
9126          */
9127         appendTo: function(el){
9128             el = Roo.getDom(el);
9129             el.appendChild(this.dom);
9130             return this;
9131         },
9132
9133         /**
9134          * Inserts this element before the passed element in the DOM
9135          * @param {String/HTMLElement/Element} el The element to insert before
9136          * @return {Roo.Element} this
9137          */
9138         insertBefore: function(el){
9139             el = Roo.getDom(el);
9140             el.parentNode.insertBefore(this.dom, el);
9141             return this;
9142         },
9143
9144         /**
9145          * Inserts this element after the passed element in the DOM
9146          * @param {String/HTMLElement/Element} el The element to insert after
9147          * @return {Roo.Element} this
9148          */
9149         insertAfter: function(el){
9150             el = Roo.getDom(el);
9151             el.parentNode.insertBefore(this.dom, el.nextSibling);
9152             return this;
9153         },
9154
9155         /**
9156          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9157          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9158          * @return {Roo.Element} The new child
9159          */
9160         insertFirst: function(el, returnDom){
9161             el = el || {};
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 return this.createChild(el, this.dom.firstChild, returnDom);
9164             }else{
9165                 el = Roo.getDom(el);
9166                 this.dom.insertBefore(el, this.dom.firstChild);
9167                 return !returnDom ? Roo.get(el) : el;
9168             }
9169         },
9170
9171         /**
9172          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9173          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9174          * @param {String} where (optional) 'before' or 'after' defaults to before
9175          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9176          * @return {Roo.Element} the inserted Element
9177          */
9178         insertSibling: function(el, where, returnDom){
9179             where = where ? where.toLowerCase() : 'before';
9180             el = el || {};
9181             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9182
9183             if(typeof el == 'object' && !el.nodeType){ // dh config
9184                 if(where == 'after' && !this.dom.nextSibling){
9185                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9186                 }else{
9187                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9188                 }
9189
9190             }else{
9191                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9192                             where == 'before' ? this.dom : this.dom.nextSibling);
9193                 if(!returnDom){
9194                     rt = Roo.get(rt);
9195                 }
9196             }
9197             return rt;
9198         },
9199
9200         /**
9201          * Creates and wraps this element with another element
9202          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9203          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9204          * @return {HTMLElement/Element} The newly created wrapper element
9205          */
9206         wrap: function(config, returnDom){
9207             if(!config){
9208                 config = {tag: "div"};
9209             }
9210             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9211             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9212             return newEl;
9213         },
9214
9215         /**
9216          * Replaces the passed element with this element
9217          * @param {String/HTMLElement/Element} el The element to replace
9218          * @return {Roo.Element} this
9219          */
9220         replace: function(el){
9221             el = Roo.get(el);
9222             this.insertBefore(el);
9223             el.remove();
9224             return this;
9225         },
9226
9227         /**
9228          * Inserts an html fragment into this element
9229          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9230          * @param {String} html The HTML fragment
9231          * @param {Boolean} returnEl True to return an Roo.Element
9232          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9233          */
9234         insertHtml : function(where, html, returnEl){
9235             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9236             return returnEl ? Roo.get(el) : el;
9237         },
9238
9239         /**
9240          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9241          * @param {Object} o The object with the attributes
9242          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9243          * @return {Roo.Element} this
9244          */
9245         set : function(o, useSet){
9246             var el = this.dom;
9247             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9248             for(var attr in o){
9249                 if(attr == "style" || typeof o[attr] == "function") continue;
9250                 if(attr=="cls"){
9251                     el.className = o["cls"];
9252                 }else{
9253                     if(useSet) el.setAttribute(attr, o[attr]);
9254                     else el[attr] = o[attr];
9255                 }
9256             }
9257             if(o.style){
9258                 Roo.DomHelper.applyStyles(el, o.style);
9259             }
9260             return this;
9261         },
9262
9263         /**
9264          * Convenience method for constructing a KeyMap
9265          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9266          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9267          * @param {Function} fn The function to call
9268          * @param {Object} scope (optional) The scope of the function
9269          * @return {Roo.KeyMap} The KeyMap created
9270          */
9271         addKeyListener : function(key, fn, scope){
9272             var config;
9273             if(typeof key != "object" || key instanceof Array){
9274                 config = {
9275                     key: key,
9276                     fn: fn,
9277                     scope: scope
9278                 };
9279             }else{
9280                 config = {
9281                     key : key.key,
9282                     shift : key.shift,
9283                     ctrl : key.ctrl,
9284                     alt : key.alt,
9285                     fn: fn,
9286                     scope: scope
9287                 };
9288             }
9289             return new Roo.KeyMap(this, config);
9290         },
9291
9292         /**
9293          * Creates a KeyMap for this element
9294          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9295          * @return {Roo.KeyMap} The KeyMap created
9296          */
9297         addKeyMap : function(config){
9298             return new Roo.KeyMap(this, config);
9299         },
9300
9301         /**
9302          * Returns true if this element is scrollable.
9303          * @return {Boolean}
9304          */
9305          isScrollable : function(){
9306             var dom = this.dom;
9307             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9308         },
9309
9310         /**
9311          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9312          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9313          * @param {Number} value The new scroll value
9314          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9315          * @return {Element} this
9316          */
9317
9318         scrollTo : function(side, value, animate){
9319             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9320             if(!animate || !A){
9321                 this.dom[prop] = value;
9322             }else{
9323                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9324                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9325             }
9326             return this;
9327         },
9328
9329         /**
9330          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9331          * within this element's scrollable range.
9332          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9333          * @param {Number} distance How far to scroll the element in pixels
9334          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9335          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9336          * was scrolled as far as it could go.
9337          */
9338          scroll : function(direction, distance, animate){
9339              if(!this.isScrollable()){
9340                  return;
9341              }
9342              var el = this.dom;
9343              var l = el.scrollLeft, t = el.scrollTop;
9344              var w = el.scrollWidth, h = el.scrollHeight;
9345              var cw = el.clientWidth, ch = el.clientHeight;
9346              direction = direction.toLowerCase();
9347              var scrolled = false;
9348              var a = this.preanim(arguments, 2);
9349              switch(direction){
9350                  case "l":
9351                  case "left":
9352                      if(w - l > cw){
9353                          var v = Math.min(l + distance, w-cw);
9354                          this.scrollTo("left", v, a);
9355                          scrolled = true;
9356                      }
9357                      break;
9358                 case "r":
9359                 case "right":
9360                      if(l > 0){
9361                          var v = Math.max(l - distance, 0);
9362                          this.scrollTo("left", v, a);
9363                          scrolled = true;
9364                      }
9365                      break;
9366                 case "t":
9367                 case "top":
9368                 case "up":
9369                      if(t > 0){
9370                          var v = Math.max(t - distance, 0);
9371                          this.scrollTo("top", v, a);
9372                          scrolled = true;
9373                      }
9374                      break;
9375                 case "b":
9376                 case "bottom":
9377                 case "down":
9378                      if(h - t > ch){
9379                          var v = Math.min(t + distance, h-ch);
9380                          this.scrollTo("top", v, a);
9381                          scrolled = true;
9382                      }
9383                      break;
9384              }
9385              return scrolled;
9386         },
9387
9388         /**
9389          * Translates the passed page coordinates into left/top css values for this element
9390          * @param {Number/Array} x The page x or an array containing [x, y]
9391          * @param {Number} y The page y
9392          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9393          */
9394         translatePoints : function(x, y){
9395             if(typeof x == 'object' || x instanceof Array){
9396                 y = x[1]; x = x[0];
9397             }
9398             var p = this.getStyle('position');
9399             var o = this.getXY();
9400
9401             var l = parseInt(this.getStyle('left'), 10);
9402             var t = parseInt(this.getStyle('top'), 10);
9403
9404             if(isNaN(l)){
9405                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9406             }
9407             if(isNaN(t)){
9408                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9409             }
9410
9411             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9412         },
9413
9414         /**
9415          * Returns the current scroll position of the element.
9416          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9417          */
9418         getScroll : function(){
9419             var d = this.dom, doc = document;
9420             if(d == doc || d == doc.body){
9421                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9422                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9423                 return {left: l, top: t};
9424             }else{
9425                 return {left: d.scrollLeft, top: d.scrollTop};
9426             }
9427         },
9428
9429         /**
9430          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9431          * are convert to standard 6 digit hex color.
9432          * @param {String} attr The css attribute
9433          * @param {String} defaultValue The default value to use when a valid color isn't found
9434          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9435          * YUI color anims.
9436          */
9437         getColor : function(attr, defaultValue, prefix){
9438             var v = this.getStyle(attr);
9439             if(!v || v == "transparent" || v == "inherit") {
9440                 return defaultValue;
9441             }
9442             var color = typeof prefix == "undefined" ? "#" : prefix;
9443             if(v.substr(0, 4) == "rgb("){
9444                 var rvs = v.slice(4, v.length -1).split(",");
9445                 for(var i = 0; i < 3; i++){
9446                     var h = parseInt(rvs[i]).toString(16);
9447                     if(h < 16){
9448                         h = "0" + h;
9449                     }
9450                     color += h;
9451                 }
9452             } else {
9453                 if(v.substr(0, 1) == "#"){
9454                     if(v.length == 4) {
9455                         for(var i = 1; i < 4; i++){
9456                             var c = v.charAt(i);
9457                             color +=  c + c;
9458                         }
9459                     }else if(v.length == 7){
9460                         color += v.substr(1);
9461                     }
9462                 }
9463             }
9464             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9465         },
9466
9467         /**
9468          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9469          * gradient background, rounded corners and a 4-way shadow.
9470          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9471          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9472          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9473          * @return {Roo.Element} this
9474          */
9475         boxWrap : function(cls){
9476             cls = cls || 'x-box';
9477             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9478             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9479             return el;
9480         },
9481
9482         /**
9483          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9484          * @param {String} namespace The namespace in which to look for the attribute
9485          * @param {String} name The attribute name
9486          * @return {String} The attribute value
9487          */
9488         getAttributeNS : Roo.isIE ? function(ns, name){
9489             var d = this.dom;
9490             var type = typeof d[ns+":"+name];
9491             if(type != 'undefined' && type != 'unknown'){
9492                 return d[ns+":"+name];
9493             }
9494             return d[name];
9495         } : function(ns, name){
9496             var d = this.dom;
9497             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9498         }
9499     };
9500
9501     var ep = El.prototype;
9502
9503     /**
9504      * Appends an event handler (Shorthand for addListener)
9505      * @param {String}   eventName     The type of event to append
9506      * @param {Function} fn        The method the event invokes
9507      * @param {Object} scope       (optional) The scope (this object) of the fn
9508      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9509      * @method
9510      */
9511     ep.on = ep.addListener;
9512         // backwards compat
9513     ep.mon = ep.addListener;
9514
9515     /**
9516      * Removes an event handler from this element (shorthand for removeListener)
9517      * @param {String} eventName the type of event to remove
9518      * @param {Function} fn the method the event invokes
9519      * @return {Roo.Element} this
9520      * @method
9521      */
9522     ep.un = ep.removeListener;
9523
9524     /**
9525      * true to automatically adjust width and height settings for box-model issues (default to true)
9526      */
9527     ep.autoBoxAdjust = true;
9528
9529     // private
9530     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9531
9532     // private
9533     El.addUnits = function(v, defaultUnit){
9534         if(v === "" || v == "auto"){
9535             return v;
9536         }
9537         if(v === undefined){
9538             return '';
9539         }
9540         if(typeof v == "number" || !El.unitPattern.test(v)){
9541             return v + (defaultUnit || 'px');
9542         }
9543         return v;
9544     };
9545
9546     // special markup used throughout Roo when box wrapping elements
9547     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9548     /**
9549      * Visibility mode constant - Use visibility to hide element
9550      * @static
9551      * @type Number
9552      */
9553     El.VISIBILITY = 1;
9554     /**
9555      * Visibility mode constant - Use display to hide element
9556      * @static
9557      * @type Number
9558      */
9559     El.DISPLAY = 2;
9560
9561     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9562     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9563     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9564
9565
9566
9567     /**
9568      * @private
9569      */
9570     El.cache = {};
9571
9572     var docEl;
9573
9574     /**
9575      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9576      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9577      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9578      * @return {Element} The Element object
9579      * @static
9580      */
9581     El.get = function(el){
9582         var ex, elm, id;
9583         if(!el){ return null; }
9584         if(typeof el == "string"){ // element id
9585             if(!(elm = document.getElementById(el))){
9586                 return null;
9587             }
9588             if(ex = El.cache[el]){
9589                 ex.dom = elm;
9590             }else{
9591                 ex = El.cache[el] = new El(elm);
9592             }
9593             return ex;
9594         }else if(el.tagName){ // dom element
9595             if(!(id = el.id)){
9596                 id = Roo.id(el);
9597             }
9598             if(ex = El.cache[id]){
9599                 ex.dom = el;
9600             }else{
9601                 ex = El.cache[id] = new El(el);
9602             }
9603             return ex;
9604         }else if(el instanceof El){
9605             if(el != docEl){
9606                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9607                                                               // catch case where it hasn't been appended
9608                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9609             }
9610             return el;
9611         }else if(el.isComposite){
9612             return el;
9613         }else if(el instanceof Array){
9614             return El.select(el);
9615         }else if(el == document){
9616             // create a bogus element object representing the document object
9617             if(!docEl){
9618                 var f = function(){};
9619                 f.prototype = El.prototype;
9620                 docEl = new f();
9621                 docEl.dom = document;
9622             }
9623             return docEl;
9624         }
9625         return null;
9626     };
9627
9628     // private
9629     El.uncache = function(el){
9630         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9631             if(a[i]){
9632                 delete El.cache[a[i].id || a[i]];
9633             }
9634         }
9635     };
9636
9637     // private
9638     // Garbage collection - uncache elements/purge listeners on orphaned elements
9639     // so we don't hold a reference and cause the browser to retain them
9640     El.garbageCollect = function(){
9641         if(!Roo.enableGarbageCollector){
9642             clearInterval(El.collectorThread);
9643             return;
9644         }
9645         for(var eid in El.cache){
9646             var el = El.cache[eid], d = el.dom;
9647             // -------------------------------------------------------
9648             // Determining what is garbage:
9649             // -------------------------------------------------------
9650             // !d
9651             // dom node is null, definitely garbage
9652             // -------------------------------------------------------
9653             // !d.parentNode
9654             // no parentNode == direct orphan, definitely garbage
9655             // -------------------------------------------------------
9656             // !d.offsetParent && !document.getElementById(eid)
9657             // display none elements have no offsetParent so we will
9658             // also try to look it up by it's id. However, check
9659             // offsetParent first so we don't do unneeded lookups.
9660             // This enables collection of elements that are not orphans
9661             // directly, but somewhere up the line they have an orphan
9662             // parent.
9663             // -------------------------------------------------------
9664             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9665                 delete El.cache[eid];
9666                 if(d && Roo.enableListenerCollection){
9667                     E.purgeElement(d);
9668                 }
9669             }
9670         }
9671     }
9672     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9673
9674
9675     // dom is optional
9676     El.Flyweight = function(dom){
9677         this.dom = dom;
9678     };
9679     El.Flyweight.prototype = El.prototype;
9680
9681     El._flyweights = {};
9682     /**
9683      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9684      * the dom node can be overwritten by other code.
9685      * @param {String/HTMLElement} el The dom node or id
9686      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9687      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9688      * @static
9689      * @return {Element} The shared Element object
9690      */
9691     El.fly = function(el, named){
9692         named = named || '_global';
9693         el = Roo.getDom(el);
9694         if(!el){
9695             return null;
9696         }
9697         if(!El._flyweights[named]){
9698             El._flyweights[named] = new El.Flyweight();
9699         }
9700         El._flyweights[named].dom = el;
9701         return El._flyweights[named];
9702     };
9703
9704     /**
9705      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9706      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9707      * Shorthand of {@link Roo.Element#get}
9708      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9709      * @return {Element} The Element object
9710      * @member Roo
9711      * @method get
9712      */
9713     Roo.get = El.get;
9714     /**
9715      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9716      * the dom node can be overwritten by other code.
9717      * Shorthand of {@link Roo.Element#fly}
9718      * @param {String/HTMLElement} el The dom node or id
9719      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9720      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9721      * @static
9722      * @return {Element} The shared Element object
9723      * @member Roo
9724      * @method fly
9725      */
9726     Roo.fly = El.fly;
9727
9728     // speedy lookup for elements never to box adjust
9729     var noBoxAdjust = Roo.isStrict ? {
9730         select:1
9731     } : {
9732         input:1, select:1, textarea:1
9733     };
9734     if(Roo.isIE || Roo.isGecko){
9735         noBoxAdjust['button'] = 1;
9736     }
9737
9738
9739     Roo.EventManager.on(window, 'unload', function(){
9740         delete El.cache;
9741         delete El._flyweights;
9742     });
9743 })();
9744
9745
9746
9747
9748 if(Roo.DomQuery){
9749     Roo.Element.selectorFunction = Roo.DomQuery.select;
9750 }
9751
9752 Roo.Element.select = function(selector, unique, root){
9753     var els;
9754     if(typeof selector == "string"){
9755         els = Roo.Element.selectorFunction(selector, root);
9756     }else if(selector.length !== undefined){
9757         els = selector;
9758     }else{
9759         throw "Invalid selector";
9760     }
9761     if(unique === true){
9762         return new Roo.CompositeElement(els);
9763     }else{
9764         return new Roo.CompositeElementLite(els);
9765     }
9766 };
9767 /**
9768  * Selects elements based on the passed CSS selector to enable working on them as 1.
9769  * @param {String/Array} selector The CSS selector or an array of elements
9770  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9771  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9772  * @return {CompositeElementLite/CompositeElement}
9773  * @member Roo
9774  * @method select
9775  */
9776 Roo.select = Roo.Element.select;
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791 /*
9792  * Based on:
9793  * Ext JS Library 1.1.1
9794  * Copyright(c) 2006-2007, Ext JS, LLC.
9795  *
9796  * Originally Released Under LGPL - original licence link has changed is not relivant.
9797  *
9798  * Fork - LGPL
9799  * <script type="text/javascript">
9800  */
9801
9802
9803
9804 //Notifies Element that fx methods are available
9805 Roo.enableFx = true;
9806
9807 /**
9808  * @class Roo.Fx
9809  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9810  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9811  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9812  * Element effects to work.</p><br/>
9813  *
9814  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9815  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9816  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9817  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9818  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9819  * expected results and should be done with care.</p><br/>
9820  *
9821  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9822  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9823 <pre>
9824 Value  Description
9825 -----  -----------------------------
9826 tl     The top left corner
9827 t      The center of the top edge
9828 tr     The top right corner
9829 l      The center of the left edge
9830 r      The center of the right edge
9831 bl     The bottom left corner
9832 b      The center of the bottom edge
9833 br     The bottom right corner
9834 </pre>
9835  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9836  * below are common options that can be passed to any Fx method.</b>
9837  * @cfg {Function} callback A function called when the effect is finished
9838  * @cfg {Object} scope The scope of the effect function
9839  * @cfg {String} easing A valid Easing value for the effect
9840  * @cfg {String} afterCls A css class to apply after the effect
9841  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9842  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9843  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9844  * effects that end with the element being visually hidden, ignored otherwise)
9845  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9846  * a function which returns such a specification that will be applied to the Element after the effect finishes
9847  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9848  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9849  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9850  */
9851 Roo.Fx = {
9852         /**
9853          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9854          * origin for the slide effect.  This function automatically handles wrapping the element with
9855          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9856          * Usage:
9857          *<pre><code>
9858 // default: slide the element in from the top
9859 el.slideIn();
9860
9861 // custom: slide the element in from the right with a 2-second duration
9862 el.slideIn('r', { duration: 2 });
9863
9864 // common config options shown with default values
9865 el.slideIn('t', {
9866     easing: 'easeOut',
9867     duration: .5
9868 });
9869 </code></pre>
9870          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9871          * @param {Object} options (optional) Object literal with any of the Fx config options
9872          * @return {Roo.Element} The Element
9873          */
9874     slideIn : function(anchor, o){
9875         var el = this.getFxEl();
9876         o = o || {};
9877
9878         el.queueFx(o, function(){
9879
9880             anchor = anchor || "t";
9881
9882             // fix display to visibility
9883             this.fixDisplay();
9884
9885             // restore values after effect
9886             var r = this.getFxRestore();
9887             var b = this.getBox();
9888             // fixed size for slide
9889             this.setSize(b);
9890
9891             // wrap if needed
9892             var wrap = this.fxWrap(r.pos, o, "hidden");
9893
9894             var st = this.dom.style;
9895             st.visibility = "visible";
9896             st.position = "absolute";
9897
9898             // clear out temp styles after slide and unwrap
9899             var after = function(){
9900                 el.fxUnwrap(wrap, r.pos, o);
9901                 st.width = r.width;
9902                 st.height = r.height;
9903                 el.afterFx(o);
9904             };
9905             // time to calc the positions
9906             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9907
9908             switch(anchor.toLowerCase()){
9909                 case "t":
9910                     wrap.setSize(b.width, 0);
9911                     st.left = st.bottom = "0";
9912                     a = {height: bh};
9913                 break;
9914                 case "l":
9915                     wrap.setSize(0, b.height);
9916                     st.right = st.top = "0";
9917                     a = {width: bw};
9918                 break;
9919                 case "r":
9920                     wrap.setSize(0, b.height);
9921                     wrap.setX(b.right);
9922                     st.left = st.top = "0";
9923                     a = {width: bw, points: pt};
9924                 break;
9925                 case "b":
9926                     wrap.setSize(b.width, 0);
9927                     wrap.setY(b.bottom);
9928                     st.left = st.top = "0";
9929                     a = {height: bh, points: pt};
9930                 break;
9931                 case "tl":
9932                     wrap.setSize(0, 0);
9933                     st.right = st.bottom = "0";
9934                     a = {width: bw, height: bh};
9935                 break;
9936                 case "bl":
9937                     wrap.setSize(0, 0);
9938                     wrap.setY(b.y+b.height);
9939                     st.right = st.top = "0";
9940                     a = {width: bw, height: bh, points: pt};
9941                 break;
9942                 case "br":
9943                     wrap.setSize(0, 0);
9944                     wrap.setXY([b.right, b.bottom]);
9945                     st.left = st.top = "0";
9946                     a = {width: bw, height: bh, points: pt};
9947                 break;
9948                 case "tr":
9949                     wrap.setSize(0, 0);
9950                     wrap.setX(b.x+b.width);
9951                     st.left = st.bottom = "0";
9952                     a = {width: bw, height: bh, points: pt};
9953                 break;
9954             }
9955             this.dom.style.visibility = "visible";
9956             wrap.show();
9957
9958             arguments.callee.anim = wrap.fxanim(a,
9959                 o,
9960                 'motion',
9961                 .5,
9962                 'easeOut', after);
9963         });
9964         return this;
9965     },
9966     
9967         /**
9968          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9969          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9970          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9971          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9972          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9973          * Usage:
9974          *<pre><code>
9975 // default: slide the element out to the top
9976 el.slideOut();
9977
9978 // custom: slide the element out to the right with a 2-second duration
9979 el.slideOut('r', { duration: 2 });
9980
9981 // common config options shown with default values
9982 el.slideOut('t', {
9983     easing: 'easeOut',
9984     duration: .5,
9985     remove: false,
9986     useDisplay: false
9987 });
9988 </code></pre>
9989          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9990          * @param {Object} options (optional) Object literal with any of the Fx config options
9991          * @return {Roo.Element} The Element
9992          */
9993     slideOut : function(anchor, o){
9994         var el = this.getFxEl();
9995         o = o || {};
9996
9997         el.queueFx(o, function(){
9998
9999             anchor = anchor || "t";
10000
10001             // restore values after effect
10002             var r = this.getFxRestore();
10003             
10004             var b = this.getBox();
10005             // fixed size for slide
10006             this.setSize(b);
10007
10008             // wrap if needed
10009             var wrap = this.fxWrap(r.pos, o, "visible");
10010
10011             var st = this.dom.style;
10012             st.visibility = "visible";
10013             st.position = "absolute";
10014
10015             wrap.setSize(b);
10016
10017             var after = function(){
10018                 if(o.useDisplay){
10019                     el.setDisplayed(false);
10020                 }else{
10021                     el.hide();
10022                 }
10023
10024                 el.fxUnwrap(wrap, r.pos, o);
10025
10026                 st.width = r.width;
10027                 st.height = r.height;
10028
10029                 el.afterFx(o);
10030             };
10031
10032             var a, zero = {to: 0};
10033             switch(anchor.toLowerCase()){
10034                 case "t":
10035                     st.left = st.bottom = "0";
10036                     a = {height: zero};
10037                 break;
10038                 case "l":
10039                     st.right = st.top = "0";
10040                     a = {width: zero};
10041                 break;
10042                 case "r":
10043                     st.left = st.top = "0";
10044                     a = {width: zero, points: {to:[b.right, b.y]}};
10045                 break;
10046                 case "b":
10047                     st.left = st.top = "0";
10048                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10049                 break;
10050                 case "tl":
10051                     st.right = st.bottom = "0";
10052                     a = {width: zero, height: zero};
10053                 break;
10054                 case "bl":
10055                     st.right = st.top = "0";
10056                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10057                 break;
10058                 case "br":
10059                     st.left = st.top = "0";
10060                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10061                 break;
10062                 case "tr":
10063                     st.left = st.bottom = "0";
10064                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10065                 break;
10066             }
10067
10068             arguments.callee.anim = wrap.fxanim(a,
10069                 o,
10070                 'motion',
10071                 .5,
10072                 "easeOut", after);
10073         });
10074         return this;
10075     },
10076
10077         /**
10078          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10079          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10080          * The element must be removed from the DOM using the 'remove' config option if desired.
10081          * Usage:
10082          *<pre><code>
10083 // default
10084 el.puff();
10085
10086 // common config options shown with default values
10087 el.puff({
10088     easing: 'easeOut',
10089     duration: .5,
10090     remove: false,
10091     useDisplay: false
10092 });
10093 </code></pre>
10094          * @param {Object} options (optional) Object literal with any of the Fx config options
10095          * @return {Roo.Element} The Element
10096          */
10097     puff : function(o){
10098         var el = this.getFxEl();
10099         o = o || {};
10100
10101         el.queueFx(o, function(){
10102             this.clearOpacity();
10103             this.show();
10104
10105             // restore values after effect
10106             var r = this.getFxRestore();
10107             var st = this.dom.style;
10108
10109             var after = function(){
10110                 if(o.useDisplay){
10111                     el.setDisplayed(false);
10112                 }else{
10113                     el.hide();
10114                 }
10115
10116                 el.clearOpacity();
10117
10118                 el.setPositioning(r.pos);
10119                 st.width = r.width;
10120                 st.height = r.height;
10121                 st.fontSize = '';
10122                 el.afterFx(o);
10123             };
10124
10125             var width = this.getWidth();
10126             var height = this.getHeight();
10127
10128             arguments.callee.anim = this.fxanim({
10129                     width : {to: this.adjustWidth(width * 2)},
10130                     height : {to: this.adjustHeight(height * 2)},
10131                     points : {by: [-(width * .5), -(height * .5)]},
10132                     opacity : {to: 0},
10133                     fontSize: {to:200, unit: "%"}
10134                 },
10135                 o,
10136                 'motion',
10137                 .5,
10138                 "easeOut", after);
10139         });
10140         return this;
10141     },
10142
10143         /**
10144          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10145          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10146          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10147          * Usage:
10148          *<pre><code>
10149 // default
10150 el.switchOff();
10151
10152 // all config options shown with default values
10153 el.switchOff({
10154     easing: 'easeIn',
10155     duration: .3,
10156     remove: false,
10157     useDisplay: false
10158 });
10159 </code></pre>
10160          * @param {Object} options (optional) Object literal with any of the Fx config options
10161          * @return {Roo.Element} The Element
10162          */
10163     switchOff : function(o){
10164         var el = this.getFxEl();
10165         o = o || {};
10166
10167         el.queueFx(o, function(){
10168             this.clearOpacity();
10169             this.clip();
10170
10171             // restore values after effect
10172             var r = this.getFxRestore();
10173             var st = this.dom.style;
10174
10175             var after = function(){
10176                 if(o.useDisplay){
10177                     el.setDisplayed(false);
10178                 }else{
10179                     el.hide();
10180                 }
10181
10182                 el.clearOpacity();
10183                 el.setPositioning(r.pos);
10184                 st.width = r.width;
10185                 st.height = r.height;
10186
10187                 el.afterFx(o);
10188             };
10189
10190             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10191                 this.clearOpacity();
10192                 (function(){
10193                     this.fxanim({
10194                         height:{to:1},
10195                         points:{by:[0, this.getHeight() * .5]}
10196                     }, o, 'motion', 0.3, 'easeIn', after);
10197                 }).defer(100, this);
10198             });
10199         });
10200         return this;
10201     },
10202
10203     /**
10204      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10205      * changed using the "attr" config option) and then fading back to the original color. If no original
10206      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10207      * Usage:
10208 <pre><code>
10209 // default: highlight background to yellow
10210 el.highlight();
10211
10212 // custom: highlight foreground text to blue for 2 seconds
10213 el.highlight("0000ff", { attr: 'color', duration: 2 });
10214
10215 // common config options shown with default values
10216 el.highlight("ffff9c", {
10217     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10218     endColor: (current color) or "ffffff",
10219     easing: 'easeIn',
10220     duration: 1
10221 });
10222 </code></pre>
10223      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10224      * @param {Object} options (optional) Object literal with any of the Fx config options
10225      * @return {Roo.Element} The Element
10226      */ 
10227     highlight : function(color, o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230
10231         el.queueFx(o, function(){
10232             color = color || "ffff9c";
10233             attr = o.attr || "backgroundColor";
10234
10235             this.clearOpacity();
10236             this.show();
10237
10238             var origColor = this.getColor(attr);
10239             var restoreColor = this.dom.style[attr];
10240             endColor = (o.endColor || origColor) || "ffffff";
10241
10242             var after = function(){
10243                 el.dom.style[attr] = restoreColor;
10244                 el.afterFx(o);
10245             };
10246
10247             var a = {};
10248             a[attr] = {from: color, to: endColor};
10249             arguments.callee.anim = this.fxanim(a,
10250                 o,
10251                 'color',
10252                 1,
10253                 'easeIn', after);
10254         });
10255         return this;
10256     },
10257
10258    /**
10259     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10260     * Usage:
10261 <pre><code>
10262 // default: a single light blue ripple
10263 el.frame();
10264
10265 // custom: 3 red ripples lasting 3 seconds total
10266 el.frame("ff0000", 3, { duration: 3 });
10267
10268 // common config options shown with default values
10269 el.frame("C3DAF9", 1, {
10270     duration: 1 //duration of entire animation (not each individual ripple)
10271     // Note: Easing is not configurable and will be ignored if included
10272 });
10273 </code></pre>
10274     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10275     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10276     * @param {Object} options (optional) Object literal with any of the Fx config options
10277     * @return {Roo.Element} The Element
10278     */
10279     frame : function(color, count, o){
10280         var el = this.getFxEl();
10281         o = o || {};
10282
10283         el.queueFx(o, function(){
10284             color = color || "#C3DAF9";
10285             if(color.length == 6){
10286                 color = "#" + color;
10287             }
10288             count = count || 1;
10289             duration = o.duration || 1;
10290             this.show();
10291
10292             var b = this.getBox();
10293             var animFn = function(){
10294                 var proxy = this.createProxy({
10295
10296                      style:{
10297                         visbility:"hidden",
10298                         position:"absolute",
10299                         "z-index":"35000", // yee haw
10300                         border:"0px solid " + color
10301                      }
10302                   });
10303                 var scale = Roo.isBorderBox ? 2 : 1;
10304                 proxy.animate({
10305                     top:{from:b.y, to:b.y - 20},
10306                     left:{from:b.x, to:b.x - 20},
10307                     borderWidth:{from:0, to:10},
10308                     opacity:{from:1, to:0},
10309                     height:{from:b.height, to:(b.height + (20*scale))},
10310                     width:{from:b.width, to:(b.width + (20*scale))}
10311                 }, duration, function(){
10312                     proxy.remove();
10313                 });
10314                 if(--count > 0){
10315                      animFn.defer((duration/2)*1000, this);
10316                 }else{
10317                     el.afterFx(o);
10318                 }
10319             };
10320             animFn.call(this);
10321         });
10322         return this;
10323     },
10324
10325    /**
10326     * Creates a pause before any subsequent queued effects begin.  If there are
10327     * no effects queued after the pause it will have no effect.
10328     * Usage:
10329 <pre><code>
10330 el.pause(1);
10331 </code></pre>
10332     * @param {Number} seconds The length of time to pause (in seconds)
10333     * @return {Roo.Element} The Element
10334     */
10335     pause : function(seconds){
10336         var el = this.getFxEl();
10337         var o = {};
10338
10339         el.queueFx(o, function(){
10340             setTimeout(function(){
10341                 el.afterFx(o);
10342             }, seconds * 1000);
10343         });
10344         return this;
10345     },
10346
10347    /**
10348     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10349     * using the "endOpacity" config option.
10350     * Usage:
10351 <pre><code>
10352 // default: fade in from opacity 0 to 100%
10353 el.fadeIn();
10354
10355 // custom: fade in from opacity 0 to 75% over 2 seconds
10356 el.fadeIn({ endOpacity: .75, duration: 2});
10357
10358 // common config options shown with default values
10359 el.fadeIn({
10360     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10361     easing: 'easeOut',
10362     duration: .5
10363 });
10364 </code></pre>
10365     * @param {Object} options (optional) Object literal with any of the Fx config options
10366     * @return {Roo.Element} The Element
10367     */
10368     fadeIn : function(o){
10369         var el = this.getFxEl();
10370         o = o || {};
10371         el.queueFx(o, function(){
10372             this.setOpacity(0);
10373             this.fixDisplay();
10374             this.dom.style.visibility = 'visible';
10375             var to = o.endOpacity || 1;
10376             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10377                 o, null, .5, "easeOut", function(){
10378                 if(to == 1){
10379                     this.clearOpacity();
10380                 }
10381                 el.afterFx(o);
10382             });
10383         });
10384         return this;
10385     },
10386
10387    /**
10388     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10389     * using the "endOpacity" config option.
10390     * Usage:
10391 <pre><code>
10392 // default: fade out from the element's current opacity to 0
10393 el.fadeOut();
10394
10395 // custom: fade out from the element's current opacity to 25% over 2 seconds
10396 el.fadeOut({ endOpacity: .25, duration: 2});
10397
10398 // common config options shown with default values
10399 el.fadeOut({
10400     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10401     easing: 'easeOut',
10402     duration: .5
10403     remove: false,
10404     useDisplay: false
10405 });
10406 </code></pre>
10407     * @param {Object} options (optional) Object literal with any of the Fx config options
10408     * @return {Roo.Element} The Element
10409     */
10410     fadeOut : function(o){
10411         var el = this.getFxEl();
10412         o = o || {};
10413         el.queueFx(o, function(){
10414             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10415                 o, null, .5, "easeOut", function(){
10416                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10417                      this.dom.style.display = "none";
10418                 }else{
10419                      this.dom.style.visibility = "hidden";
10420                 }
10421                 this.clearOpacity();
10422                 el.afterFx(o);
10423             });
10424         });
10425         return this;
10426     },
10427
10428    /**
10429     * Animates the transition of an element's dimensions from a starting height/width
10430     * to an ending height/width.
10431     * Usage:
10432 <pre><code>
10433 // change height and width to 100x100 pixels
10434 el.scale(100, 100);
10435
10436 // common config options shown with default values.  The height and width will default to
10437 // the element's existing values if passed as null.
10438 el.scale(
10439     [element's width],
10440     [element's height], {
10441     easing: 'easeOut',
10442     duration: .35
10443 });
10444 </code></pre>
10445     * @param {Number} width  The new width (pass undefined to keep the original width)
10446     * @param {Number} height  The new height (pass undefined to keep the original height)
10447     * @param {Object} options (optional) Object literal with any of the Fx config options
10448     * @return {Roo.Element} The Element
10449     */
10450     scale : function(w, h, o){
10451         this.shift(Roo.apply({}, o, {
10452             width: w,
10453             height: h
10454         }));
10455         return this;
10456     },
10457
10458    /**
10459     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10460     * Any of these properties not specified in the config object will not be changed.  This effect 
10461     * requires that at least one new dimension, position or opacity setting must be passed in on
10462     * the config object in order for the function to have any effect.
10463     * Usage:
10464 <pre><code>
10465 // slide the element horizontally to x position 200 while changing the height and opacity
10466 el.shift({ x: 200, height: 50, opacity: .8 });
10467
10468 // common config options shown with default values.
10469 el.shift({
10470     width: [element's width],
10471     height: [element's height],
10472     x: [element's x position],
10473     y: [element's y position],
10474     opacity: [element's opacity],
10475     easing: 'easeOut',
10476     duration: .35
10477 });
10478 </code></pre>
10479     * @param {Object} options  Object literal with any of the Fx config options
10480     * @return {Roo.Element} The Element
10481     */
10482     shift : function(o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485         el.queueFx(o, function(){
10486             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10487             if(w !== undefined){
10488                 a.width = {to: this.adjustWidth(w)};
10489             }
10490             if(h !== undefined){
10491                 a.height = {to: this.adjustHeight(h)};
10492             }
10493             if(x !== undefined || y !== undefined){
10494                 a.points = {to: [
10495                     x !== undefined ? x : this.getX(),
10496                     y !== undefined ? y : this.getY()
10497                 ]};
10498             }
10499             if(op !== undefined){
10500                 a.opacity = {to: op};
10501             }
10502             if(o.xy !== undefined){
10503                 a.points = {to: o.xy};
10504             }
10505             arguments.callee.anim = this.fxanim(a,
10506                 o, 'motion', .35, "easeOut", function(){
10507                 el.afterFx(o);
10508             });
10509         });
10510         return this;
10511     },
10512
10513         /**
10514          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10515          * ending point of the effect.
10516          * Usage:
10517          *<pre><code>
10518 // default: slide the element downward while fading out
10519 el.ghost();
10520
10521 // custom: slide the element out to the right with a 2-second duration
10522 el.ghost('r', { duration: 2 });
10523
10524 // common config options shown with default values
10525 el.ghost('b', {
10526     easing: 'easeOut',
10527     duration: .5
10528     remove: false,
10529     useDisplay: false
10530 });
10531 </code></pre>
10532          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10533          * @param {Object} options (optional) Object literal with any of the Fx config options
10534          * @return {Roo.Element} The Element
10535          */
10536     ghost : function(anchor, o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539
10540         el.queueFx(o, function(){
10541             anchor = anchor || "b";
10542
10543             // restore values after effect
10544             var r = this.getFxRestore();
10545             var w = this.getWidth(),
10546                 h = this.getHeight();
10547
10548             var st = this.dom.style;
10549
10550             var after = function(){
10551                 if(o.useDisplay){
10552                     el.setDisplayed(false);
10553                 }else{
10554                     el.hide();
10555                 }
10556
10557                 el.clearOpacity();
10558                 el.setPositioning(r.pos);
10559                 st.width = r.width;
10560                 st.height = r.height;
10561
10562                 el.afterFx(o);
10563             };
10564
10565             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10566             switch(anchor.toLowerCase()){
10567                 case "t":
10568                     pt.by = [0, -h];
10569                 break;
10570                 case "l":
10571                     pt.by = [-w, 0];
10572                 break;
10573                 case "r":
10574                     pt.by = [w, 0];
10575                 break;
10576                 case "b":
10577                     pt.by = [0, h];
10578                 break;
10579                 case "tl":
10580                     pt.by = [-w, -h];
10581                 break;
10582                 case "bl":
10583                     pt.by = [-w, h];
10584                 break;
10585                 case "br":
10586                     pt.by = [w, h];
10587                 break;
10588                 case "tr":
10589                     pt.by = [w, -h];
10590                 break;
10591             }
10592
10593             arguments.callee.anim = this.fxanim(a,
10594                 o,
10595                 'motion',
10596                 .5,
10597                 "easeOut", after);
10598         });
10599         return this;
10600     },
10601
10602         /**
10603          * Ensures that all effects queued after syncFx is called on the element are
10604          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10605          * @return {Roo.Element} The Element
10606          */
10607     syncFx : function(){
10608         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10609             block : false,
10610             concurrent : true,
10611             stopFx : false
10612         });
10613         return this;
10614     },
10615
10616         /**
10617          * Ensures that all effects queued after sequenceFx is called on the element are
10618          * run in sequence.  This is the opposite of {@link #syncFx}.
10619          * @return {Roo.Element} The Element
10620          */
10621     sequenceFx : function(){
10622         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10623             block : false,
10624             concurrent : false,
10625             stopFx : false
10626         });
10627         return this;
10628     },
10629
10630         /* @private */
10631     nextFx : function(){
10632         var ef = this.fxQueue[0];
10633         if(ef){
10634             ef.call(this);
10635         }
10636     },
10637
10638         /**
10639          * Returns true if the element has any effects actively running or queued, else returns false.
10640          * @return {Boolean} True if element has active effects, else false
10641          */
10642     hasActiveFx : function(){
10643         return this.fxQueue && this.fxQueue[0];
10644     },
10645
10646         /**
10647          * Stops any running effects and clears the element's internal effects queue if it contains
10648          * any additional effects that haven't started yet.
10649          * @return {Roo.Element} The Element
10650          */
10651     stopFx : function(){
10652         if(this.hasActiveFx()){
10653             var cur = this.fxQueue[0];
10654             if(cur && cur.anim && cur.anim.isAnimated()){
10655                 this.fxQueue = [cur]; // clear out others
10656                 cur.anim.stop(true);
10657             }
10658         }
10659         return this;
10660     },
10661
10662         /* @private */
10663     beforeFx : function(o){
10664         if(this.hasActiveFx() && !o.concurrent){
10665            if(o.stopFx){
10666                this.stopFx();
10667                return true;
10668            }
10669            return false;
10670         }
10671         return true;
10672     },
10673
10674         /**
10675          * Returns true if the element is currently blocking so that no other effect can be queued
10676          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10677          * used to ensure that an effect initiated by a user action runs to completion prior to the
10678          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10679          * @return {Boolean} True if blocking, else false
10680          */
10681     hasFxBlock : function(){
10682         var q = this.fxQueue;
10683         return q && q[0] && q[0].block;
10684     },
10685
10686         /* @private */
10687     queueFx : function(o, fn){
10688         if(!this.fxQueue){
10689             this.fxQueue = [];
10690         }
10691         if(!this.hasFxBlock()){
10692             Roo.applyIf(o, this.fxDefaults);
10693             if(!o.concurrent){
10694                 var run = this.beforeFx(o);
10695                 fn.block = o.block;
10696                 this.fxQueue.push(fn);
10697                 if(run){
10698                     this.nextFx();
10699                 }
10700             }else{
10701                 fn.call(this);
10702             }
10703         }
10704         return this;
10705     },
10706
10707         /* @private */
10708     fxWrap : function(pos, o, vis){
10709         var wrap;
10710         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10711             var wrapXY;
10712             if(o.fixPosition){
10713                 wrapXY = this.getXY();
10714             }
10715             var div = document.createElement("div");
10716             div.style.visibility = vis;
10717             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10718             wrap.setPositioning(pos);
10719             if(wrap.getStyle("position") == "static"){
10720                 wrap.position("relative");
10721             }
10722             this.clearPositioning('auto');
10723             wrap.clip();
10724             wrap.dom.appendChild(this.dom);
10725             if(wrapXY){
10726                 wrap.setXY(wrapXY);
10727             }
10728         }
10729         return wrap;
10730     },
10731
10732         /* @private */
10733     fxUnwrap : function(wrap, pos, o){
10734         this.clearPositioning();
10735         this.setPositioning(pos);
10736         if(!o.wrap){
10737             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10738             wrap.remove();
10739         }
10740     },
10741
10742         /* @private */
10743     getFxRestore : function(){
10744         var st = this.dom.style;
10745         return {pos: this.getPositioning(), width: st.width, height : st.height};
10746     },
10747
10748         /* @private */
10749     afterFx : function(o){
10750         if(o.afterStyle){
10751             this.applyStyles(o.afterStyle);
10752         }
10753         if(o.afterCls){
10754             this.addClass(o.afterCls);
10755         }
10756         if(o.remove === true){
10757             this.remove();
10758         }
10759         Roo.callback(o.callback, o.scope, [this]);
10760         if(!o.concurrent){
10761             this.fxQueue.shift();
10762             this.nextFx();
10763         }
10764     },
10765
10766         /* @private */
10767     getFxEl : function(){ // support for composite element fx
10768         return Roo.get(this.dom);
10769     },
10770
10771         /* @private */
10772     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10773         animType = animType || 'run';
10774         opt = opt || {};
10775         var anim = Roo.lib.Anim[animType](
10776             this.dom, args,
10777             (opt.duration || defaultDur) || .35,
10778             (opt.easing || defaultEase) || 'easeOut',
10779             function(){
10780                 Roo.callback(cb, this);
10781             },
10782             this
10783         );
10784         opt.anim = anim;
10785         return anim;
10786     }
10787 };
10788
10789 // backwords compat
10790 Roo.Fx.resize = Roo.Fx.scale;
10791
10792 //When included, Roo.Fx is automatically applied to Element so that all basic
10793 //effects are available directly via the Element API
10794 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10795  * Based on:
10796  * Ext JS Library 1.1.1
10797  * Copyright(c) 2006-2007, Ext JS, LLC.
10798  *
10799  * Originally Released Under LGPL - original licence link has changed is not relivant.
10800  *
10801  * Fork - LGPL
10802  * <script type="text/javascript">
10803  */
10804
10805
10806 /**
10807  * @class Roo.CompositeElement
10808  * Standard composite class. Creates a Roo.Element for every element in the collection.
10809  * <br><br>
10810  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10811  * actions will be performed on all the elements in this collection.</b>
10812  * <br><br>
10813  * All methods return <i>this</i> and can be chained.
10814  <pre><code>
10815  var els = Roo.select("#some-el div.some-class", true);
10816  // or select directly from an existing element
10817  var el = Roo.get('some-el');
10818  el.select('div.some-class', true);
10819
10820  els.setWidth(100); // all elements become 100 width
10821  els.hide(true); // all elements fade out and hide
10822  // or
10823  els.setWidth(100).hide(true);
10824  </code></pre>
10825  */
10826 Roo.CompositeElement = function(els){
10827     this.elements = [];
10828     this.addElements(els);
10829 };
10830 Roo.CompositeElement.prototype = {
10831     isComposite: true,
10832     addElements : function(els){
10833         if(!els) return this;
10834         if(typeof els == "string"){
10835             els = Roo.Element.selectorFunction(els);
10836         }
10837         var yels = this.elements;
10838         var index = yels.length-1;
10839         for(var i = 0, len = els.length; i < len; i++) {
10840                 yels[++index] = Roo.get(els[i]);
10841         }
10842         return this;
10843     },
10844
10845     /**
10846     * Clears this composite and adds the elements returned by the passed selector.
10847     * @param {String/Array} els A string CSS selector, an array of elements or an element
10848     * @return {CompositeElement} this
10849     */
10850     fill : function(els){
10851         this.elements = [];
10852         this.add(els);
10853         return this;
10854     },
10855
10856     /**
10857     * Filters this composite to only elements that match the passed selector.
10858     * @param {String} selector A string CSS selector
10859     * @return {CompositeElement} this
10860     */
10861     filter : function(selector){
10862         var els = [];
10863         this.each(function(el){
10864             if(el.is(selector)){
10865                 els[els.length] = el.dom;
10866             }
10867         });
10868         this.fill(els);
10869         return this;
10870     },
10871
10872     invoke : function(fn, args){
10873         var els = this.elements;
10874         for(var i = 0, len = els.length; i < len; i++) {
10875                 Roo.Element.prototype[fn].apply(els[i], args);
10876         }
10877         return this;
10878     },
10879     /**
10880     * Adds elements to this composite.
10881     * @param {String/Array} els A string CSS selector, an array of elements or an element
10882     * @return {CompositeElement} this
10883     */
10884     add : function(els){
10885         if(typeof els == "string"){
10886             this.addElements(Roo.Element.selectorFunction(els));
10887         }else if(els.length !== undefined){
10888             this.addElements(els);
10889         }else{
10890             this.addElements([els]);
10891         }
10892         return this;
10893     },
10894     /**
10895     * Calls the passed function passing (el, this, index) for each element in this composite.
10896     * @param {Function} fn The function to call
10897     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10898     * @return {CompositeElement} this
10899     */
10900     each : function(fn, scope){
10901         var els = this.elements;
10902         for(var i = 0, len = els.length; i < len; i++){
10903             if(fn.call(scope || els[i], els[i], this, i) === false) {
10904                 break;
10905             }
10906         }
10907         return this;
10908     },
10909
10910     /**
10911      * Returns the Element object at the specified index
10912      * @param {Number} index
10913      * @return {Roo.Element}
10914      */
10915     item : function(index){
10916         return this.elements[index] || null;
10917     },
10918
10919     /**
10920      * Returns the first Element
10921      * @return {Roo.Element}
10922      */
10923     first : function(){
10924         return this.item(0);
10925     },
10926
10927     /**
10928      * Returns the last Element
10929      * @return {Roo.Element}
10930      */
10931     last : function(){
10932         return this.item(this.elements.length-1);
10933     },
10934
10935     /**
10936      * Returns the number of elements in this composite
10937      * @return Number
10938      */
10939     getCount : function(){
10940         return this.elements.length;
10941     },
10942
10943     /**
10944      * Returns true if this composite contains the passed element
10945      * @return Boolean
10946      */
10947     contains : function(el){
10948         return this.indexOf(el) !== -1;
10949     },
10950
10951     /**
10952      * Returns true if this composite contains the passed element
10953      * @return Boolean
10954      */
10955     indexOf : function(el){
10956         return this.elements.indexOf(Roo.get(el));
10957     },
10958
10959
10960     /**
10961     * Removes the specified element(s).
10962     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10963     * or an array of any of those.
10964     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10965     * @return {CompositeElement} this
10966     */
10967     removeElement : function(el, removeDom){
10968         if(el instanceof Array){
10969             for(var i = 0, len = el.length; i < len; i++){
10970                 this.removeElement(el[i]);
10971             }
10972             return this;
10973         }
10974         var index = typeof el == 'number' ? el : this.indexOf(el);
10975         if(index !== -1){
10976             if(removeDom){
10977                 var d = this.elements[index];
10978                 if(d.dom){
10979                     d.remove();
10980                 }else{
10981                     d.parentNode.removeChild(d);
10982                 }
10983             }
10984             this.elements.splice(index, 1);
10985         }
10986         return this;
10987     },
10988
10989     /**
10990     * Replaces the specified element with the passed element.
10991     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10992     * to replace.
10993     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10994     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10995     * @return {CompositeElement} this
10996     */
10997     replaceElement : function(el, replacement, domReplace){
10998         var index = typeof el == 'number' ? el : this.indexOf(el);
10999         if(index !== -1){
11000             if(domReplace){
11001                 this.elements[index].replaceWith(replacement);
11002             }else{
11003                 this.elements.splice(index, 1, Roo.get(replacement))
11004             }
11005         }
11006         return this;
11007     },
11008
11009     /**
11010      * Removes all elements.
11011      */
11012     clear : function(){
11013         this.elements = [];
11014     }
11015 };
11016 (function(){
11017     Roo.CompositeElement.createCall = function(proto, fnName){
11018         if(!proto[fnName]){
11019             proto[fnName] = function(){
11020                 return this.invoke(fnName, arguments);
11021             };
11022         }
11023     };
11024     for(var fnName in Roo.Element.prototype){
11025         if(typeof Roo.Element.prototype[fnName] == "function"){
11026             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11027         }
11028     };
11029 })();
11030 /*
11031  * Based on:
11032  * Ext JS Library 1.1.1
11033  * Copyright(c) 2006-2007, Ext JS, LLC.
11034  *
11035  * Originally Released Under LGPL - original licence link has changed is not relivant.
11036  *
11037  * Fork - LGPL
11038  * <script type="text/javascript">
11039  */
11040
11041 /**
11042  * @class Roo.CompositeElementLite
11043  * @extends Roo.CompositeElement
11044  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11045  <pre><code>
11046  var els = Roo.select("#some-el div.some-class");
11047  // or select directly from an existing element
11048  var el = Roo.get('some-el');
11049  el.select('div.some-class');
11050
11051  els.setWidth(100); // all elements become 100 width
11052  els.hide(true); // all elements fade out and hide
11053  // or
11054  els.setWidth(100).hide(true);
11055  </code></pre><br><br>
11056  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11057  * actions will be performed on all the elements in this collection.</b>
11058  */
11059 Roo.CompositeElementLite = function(els){
11060     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11061     this.el = new Roo.Element.Flyweight();
11062 };
11063 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11064     addElements : function(els){
11065         if(els){
11066             if(els instanceof Array){
11067                 this.elements = this.elements.concat(els);
11068             }else{
11069                 var yels = this.elements;
11070                 var index = yels.length-1;
11071                 for(var i = 0, len = els.length; i < len; i++) {
11072                     yels[++index] = els[i];
11073                 }
11074             }
11075         }
11076         return this;
11077     },
11078     invoke : function(fn, args){
11079         var els = this.elements;
11080         var el = this.el;
11081         for(var i = 0, len = els.length; i < len; i++) {
11082             el.dom = els[i];
11083                 Roo.Element.prototype[fn].apply(el, args);
11084         }
11085         return this;
11086     },
11087     /**
11088      * Returns a flyweight Element of the dom element object at the specified index
11089      * @param {Number} index
11090      * @return {Roo.Element}
11091      */
11092     item : function(index){
11093         if(!this.elements[index]){
11094             return null;
11095         }
11096         this.el.dom = this.elements[index];
11097         return this.el;
11098     },
11099
11100     // fixes scope with flyweight
11101     addListener : function(eventName, handler, scope, opt){
11102         var els = this.elements;
11103         for(var i = 0, len = els.length; i < len; i++) {
11104             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11105         }
11106         return this;
11107     },
11108
11109     /**
11110     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11111     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11112     * a reference to the dom node, use el.dom.</b>
11113     * @param {Function} fn The function to call
11114     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11115     * @return {CompositeElement} this
11116     */
11117     each : function(fn, scope){
11118         var els = this.elements;
11119         var el = this.el;
11120         for(var i = 0, len = els.length; i < len; i++){
11121             el.dom = els[i];
11122                 if(fn.call(scope || el, el, this, i) === false){
11123                 break;
11124             }
11125         }
11126         return this;
11127     },
11128
11129     indexOf : function(el){
11130         return this.elements.indexOf(Roo.getDom(el));
11131     },
11132
11133     replaceElement : function(el, replacement, domReplace){
11134         var index = typeof el == 'number' ? el : this.indexOf(el);
11135         if(index !== -1){
11136             replacement = Roo.getDom(replacement);
11137             if(domReplace){
11138                 var d = this.elements[index];
11139                 d.parentNode.insertBefore(replacement, d);
11140                 d.parentNode.removeChild(d);
11141             }
11142             this.elements.splice(index, 1, replacement);
11143         }
11144         return this;
11145     }
11146 });
11147 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11148
11149 /*
11150  * Based on:
11151  * Ext JS Library 1.1.1
11152  * Copyright(c) 2006-2007, Ext JS, LLC.
11153  *
11154  * Originally Released Under LGPL - original licence link has changed is not relivant.
11155  *
11156  * Fork - LGPL
11157  * <script type="text/javascript">
11158  */
11159
11160  
11161
11162 /**
11163  * @class Roo.data.Connection
11164  * @extends Roo.util.Observable
11165  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11166  * either to a configured URL, or to a URL specified at request time.<br><br>
11167  * <p>
11168  * Requests made by this class are asynchronous, and will return immediately. No data from
11169  * the server will be available to the statement immediately following the {@link #request} call.
11170  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11171  * <p>
11172  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11173  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11174  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11175  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11176  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11177  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11178  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11179  * standard DOM methods.
11180  * @constructor
11181  * @param {Object} config a configuration object.
11182  */
11183 Roo.data.Connection = function(config){
11184     Roo.apply(this, config);
11185     this.addEvents({
11186         /**
11187          * @event beforerequest
11188          * Fires before a network request is made to retrieve a data object.
11189          * @param {Connection} conn This Connection object.
11190          * @param {Object} options The options config object passed to the {@link #request} method.
11191          */
11192         "beforerequest" : true,
11193         /**
11194          * @event requestcomplete
11195          * Fires if the request was successfully completed.
11196          * @param {Connection} conn This Connection object.
11197          * @param {Object} response The XHR object containing the response data.
11198          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11199          * @param {Object} options The options config object passed to the {@link #request} method.
11200          */
11201         "requestcomplete" : true,
11202         /**
11203          * @event requestexception
11204          * Fires if an error HTTP status was returned from the server.
11205          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11206          * @param {Connection} conn This Connection object.
11207          * @param {Object} response The XHR object containing the response data.
11208          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11209          * @param {Object} options The options config object passed to the {@link #request} method.
11210          */
11211         "requestexception" : true
11212     });
11213     Roo.data.Connection.superclass.constructor.call(this);
11214 };
11215
11216 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11217     /**
11218      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11219      */
11220     /**
11221      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11222      * extra parameters to each request made by this object. (defaults to undefined)
11223      */
11224     /**
11225      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11226      *  to each request made by this object. (defaults to undefined)
11227      */
11228     /**
11229      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11230      */
11231     /**
11232      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11233      */
11234     timeout : 30000,
11235     /**
11236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11237      * @type Boolean
11238      */
11239     autoAbort:false,
11240
11241     /**
11242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11243      * @type Boolean
11244      */
11245     disableCaching: true,
11246
11247     /**
11248      * Sends an HTTP request to a remote server.
11249      * @param {Object} options An object which may contain the following properties:<ul>
11250      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11251      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11252      * request, a url encoded string or a function to call to get either.</li>
11253      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11254      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11255      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11256      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11257      * <li>options {Object} The parameter to the request call.</li>
11258      * <li>success {Boolean} True if the request succeeded.</li>
11259      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11260      * </ul></li>
11261      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11262      * The callback is passed the following parameters:<ul>
11263      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11264      * <li>options {Object} The parameter to the request call.</li>
11265      * </ul></li>
11266      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11267      * The callback is passed the following parameters:<ul>
11268      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11269      * <li>options {Object} The parameter to the request call.</li>
11270      * </ul></li>
11271      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11272      * for the callback function. Defaults to the browser window.</li>
11273      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11274      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11275      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11276      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11277      * params for the post data. Any params will be appended to the URL.</li>
11278      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11279      * </ul>
11280      * @return {Number} transactionId
11281      */
11282     request : function(o){
11283         if(this.fireEvent("beforerequest", this, o) !== false){
11284             var p = o.params;
11285
11286             if(typeof p == "function"){
11287                 p = p.call(o.scope||window, o);
11288             }
11289             if(typeof p == "object"){
11290                 p = Roo.urlEncode(o.params);
11291             }
11292             if(this.extraParams){
11293                 var extras = Roo.urlEncode(this.extraParams);
11294                 p = p ? (p + '&' + extras) : extras;
11295             }
11296
11297             var url = o.url || this.url;
11298             if(typeof url == 'function'){
11299                 url = url.call(o.scope||window, o);
11300             }
11301
11302             if(o.form){
11303                 var form = Roo.getDom(o.form);
11304                 url = url || form.action;
11305
11306                 var enctype = form.getAttribute("enctype");
11307                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11308                     return this.doFormUpload(o, p, url);
11309                 }
11310                 var f = Roo.lib.Ajax.serializeForm(form);
11311                 p = p ? (p + '&' + f) : f;
11312             }
11313
11314             var hs = o.headers;
11315             if(this.defaultHeaders){
11316                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11317                 if(!o.headers){
11318                     o.headers = hs;
11319                 }
11320             }
11321
11322             var cb = {
11323                 success: this.handleResponse,
11324                 failure: this.handleFailure,
11325                 scope: this,
11326                 argument: {options: o},
11327                 timeout : this.timeout
11328             };
11329
11330             var method = o.method||this.method||(p ? "POST" : "GET");
11331
11332             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11333                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11334             }
11335
11336             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11337                 if(o.autoAbort){
11338                     this.abort();
11339                 }
11340             }else if(this.autoAbort !== false){
11341                 this.abort();
11342             }
11343
11344             if((method == 'GET' && p) || o.xmlData){
11345                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11346                 p = '';
11347             }
11348             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11349             return this.transId;
11350         }else{
11351             Roo.callback(o.callback, o.scope, [o, null, null]);
11352             return null;
11353         }
11354     },
11355
11356     /**
11357      * Determine whether this object has a request outstanding.
11358      * @param {Number} transactionId (Optional) defaults to the last transaction
11359      * @return {Boolean} True if there is an outstanding request.
11360      */
11361     isLoading : function(transId){
11362         if(transId){
11363             return Roo.lib.Ajax.isCallInProgress(transId);
11364         }else{
11365             return this.transId ? true : false;
11366         }
11367     },
11368
11369     /**
11370      * Aborts any outstanding request.
11371      * @param {Number} transactionId (Optional) defaults to the last transaction
11372      */
11373     abort : function(transId){
11374         if(transId || this.isLoading()){
11375             Roo.lib.Ajax.abort(transId || this.transId);
11376         }
11377     },
11378
11379     // private
11380     handleResponse : function(response){
11381         this.transId = false;
11382         var options = response.argument.options;
11383         response.argument = options ? options.argument : null;
11384         this.fireEvent("requestcomplete", this, response, options);
11385         Roo.callback(options.success, options.scope, [response, options]);
11386         Roo.callback(options.callback, options.scope, [options, true, response]);
11387     },
11388
11389     // private
11390     handleFailure : function(response, e){
11391         this.transId = false;
11392         var options = response.argument.options;
11393         response.argument = options ? options.argument : null;
11394         this.fireEvent("requestexception", this, response, options, e);
11395         Roo.callback(options.failure, options.scope, [response, options]);
11396         Roo.callback(options.callback, options.scope, [options, false, response]);
11397     },
11398
11399     // private
11400     doFormUpload : function(o, ps, url){
11401         var id = Roo.id();
11402         var frame = document.createElement('iframe');
11403         frame.id = id;
11404         frame.name = id;
11405         frame.className = 'x-hidden';
11406         if(Roo.isIE){
11407             frame.src = Roo.SSL_SECURE_URL;
11408         }
11409         document.body.appendChild(frame);
11410
11411         if(Roo.isIE){
11412            document.frames[id].name = id;
11413         }
11414
11415         var form = Roo.getDom(o.form);
11416         form.target = id;
11417         form.method = 'POST';
11418         form.enctype = form.encoding = 'multipart/form-data';
11419         if(url){
11420             form.action = url;
11421         }
11422
11423         var hiddens, hd;
11424         if(ps){ // add dynamic params
11425             hiddens = [];
11426             ps = Roo.urlDecode(ps, false);
11427             for(var k in ps){
11428                 if(ps.hasOwnProperty(k)){
11429                     hd = document.createElement('input');
11430                     hd.type = 'hidden';
11431                     hd.name = k;
11432                     hd.value = ps[k];
11433                     form.appendChild(hd);
11434                     hiddens.push(hd);
11435                 }
11436             }
11437         }
11438
11439         function cb(){
11440             var r = {  // bogus response object
11441                 responseText : '',
11442                 responseXML : null
11443             };
11444
11445             r.argument = o ? o.argument : null;
11446
11447             try { //
11448                 var doc;
11449                 if(Roo.isIE){
11450                     doc = frame.contentWindow.document;
11451                 }else {
11452                     doc = (frame.contentDocument || window.frames[id].document);
11453                 }
11454                 if(doc && doc.body){
11455                     r.responseText = doc.body.innerHTML;
11456                 }
11457                 if(doc && doc.XMLDocument){
11458                     r.responseXML = doc.XMLDocument;
11459                 }else {
11460                     r.responseXML = doc;
11461                 }
11462             }
11463             catch(e) {
11464                 // ignore
11465             }
11466
11467             Roo.EventManager.removeListener(frame, 'load', cb, this);
11468
11469             this.fireEvent("requestcomplete", this, r, o);
11470             Roo.callback(o.success, o.scope, [r, o]);
11471             Roo.callback(o.callback, o.scope, [o, true, r]);
11472
11473             setTimeout(function(){document.body.removeChild(frame);}, 100);
11474         }
11475
11476         Roo.EventManager.on(frame, 'load', cb, this);
11477         form.submit();
11478
11479         if(hiddens){ // remove dynamic params
11480             for(var i = 0, len = hiddens.length; i < len; i++){
11481                 form.removeChild(hiddens[i]);
11482             }
11483         }
11484     }
11485 });
11486
11487 /**
11488  * @class Roo.Ajax
11489  * @extends Roo.data.Connection
11490  * Global Ajax request class.
11491  *
11492  * @singleton
11493  */
11494 Roo.Ajax = new Roo.data.Connection({
11495     // fix up the docs
11496    /**
11497      * @cfg {String} url @hide
11498      */
11499     /**
11500      * @cfg {Object} extraParams @hide
11501      */
11502     /**
11503      * @cfg {Object} defaultHeaders @hide
11504      */
11505     /**
11506      * @cfg {String} method (Optional) @hide
11507      */
11508     /**
11509      * @cfg {Number} timeout (Optional) @hide
11510      */
11511     /**
11512      * @cfg {Boolean} autoAbort (Optional) @hide
11513      */
11514
11515     /**
11516      * @cfg {Boolean} disableCaching (Optional) @hide
11517      */
11518
11519     /**
11520      * @property  disableCaching
11521      * True to add a unique cache-buster param to GET requests. (defaults to true)
11522      * @type Boolean
11523      */
11524     /**
11525      * @property  url
11526      * The default URL to be used for requests to the server. (defaults to undefined)
11527      * @type String
11528      */
11529     /**
11530      * @property  extraParams
11531      * An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      * @type Object
11534      */
11535     /**
11536      * @property  defaultHeaders
11537      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11538      * @type Object
11539      */
11540     /**
11541      * @property  method
11542      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11543      * @type String
11544      */
11545     /**
11546      * @property  timeout
11547      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11548      * @type Number
11549      */
11550
11551     /**
11552      * @property  autoAbort
11553      * Whether a new request should abort any pending requests. (defaults to false)
11554      * @type Boolean
11555      */
11556     autoAbort : false,
11557
11558     /**
11559      * Serialize the passed form into a url encoded string
11560      * @param {String/HTMLElement} form
11561      * @return {String}
11562      */
11563     serializeForm : function(form){
11564         return Roo.lib.Ajax.serializeForm(form);
11565     }
11566 });/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576  
11577 /**
11578  * Global Ajax request class.
11579  * 
11580  * @class Roo.Ajax
11581  * @extends Roo.data.Connection
11582  * @static
11583  * 
11584  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11585  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11586  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11587  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11588  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11589  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11590  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11591  */
11592 Roo.Ajax = new Roo.data.Connection({
11593     // fix up the docs
11594     /**
11595      * @scope Roo.Ajax
11596      * @type {Boolear} 
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @scope Roo.Ajax
11603      * @param {String/HTMLElement} form
11604      * @return {String}
11605      */
11606     serializeForm : function(form){
11607         return Roo.lib.Ajax.serializeForm(form);
11608     }
11609 });/*
11610  * Based on:
11611  * Ext JS Library 1.1.1
11612  * Copyright(c) 2006-2007, Ext JS, LLC.
11613  *
11614  * Originally Released Under LGPL - original licence link has changed is not relivant.
11615  *
11616  * Fork - LGPL
11617  * <script type="text/javascript">
11618  */
11619
11620  
11621 /**
11622  * @class Roo.UpdateManager
11623  * @extends Roo.util.Observable
11624  * Provides AJAX-style update for Element object.<br><br>
11625  * Usage:<br>
11626  * <pre><code>
11627  * // Get it from a Roo.Element object
11628  * var el = Roo.get("foo");
11629  * var mgr = el.getUpdateManager();
11630  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11631  * ...
11632  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11633  * <br>
11634  * // or directly (returns the same UpdateManager instance)
11635  * var mgr = new Roo.UpdateManager("myElementId");
11636  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11637  * mgr.on("update", myFcnNeedsToKnow);
11638  * <br>
11639    // short handed call directly from the element object
11640    Roo.get("foo").load({
11641         url: "bar.php",
11642         scripts:true,
11643         params: "for=bar",
11644         text: "Loading Foo..."
11645    });
11646  * </code></pre>
11647  * @constructor
11648  * Create new UpdateManager directly.
11649  * @param {String/HTMLElement/Roo.Element} el The element to update
11650  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11651  */
11652 Roo.UpdateManager = function(el, forceNew){
11653     el = Roo.get(el);
11654     if(!forceNew && el.updateManager){
11655         return el.updateManager;
11656     }
11657     /**
11658      * The Element object
11659      * @type Roo.Element
11660      */
11661     this.el = el;
11662     /**
11663      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11664      * @type String
11665      */
11666     this.defaultUrl = null;
11667
11668     this.addEvents({
11669         /**
11670          * @event beforeupdate
11671          * Fired before an update is made, return false from your handler and the update is cancelled.
11672          * @param {Roo.Element} el
11673          * @param {String/Object/Function} url
11674          * @param {String/Object} params
11675          */
11676         "beforeupdate": true,
11677         /**
11678          * @event update
11679          * Fired after successful update is made.
11680          * @param {Roo.Element} el
11681          * @param {Object} oResponseObject The response Object
11682          */
11683         "update": true,
11684         /**
11685          * @event failure
11686          * Fired on update failure.
11687          * @param {Roo.Element} el
11688          * @param {Object} oResponseObject The response Object
11689          */
11690         "failure": true
11691     });
11692     var d = Roo.UpdateManager.defaults;
11693     /**
11694      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11695      * @type String
11696      */
11697     this.sslBlankUrl = d.sslBlankUrl;
11698     /**
11699      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11700      * @type Boolean
11701      */
11702     this.disableCaching = d.disableCaching;
11703     /**
11704      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11705      * @type String
11706      */
11707     this.indicatorText = d.indicatorText;
11708     /**
11709      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11710      * @type String
11711      */
11712     this.showLoadIndicator = d.showLoadIndicator;
11713     /**
11714      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11715      * @type Number
11716      */
11717     this.timeout = d.timeout;
11718
11719     /**
11720      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11721      * @type Boolean
11722      */
11723     this.loadScripts = d.loadScripts;
11724
11725     /**
11726      * Transaction object of current executing transaction
11727      */
11728     this.transaction = null;
11729
11730     /**
11731      * @private
11732      */
11733     this.autoRefreshProcId = null;
11734     /**
11735      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11736      * @type Function
11737      */
11738     this.refreshDelegate = this.refresh.createDelegate(this);
11739     /**
11740      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11741      * @type Function
11742      */
11743     this.updateDelegate = this.update.createDelegate(this);
11744     /**
11745      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11749     /**
11750      * @private
11751      */
11752     this.successDelegate = this.processSuccess.createDelegate(this);
11753     /**
11754      * @private
11755      */
11756     this.failureDelegate = this.processFailure.createDelegate(this);
11757
11758     if(!this.renderer){
11759      /**
11760       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11761       */
11762     this.renderer = new Roo.UpdateManager.BasicRenderer();
11763     }
11764     
11765     Roo.UpdateManager.superclass.constructor.call(this);
11766 };
11767
11768 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11769     /**
11770      * Get the Element this UpdateManager is bound to
11771      * @return {Roo.Element} The element
11772      */
11773     getEl : function(){
11774         return this.el;
11775     },
11776     /**
11777      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11778      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11779 <pre><code>
11780 um.update({<br/>
11781     url: "your-url.php",<br/>
11782     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11783     callback: yourFunction,<br/>
11784     scope: yourObject, //(optional scope)  <br/>
11785     discardUrl: false, <br/>
11786     nocache: false,<br/>
11787     text: "Loading...",<br/>
11788     timeout: 30,<br/>
11789     scripts: false<br/>
11790 });
11791 </code></pre>
11792      * The only required property is url. The optional properties nocache, text and scripts
11793      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11794      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11795      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11796      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11797      */
11798     update : function(url, params, callback, discardUrl){
11799         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11800             var method = this.method, cfg;
11801             if(typeof url == "object"){ // must be config object
11802                 cfg = url;
11803                 url = cfg.url;
11804                 params = params || cfg.params;
11805                 callback = callback || cfg.callback;
11806                 discardUrl = discardUrl || cfg.discardUrl;
11807                 if(callback && cfg.scope){
11808                     callback = callback.createDelegate(cfg.scope);
11809                 }
11810                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11811                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11812                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11813                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11814                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11815             }
11816             this.showLoading();
11817             if(!discardUrl){
11818                 this.defaultUrl = url;
11819             }
11820             if(typeof url == "function"){
11821                 url = url.call(this);
11822             }
11823
11824             method = method || (params ? "POST" : "GET");
11825             if(method == "GET"){
11826                 url = this.prepareUrl(url);
11827             }
11828
11829             var o = Roo.apply(cfg ||{}, {
11830                 url : url,
11831                 params: params,
11832                 success: this.successDelegate,
11833                 failure: this.failureDelegate,
11834                 callback: undefined,
11835                 timeout: (this.timeout*1000),
11836                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11837             });
11838
11839             this.transaction = Roo.Ajax.request(o);
11840         }
11841     },
11842
11843     /**
11844      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11845      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11846      * @param {String/HTMLElement} form The form Id or form element
11847      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11848      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11849      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11850      */
11851     formUpdate : function(form, url, reset, callback){
11852         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11853             if(typeof url == "function"){
11854                 url = url.call(this);
11855             }
11856             form = Roo.getDom(form);
11857             this.transaction = Roo.Ajax.request({
11858                 form: form,
11859                 url:url,
11860                 success: this.successDelegate,
11861                 failure: this.failureDelegate,
11862                 timeout: (this.timeout*1000),
11863                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11864             });
11865             this.showLoading.defer(1, this);
11866         }
11867     },
11868
11869     /**
11870      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11872      */
11873     refresh : function(callback){
11874         if(this.defaultUrl == null){
11875             return;
11876         }
11877         this.update(this.defaultUrl, null, callback, true);
11878     },
11879
11880     /**
11881      * Set this element to auto refresh.
11882      * @param {Number} interval How often to update (in seconds).
11883      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11884      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11885      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11886      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11887      */
11888     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11889         if(refreshNow){
11890             this.update(url || this.defaultUrl, params, callback, true);
11891         }
11892         if(this.autoRefreshProcId){
11893             clearInterval(this.autoRefreshProcId);
11894         }
11895         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11896     },
11897
11898     /**
11899      * Stop auto refresh on this element.
11900      */
11901      stopAutoRefresh : function(){
11902         if(this.autoRefreshProcId){
11903             clearInterval(this.autoRefreshProcId);
11904             delete this.autoRefreshProcId;
11905         }
11906     },
11907
11908     isAutoRefreshing : function(){
11909        return this.autoRefreshProcId ? true : false;
11910     },
11911     /**
11912      * Called to update the element to "Loading" state. Override to perform custom action.
11913      */
11914     showLoading : function(){
11915         if(this.showLoadIndicator){
11916             this.el.update(this.indicatorText);
11917         }
11918     },
11919
11920     /**
11921      * Adds unique parameter to query string if disableCaching = true
11922      * @private
11923      */
11924     prepareUrl : function(url){
11925         if(this.disableCaching){
11926             var append = "_dc=" + (new Date().getTime());
11927             if(url.indexOf("?") !== -1){
11928                 url += "&" + append;
11929             }else{
11930                 url += "?" + append;
11931             }
11932         }
11933         return url;
11934     },
11935
11936     /**
11937      * @private
11938      */
11939     processSuccess : function(response){
11940         this.transaction = null;
11941         if(response.argument.form && response.argument.reset){
11942             try{ // put in try/catch since some older FF releases had problems with this
11943                 response.argument.form.reset();
11944             }catch(e){}
11945         }
11946         if(this.loadScripts){
11947             this.renderer.render(this.el, response, this,
11948                 this.updateComplete.createDelegate(this, [response]));
11949         }else{
11950             this.renderer.render(this.el, response, this);
11951             this.updateComplete(response);
11952         }
11953     },
11954
11955     updateComplete : function(response){
11956         this.fireEvent("update", this.el, response);
11957         if(typeof response.argument.callback == "function"){
11958             response.argument.callback(this.el, true, response);
11959         }
11960     },
11961
11962     /**
11963      * @private
11964      */
11965     processFailure : function(response){
11966         this.transaction = null;
11967         this.fireEvent("failure", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, false, response);
11970         }
11971     },
11972
11973     /**
11974      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11975      * @param {Object} renderer The object implementing the render() method
11976      */
11977     setRenderer : function(renderer){
11978         this.renderer = renderer;
11979     },
11980
11981     getRenderer : function(){
11982        return this.renderer;
11983     },
11984
11985     /**
11986      * Set the defaultUrl used for updates
11987      * @param {String/Function} defaultUrl The url or a function to call to get the url
11988      */
11989     setDefaultUrl : function(defaultUrl){
11990         this.defaultUrl = defaultUrl;
11991     },
11992
11993     /**
11994      * Aborts the executing transaction
11995      */
11996     abort : function(){
11997         if(this.transaction){
11998             Roo.Ajax.abort(this.transaction);
11999         }
12000     },
12001
12002     /**
12003      * Returns true if an update is in progress
12004      * @return {Boolean}
12005      */
12006     isUpdating : function(){
12007         if(this.transaction){
12008             return Roo.Ajax.isLoading(this.transaction);
12009         }
12010         return false;
12011     }
12012 });
12013
12014 /**
12015  * @class Roo.UpdateManager.defaults
12016  * @static (not really - but it helps the doc tool)
12017  * The defaults collection enables customizing the default properties of UpdateManager
12018  */
12019    Roo.UpdateManager.defaults = {
12020        /**
12021          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12022          * @type Number
12023          */
12024          timeout : 30,
12025
12026          /**
12027          * True to process scripts by default (Defaults to false).
12028          * @type Boolean
12029          */
12030         loadScripts : false,
12031
12032         /**
12033         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12034         * @type String
12035         */
12036         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12037         /**
12038          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12039          * @type Boolean
12040          */
12041         disableCaching : false,
12042         /**
12043          * Whether to show indicatorText when loading (Defaults to true).
12044          * @type Boolean
12045          */
12046         showLoadIndicator : true,
12047         /**
12048          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12049          * @type String
12050          */
12051         indicatorText : '<div class="loading-indicator">Loading...</div>'
12052    };
12053
12054 /**
12055  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12056  *Usage:
12057  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12058  * @param {String/HTMLElement/Roo.Element} el The element to update
12059  * @param {String} url The url
12060  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12061  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12062  * @static
12063  * @deprecated
12064  * @member Roo.UpdateManager
12065  */
12066 Roo.UpdateManager.updateElement = function(el, url, params, options){
12067     var um = Roo.get(el, true).getUpdateManager();
12068     Roo.apply(um, options);
12069     um.update(url, params, options ? options.callback : null);
12070 };
12071 // alias for backwards compat
12072 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12073 /**
12074  * @class Roo.UpdateManager.BasicRenderer
12075  * Default Content renderer. Updates the elements innerHTML with the responseText.
12076  */
12077 Roo.UpdateManager.BasicRenderer = function(){};
12078
12079 Roo.UpdateManager.BasicRenderer.prototype = {
12080     /**
12081      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12082      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12083      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12084      * @param {Roo.Element} el The element being rendered
12085      * @param {Object} response The YUI Connect response object
12086      * @param {UpdateManager} updateManager The calling update manager
12087      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12088      */
12089      render : function(el, response, updateManager, callback){
12090         el.update(response.responseText, updateManager.loadScripts, callback);
12091     }
12092 };
12093 /*
12094  * Based on:
12095  * Ext JS Library 1.1.1
12096  * Copyright(c) 2006-2007, Ext JS, LLC.
12097  *
12098  * Originally Released Under LGPL - original licence link has changed is not relivant.
12099  *
12100  * Fork - LGPL
12101  * <script type="text/javascript">
12102  */
12103
12104 /**
12105  * @class Roo.util.DelayedTask
12106  * Provides a convenient method of performing setTimeout where a new
12107  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12108  * You can use this class to buffer
12109  * the keypress events for a certain number of milliseconds, and perform only if they stop
12110  * for that amount of time.
12111  * @constructor The parameters to this constructor serve as defaults and are not required.
12112  * @param {Function} fn (optional) The default function to timeout
12113  * @param {Object} scope (optional) The default scope of that timeout
12114  * @param {Array} args (optional) The default Array of arguments
12115  */
12116 Roo.util.DelayedTask = function(fn, scope, args){
12117     var id = null, d, t;
12118
12119     var call = function(){
12120         var now = new Date().getTime();
12121         if(now - t >= d){
12122             clearInterval(id);
12123             id = null;
12124             fn.apply(scope, args || []);
12125         }
12126     };
12127     /**
12128      * Cancels any pending timeout and queues a new one
12129      * @param {Number} delay The milliseconds to delay
12130      * @param {Function} newFn (optional) Overrides function passed to constructor
12131      * @param {Object} newScope (optional) Overrides scope passed to constructor
12132      * @param {Array} newArgs (optional) Overrides args passed to constructor
12133      */
12134     this.delay = function(delay, newFn, newScope, newArgs){
12135         if(id && delay != d){
12136             this.cancel();
12137         }
12138         d = delay;
12139         t = new Date().getTime();
12140         fn = newFn || fn;
12141         scope = newScope || scope;
12142         args = newArgs || args;
12143         if(!id){
12144             id = setInterval(call, d);
12145         }
12146     };
12147
12148     /**
12149      * Cancel the last queued timeout
12150      */
12151     this.cancel = function(){
12152         if(id){
12153             clearInterval(id);
12154             id = null;
12155         }
12156     };
12157 };/*
12158  * Based on:
12159  * Ext JS Library 1.1.1
12160  * Copyright(c) 2006-2007, Ext JS, LLC.
12161  *
12162  * Originally Released Under LGPL - original licence link has changed is not relivant.
12163  *
12164  * Fork - LGPL
12165  * <script type="text/javascript">
12166  */
12167  
12168  
12169 Roo.util.TaskRunner = function(interval){
12170     interval = interval || 10;
12171     var tasks = [], removeQueue = [];
12172     var id = 0;
12173     var running = false;
12174
12175     var stopThread = function(){
12176         running = false;
12177         clearInterval(id);
12178         id = 0;
12179     };
12180
12181     var startThread = function(){
12182         if(!running){
12183             running = true;
12184             id = setInterval(runTasks, interval);
12185         }
12186     };
12187
12188     var removeTask = function(task){
12189         removeQueue.push(task);
12190         if(task.onStop){
12191             task.onStop();
12192         }
12193     };
12194
12195     var runTasks = function(){
12196         if(removeQueue.length > 0){
12197             for(var i = 0, len = removeQueue.length; i < len; i++){
12198                 tasks.remove(removeQueue[i]);
12199             }
12200             removeQueue = [];
12201             if(tasks.length < 1){
12202                 stopThread();
12203                 return;
12204             }
12205         }
12206         var now = new Date().getTime();
12207         for(var i = 0, len = tasks.length; i < len; ++i){
12208             var t = tasks[i];
12209             var itime = now - t.taskRunTime;
12210             if(t.interval <= itime){
12211                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12212                 t.taskRunTime = now;
12213                 if(rt === false || t.taskRunCount === t.repeat){
12214                     removeTask(t);
12215                     return;
12216                 }
12217             }
12218             if(t.duration && t.duration <= (now - t.taskStartTime)){
12219                 removeTask(t);
12220             }
12221         }
12222     };
12223
12224     /**
12225      * Queues a new task.
12226      * @param {Object} task
12227      */
12228     this.start = function(task){
12229         tasks.push(task);
12230         task.taskStartTime = new Date().getTime();
12231         task.taskRunTime = 0;
12232         task.taskRunCount = 0;
12233         startThread();
12234         return task;
12235     };
12236
12237     this.stop = function(task){
12238         removeTask(task);
12239         return task;
12240     };
12241
12242     this.stopAll = function(){
12243         stopThread();
12244         for(var i = 0, len = tasks.length; i < len; i++){
12245             if(tasks[i].onStop){
12246                 tasks[i].onStop();
12247             }
12248         }
12249         tasks = [];
12250         removeQueue = [];
12251     };
12252 };
12253
12254 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12255  * Based on:
12256  * Ext JS Library 1.1.1
12257  * Copyright(c) 2006-2007, Ext JS, LLC.
12258  *
12259  * Originally Released Under LGPL - original licence link has changed is not relivant.
12260  *
12261  * Fork - LGPL
12262  * <script type="text/javascript">
12263  */
12264
12265  
12266 /**
12267  * @class Roo.util.MixedCollection
12268  * @extends Roo.util.Observable
12269  * A Collection class that maintains both numeric indexes and keys and exposes events.
12270  * @constructor
12271  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12272  * collection (defaults to false)
12273  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12274  * and return the key value for that item.  This is used when available to look up the key on items that
12275  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12276  * equivalent to providing an implementation for the {@link #getKey} method.
12277  */
12278 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12279     this.items = [];
12280     this.map = {};
12281     this.keys = [];
12282     this.length = 0;
12283     this.addEvents({
12284         /**
12285          * @event clear
12286          * Fires when the collection is cleared.
12287          */
12288         "clear" : true,
12289         /**
12290          * @event add
12291          * Fires when an item is added to the collection.
12292          * @param {Number} index The index at which the item was added.
12293          * @param {Object} o The item added.
12294          * @param {String} key The key associated with the added item.
12295          */
12296         "add" : true,
12297         /**
12298          * @event replace
12299          * Fires when an item is replaced in the collection.
12300          * @param {String} key he key associated with the new added.
12301          * @param {Object} old The item being replaced.
12302          * @param {Object} new The new item.
12303          */
12304         "replace" : true,
12305         /**
12306          * @event remove
12307          * Fires when an item is removed from the collection.
12308          * @param {Object} o The item being removed.
12309          * @param {String} key (optional) The key associated with the removed item.
12310          */
12311         "remove" : true,
12312         "sort" : true
12313     });
12314     this.allowFunctions = allowFunctions === true;
12315     if(keyFn){
12316         this.getKey = keyFn;
12317     }
12318     Roo.util.MixedCollection.superclass.constructor.call(this);
12319 };
12320
12321 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12322     allowFunctions : false,
12323     
12324 /**
12325  * Adds an item to the collection.
12326  * @param {String} key The key to associate with the item
12327  * @param {Object} o The item to add.
12328  * @return {Object} The item added.
12329  */
12330     add : function(key, o){
12331         if(arguments.length == 1){
12332             o = arguments[0];
12333             key = this.getKey(o);
12334         }
12335         if(typeof key == "undefined" || key === null){
12336             this.length++;
12337             this.items.push(o);
12338             this.keys.push(null);
12339         }else{
12340             var old = this.map[key];
12341             if(old){
12342                 return this.replace(key, o);
12343             }
12344             this.length++;
12345             this.items.push(o);
12346             this.map[key] = o;
12347             this.keys.push(key);
12348         }
12349         this.fireEvent("add", this.length-1, o, key);
12350         return o;
12351     },
12352        
12353 /**
12354   * MixedCollection has a generic way to fetch keys if you implement getKey.
12355 <pre><code>
12356 // normal way
12357 var mc = new Roo.util.MixedCollection();
12358 mc.add(someEl.dom.id, someEl);
12359 mc.add(otherEl.dom.id, otherEl);
12360 //and so on
12361
12362 // using getKey
12363 var mc = new Roo.util.MixedCollection();
12364 mc.getKey = function(el){
12365    return el.dom.id;
12366 };
12367 mc.add(someEl);
12368 mc.add(otherEl);
12369
12370 // or via the constructor
12371 var mc = new Roo.util.MixedCollection(false, function(el){
12372    return el.dom.id;
12373 });
12374 mc.add(someEl);
12375 mc.add(otherEl);
12376 </code></pre>
12377  * @param o {Object} The item for which to find the key.
12378  * @return {Object} The key for the passed item.
12379  */
12380     getKey : function(o){
12381          return o.id; 
12382     },
12383    
12384 /**
12385  * Replaces an item in the collection.
12386  * @param {String} key The key associated with the item to replace, or the item to replace.
12387  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12388  * @return {Object}  The new item.
12389  */
12390     replace : function(key, o){
12391         if(arguments.length == 1){
12392             o = arguments[0];
12393             key = this.getKey(o);
12394         }
12395         var old = this.item(key);
12396         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12397              return this.add(key, o);
12398         }
12399         var index = this.indexOfKey(key);
12400         this.items[index] = o;
12401         this.map[key] = o;
12402         this.fireEvent("replace", key, old, o);
12403         return o;
12404     },
12405    
12406 /**
12407  * Adds all elements of an Array or an Object to the collection.
12408  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12409  * an Array of values, each of which are added to the collection.
12410  */
12411     addAll : function(objs){
12412         if(arguments.length > 1 || objs instanceof Array){
12413             var args = arguments.length > 1 ? arguments : objs;
12414             for(var i = 0, len = args.length; i < len; i++){
12415                 this.add(args[i]);
12416             }
12417         }else{
12418             for(var key in objs){
12419                 if(this.allowFunctions || typeof objs[key] != "function"){
12420                     this.add(key, objs[key]);
12421                 }
12422             }
12423         }
12424     },
12425    
12426 /**
12427  * Executes the specified function once for every item in the collection, passing each
12428  * item as the first and only parameter. returning false from the function will stop the iteration.
12429  * @param {Function} fn The function to execute for each item.
12430  * @param {Object} scope (optional) The scope in which to execute the function.
12431  */
12432     each : function(fn, scope){
12433         var items = [].concat(this.items); // each safe for removal
12434         for(var i = 0, len = items.length; i < len; i++){
12435             if(fn.call(scope || items[i], items[i], i, len) === false){
12436                 break;
12437             }
12438         }
12439     },
12440    
12441 /**
12442  * Executes the specified function once for every key in the collection, passing each
12443  * key, and its associated item as the first two parameters.
12444  * @param {Function} fn The function to execute for each item.
12445  * @param {Object} scope (optional) The scope in which to execute the function.
12446  */
12447     eachKey : function(fn, scope){
12448         for(var i = 0, len = this.keys.length; i < len; i++){
12449             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12450         }
12451     },
12452    
12453 /**
12454  * Returns the first item in the collection which elicits a true return value from the
12455  * passed selection function.
12456  * @param {Function} fn The selection function to execute for each item.
12457  * @param {Object} scope (optional) The scope in which to execute the function.
12458  * @return {Object} The first item in the collection which returned true from the selection function.
12459  */
12460     find : function(fn, scope){
12461         for(var i = 0, len = this.items.length; i < len; i++){
12462             if(fn.call(scope || window, this.items[i], this.keys[i])){
12463                 return this.items[i];
12464             }
12465         }
12466         return null;
12467     },
12468    
12469 /**
12470  * Inserts an item at the specified index in the collection.
12471  * @param {Number} index The index to insert the item at.
12472  * @param {String} key The key to associate with the new item, or the item itself.
12473  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12474  * @return {Object} The item inserted.
12475  */
12476     insert : function(index, key, o){
12477         if(arguments.length == 2){
12478             o = arguments[1];
12479             key = this.getKey(o);
12480         }
12481         if(index >= this.length){
12482             return this.add(key, o);
12483         }
12484         this.length++;
12485         this.items.splice(index, 0, o);
12486         if(typeof key != "undefined" && key != null){
12487             this.map[key] = o;
12488         }
12489         this.keys.splice(index, 0, key);
12490         this.fireEvent("add", index, o, key);
12491         return o;
12492     },
12493    
12494 /**
12495  * Removed an item from the collection.
12496  * @param {Object} o The item to remove.
12497  * @return {Object} The item removed.
12498  */
12499     remove : function(o){
12500         return this.removeAt(this.indexOf(o));
12501     },
12502    
12503 /**
12504  * Remove an item from a specified index in the collection.
12505  * @param {Number} index The index within the collection of the item to remove.
12506  */
12507     removeAt : function(index){
12508         if(index < this.length && index >= 0){
12509             this.length--;
12510             var o = this.items[index];
12511             this.items.splice(index, 1);
12512             var key = this.keys[index];
12513             if(typeof key != "undefined"){
12514                 delete this.map[key];
12515             }
12516             this.keys.splice(index, 1);
12517             this.fireEvent("remove", o, key);
12518         }
12519     },
12520    
12521 /**
12522  * Removed an item associated with the passed key fom the collection.
12523  * @param {String} key The key of the item to remove.
12524  */
12525     removeKey : function(key){
12526         return this.removeAt(this.indexOfKey(key));
12527     },
12528    
12529 /**
12530  * Returns the number of items in the collection.
12531  * @return {Number} the number of items in the collection.
12532  */
12533     getCount : function(){
12534         return this.length; 
12535     },
12536    
12537 /**
12538  * Returns index within the collection of the passed Object.
12539  * @param {Object} o The item to find the index of.
12540  * @return {Number} index of the item.
12541  */
12542     indexOf : function(o){
12543         if(!this.items.indexOf){
12544             for(var i = 0, len = this.items.length; i < len; i++){
12545                 if(this.items[i] == o) return i;
12546             }
12547             return -1;
12548         }else{
12549             return this.items.indexOf(o);
12550         }
12551     },
12552    
12553 /**
12554  * Returns index within the collection of the passed key.
12555  * @param {String} key The key to find the index of.
12556  * @return {Number} index of the key.
12557  */
12558     indexOfKey : function(key){
12559         if(!this.keys.indexOf){
12560             for(var i = 0, len = this.keys.length; i < len; i++){
12561                 if(this.keys[i] == key) return i;
12562             }
12563             return -1;
12564         }else{
12565             return this.keys.indexOf(key);
12566         }
12567     },
12568    
12569 /**
12570  * Returns the item associated with the passed key OR index. Key has priority over index.
12571  * @param {String/Number} key The key or index of the item.
12572  * @return {Object} The item associated with the passed key.
12573  */
12574     item : function(key){
12575         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12576         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12577     },
12578     
12579 /**
12580  * Returns the item at the specified index.
12581  * @param {Number} index The index of the item.
12582  * @return {Object}
12583  */
12584     itemAt : function(index){
12585         return this.items[index];
12586     },
12587     
12588 /**
12589  * Returns the item associated with the passed key.
12590  * @param {String/Number} key The key of the item.
12591  * @return {Object} The item associated with the passed key.
12592  */
12593     key : function(key){
12594         return this.map[key];
12595     },
12596    
12597 /**
12598  * Returns true if the collection contains the passed Object as an item.
12599  * @param {Object} o  The Object to look for in the collection.
12600  * @return {Boolean} True if the collection contains the Object as an item.
12601  */
12602     contains : function(o){
12603         return this.indexOf(o) != -1;
12604     },
12605    
12606 /**
12607  * Returns true if the collection contains the passed Object as a key.
12608  * @param {String} key The key to look for in the collection.
12609  * @return {Boolean} True if the collection contains the Object as a key.
12610  */
12611     containsKey : function(key){
12612         return typeof this.map[key] != "undefined";
12613     },
12614    
12615 /**
12616  * Removes all items from the collection.
12617  */
12618     clear : function(){
12619         this.length = 0;
12620         this.items = [];
12621         this.keys = [];
12622         this.map = {};
12623         this.fireEvent("clear");
12624     },
12625    
12626 /**
12627  * Returns the first item in the collection.
12628  * @return {Object} the first item in the collection..
12629  */
12630     first : function(){
12631         return this.items[0]; 
12632     },
12633    
12634 /**
12635  * Returns the last item in the collection.
12636  * @return {Object} the last item in the collection..
12637  */
12638     last : function(){
12639         return this.items[this.length-1];   
12640     },
12641     
12642     _sort : function(property, dir, fn){
12643         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12644         fn = fn || function(a, b){
12645             return a-b;
12646         };
12647         var c = [], k = this.keys, items = this.items;
12648         for(var i = 0, len = items.length; i < len; i++){
12649             c[c.length] = {key: k[i], value: items[i], index: i};
12650         }
12651         c.sort(function(a, b){
12652             var v = fn(a[property], b[property]) * dsc;
12653             if(v == 0){
12654                 v = (a.index < b.index ? -1 : 1);
12655             }
12656             return v;
12657         });
12658         for(var i = 0, len = c.length; i < len; i++){
12659             items[i] = c[i].value;
12660             k[i] = c[i].key;
12661         }
12662         this.fireEvent("sort", this);
12663     },
12664     
12665     /**
12666      * Sorts this collection with the passed comparison function
12667      * @param {String} direction (optional) "ASC" or "DESC"
12668      * @param {Function} fn (optional) comparison function
12669      */
12670     sort : function(dir, fn){
12671         this._sort("value", dir, fn);
12672     },
12673     
12674     /**
12675      * Sorts this collection by keys
12676      * @param {String} direction (optional) "ASC" or "DESC"
12677      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12678      */
12679     keySort : function(dir, fn){
12680         this._sort("key", dir, fn || function(a, b){
12681             return String(a).toUpperCase()-String(b).toUpperCase();
12682         });
12683     },
12684     
12685     /**
12686      * Returns a range of items in this collection
12687      * @param {Number} startIndex (optional) defaults to 0
12688      * @param {Number} endIndex (optional) default to the last item
12689      * @return {Array} An array of items
12690      */
12691     getRange : function(start, end){
12692         var items = this.items;
12693         if(items.length < 1){
12694             return [];
12695         }
12696         start = start || 0;
12697         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12698         var r = [];
12699         if(start <= end){
12700             for(var i = start; i <= end; i++) {
12701                     r[r.length] = items[i];
12702             }
12703         }else{
12704             for(var i = start; i >= end; i--) {
12705                     r[r.length] = items[i];
12706             }
12707         }
12708         return r;
12709     },
12710         
12711     /**
12712      * Filter the <i>objects</i> in this collection by a specific property. 
12713      * Returns a new collection that has been filtered.
12714      * @param {String} property A property on your objects
12715      * @param {String/RegExp} value Either string that the property values 
12716      * should start with or a RegExp to test against the property
12717      * @return {MixedCollection} The new filtered collection
12718      */
12719     filter : function(property, value){
12720         if(!value.exec){ // not a regex
12721             value = String(value);
12722             if(value.length == 0){
12723                 return this.clone();
12724             }
12725             value = new RegExp("^" + Roo.escapeRe(value), "i");
12726         }
12727         return this.filterBy(function(o){
12728             return o && value.test(o[property]);
12729         });
12730         },
12731     
12732     /**
12733      * Filter by a function. * Returns a new collection that has been filtered.
12734      * The passed function will be called with each 
12735      * object in the collection. If the function returns true, the value is included 
12736      * otherwise it is filtered.
12737      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12738      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12739      * @return {MixedCollection} The new filtered collection
12740      */
12741     filterBy : function(fn, scope){
12742         var r = new Roo.util.MixedCollection();
12743         r.getKey = this.getKey;
12744         var k = this.keys, it = this.items;
12745         for(var i = 0, len = it.length; i < len; i++){
12746             if(fn.call(scope||this, it[i], k[i])){
12747                                 r.add(k[i], it[i]);
12748                         }
12749         }
12750         return r;
12751     },
12752     
12753     /**
12754      * Creates a duplicate of this collection
12755      * @return {MixedCollection}
12756      */
12757     clone : function(){
12758         var r = new Roo.util.MixedCollection();
12759         var k = this.keys, it = this.items;
12760         for(var i = 0, len = it.length; i < len; i++){
12761             r.add(k[i], it[i]);
12762         }
12763         r.getKey = this.getKey;
12764         return r;
12765     }
12766 });
12767 /**
12768  * Returns the item associated with the passed key or index.
12769  * @method
12770  * @param {String/Number} key The key or index of the item.
12771  * @return {Object} The item associated with the passed key.
12772  */
12773 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12774  * Based on:
12775  * Ext JS Library 1.1.1
12776  * Copyright(c) 2006-2007, Ext JS, LLC.
12777  *
12778  * Originally Released Under LGPL - original licence link has changed is not relivant.
12779  *
12780  * Fork - LGPL
12781  * <script type="text/javascript">
12782  */
12783 /**
12784  * @class Roo.util.JSON
12785  * Modified version of Douglas Crockford"s json.js that doesn"t
12786  * mess with the Object prototype 
12787  * http://www.json.org/js.html
12788  * @singleton
12789  */
12790 Roo.util.JSON = new (function(){
12791     var useHasOwn = {}.hasOwnProperty ? true : false;
12792     
12793     // crashes Safari in some instances
12794     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12795     
12796     var pad = function(n) {
12797         return n < 10 ? "0" + n : n;
12798     };
12799     
12800     var m = {
12801         "\b": '\\b',
12802         "\t": '\\t',
12803         "\n": '\\n',
12804         "\f": '\\f',
12805         "\r": '\\r',
12806         '"' : '\\"',
12807         "\\": '\\\\'
12808     };
12809
12810     var encodeString = function(s){
12811         if (/["\\\x00-\x1f]/.test(s)) {
12812             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12813                 var c = m[b];
12814                 if(c){
12815                     return c;
12816                 }
12817                 c = b.charCodeAt();
12818                 return "\\u00" +
12819                     Math.floor(c / 16).toString(16) +
12820                     (c % 16).toString(16);
12821             }) + '"';
12822         }
12823         return '"' + s + '"';
12824     };
12825     
12826     var encodeArray = function(o){
12827         var a = ["["], b, i, l = o.length, v;
12828             for (i = 0; i < l; i += 1) {
12829                 v = o[i];
12830                 switch (typeof v) {
12831                     case "undefined":
12832                     case "function":
12833                     case "unknown":
12834                         break;
12835                     default:
12836                         if (b) {
12837                             a.push(',');
12838                         }
12839                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12840                         b = true;
12841                 }
12842             }
12843             a.push("]");
12844             return a.join("");
12845     };
12846     
12847     var encodeDate = function(o){
12848         return '"' + o.getFullYear() + "-" +
12849                 pad(o.getMonth() + 1) + "-" +
12850                 pad(o.getDate()) + "T" +
12851                 pad(o.getHours()) + ":" +
12852                 pad(o.getMinutes()) + ":" +
12853                 pad(o.getSeconds()) + '"';
12854     };
12855     
12856     /**
12857      * Encodes an Object, Array or other value
12858      * @param {Mixed} o The variable to encode
12859      * @return {String} The JSON string
12860      */
12861     this.encode = function(o)
12862     {
12863         // should this be extended to fully wrap stringify..
12864         
12865         if(typeof o == "undefined" || o === null){
12866             return "null";
12867         }else if(o instanceof Array){
12868             return encodeArray(o);
12869         }else if(o instanceof Date){
12870             return encodeDate(o);
12871         }else if(typeof o == "string"){
12872             return encodeString(o);
12873         }else if(typeof o == "number"){
12874             return isFinite(o) ? String(o) : "null";
12875         }else if(typeof o == "boolean"){
12876             return String(o);
12877         }else {
12878             var a = ["{"], b, i, v;
12879             for (i in o) {
12880                 if(!useHasOwn || o.hasOwnProperty(i)) {
12881                     v = o[i];
12882                     switch (typeof v) {
12883                     case "undefined":
12884                     case "function":
12885                     case "unknown":
12886                         break;
12887                     default:
12888                         if(b){
12889                             a.push(',');
12890                         }
12891                         a.push(this.encode(i), ":",
12892                                 v === null ? "null" : this.encode(v));
12893                         b = true;
12894                     }
12895                 }
12896             }
12897             a.push("}");
12898             return a.join("");
12899         }
12900     };
12901     
12902     /**
12903      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12904      * @param {String} json The JSON string
12905      * @return {Object} The resulting object
12906      */
12907     this.decode = function(json){
12908         
12909         return  /** eval:var:json */ eval("(" + json + ')');
12910     };
12911 })();
12912 /** 
12913  * Shorthand for {@link Roo.util.JSON#encode}
12914  * @member Roo encode 
12915  * @method */
12916 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12917 /** 
12918  * Shorthand for {@link Roo.util.JSON#decode}
12919  * @member Roo decode 
12920  * @method */
12921 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12922 /*
12923  * Based on:
12924  * Ext JS Library 1.1.1
12925  * Copyright(c) 2006-2007, Ext JS, LLC.
12926  *
12927  * Originally Released Under LGPL - original licence link has changed is not relivant.
12928  *
12929  * Fork - LGPL
12930  * <script type="text/javascript">
12931  */
12932  
12933 /**
12934  * @class Roo.util.Format
12935  * Reusable data formatting functions
12936  * @singleton
12937  */
12938 Roo.util.Format = function(){
12939     var trimRe = /^\s+|\s+$/g;
12940     return {
12941         /**
12942          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12943          * @param {String} value The string to truncate
12944          * @param {Number} length The maximum length to allow before truncating
12945          * @return {String} The converted text
12946          */
12947         ellipsis : function(value, len){
12948             if(value && value.length > len){
12949                 return value.substr(0, len-3)+"...";
12950             }
12951             return value;
12952         },
12953
12954         /**
12955          * Checks a reference and converts it to empty string if it is undefined
12956          * @param {Mixed} value Reference to check
12957          * @return {Mixed} Empty string if converted, otherwise the original value
12958          */
12959         undef : function(value){
12960             return typeof value != "undefined" ? value : "";
12961         },
12962
12963         /**
12964          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12965          * @param {String} value The string to encode
12966          * @return {String} The encoded text
12967          */
12968         htmlEncode : function(value){
12969             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12970         },
12971
12972         /**
12973          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12974          * @param {String} value The string to decode
12975          * @return {String} The decoded text
12976          */
12977         htmlDecode : function(value){
12978             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12979         },
12980
12981         /**
12982          * Trims any whitespace from either side of a string
12983          * @param {String} value The text to trim
12984          * @return {String} The trimmed text
12985          */
12986         trim : function(value){
12987             return String(value).replace(trimRe, "");
12988         },
12989
12990         /**
12991          * Returns a substring from within an original string
12992          * @param {String} value The original text
12993          * @param {Number} start The start index of the substring
12994          * @param {Number} length The length of the substring
12995          * @return {String} The substring
12996          */
12997         substr : function(value, start, length){
12998             return String(value).substr(start, length);
12999         },
13000
13001         /**
13002          * Converts a string to all lower case letters
13003          * @param {String} value The text to convert
13004          * @return {String} The converted text
13005          */
13006         lowercase : function(value){
13007             return String(value).toLowerCase();
13008         },
13009
13010         /**
13011          * Converts a string to all upper case letters
13012          * @param {String} value The text to convert
13013          * @return {String} The converted text
13014          */
13015         uppercase : function(value){
13016             return String(value).toUpperCase();
13017         },
13018
13019         /**
13020          * Converts the first character only of a string to upper case
13021          * @param {String} value The text to convert
13022          * @return {String} The converted text
13023          */
13024         capitalize : function(value){
13025             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13026         },
13027
13028         // private
13029         call : function(value, fn){
13030             if(arguments.length > 2){
13031                 var args = Array.prototype.slice.call(arguments, 2);
13032                 args.unshift(value);
13033                  
13034                 return /** eval:var:value */  eval(fn).apply(window, args);
13035             }else{
13036                 /** eval:var:value */
13037                 return /** eval:var:value */ eval(fn).call(window, value);
13038             }
13039         },
13040
13041        
13042         /**
13043          * safer version of Math.toFixed..??/
13044          * @param {Number/String} value The numeric value to format
13045          * @param {Number/String} value Decimal places 
13046          * @return {String} The formatted currency string
13047          */
13048         toFixed : function(v, n)
13049         {
13050             // why not use to fixed - precision is buggered???
13051             if (!n) {
13052                 return Math.round(v-0);
13053             }
13054             var fact = Math.pow(10,n+1);
13055             v = (Math.round((v-0)*fact))/fact;
13056             var z = (''+fact).substring(2);
13057             if (v == Math.floor(v)) {
13058                 return Math.floor(v) + '.' + z;
13059             }
13060             
13061             // now just padd decimals..
13062             var ps = String(v).split('.');
13063             var fd = (ps[1] + z);
13064             var r = fd.substring(0,n); 
13065             var rm = fd.substring(n); 
13066             if (rm < 5) {
13067                 return ps[0] + '.' + r;
13068             }
13069             r*=1; // turn it into a number;
13070             r++;
13071             if (String(r).length != n) {
13072                 ps[0]*=1;
13073                 ps[0]++;
13074                 r = String(r).substring(1); // chop the end off.
13075             }
13076             
13077             return ps[0] + '.' + r;
13078              
13079         },
13080         
13081         /**
13082          * Format a number as US currency
13083          * @param {Number/String} value The numeric value to format
13084          * @return {String} The formatted currency string
13085          */
13086         usMoney : function(v){
13087             v = (Math.round((v-0)*100))/100;
13088             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13089             v = String(v);
13090             var ps = v.split('.');
13091             var whole = ps[0];
13092             var sub = ps[1] ? '.'+ ps[1] : '.00';
13093             var r = /(\d+)(\d{3})/;
13094             while (r.test(whole)) {
13095                 whole = whole.replace(r, '$1' + ',' + '$2');
13096             }
13097             return "$" + whole + sub ;
13098         },
13099         
13100         /**
13101          * Parse a value into a formatted date using the specified format pattern.
13102          * @param {Mixed} value The value to format
13103          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13104          * @return {String} The formatted date string
13105          */
13106         date : function(v, format){
13107             if(!v){
13108                 return "";
13109             }
13110             if(!(v instanceof Date)){
13111                 v = new Date(Date.parse(v));
13112             }
13113             return v.dateFormat(format || "m/d/Y");
13114         },
13115
13116         /**
13117          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13118          * @param {String} format Any valid date format string
13119          * @return {Function} The date formatting function
13120          */
13121         dateRenderer : function(format){
13122             return function(v){
13123                 return Roo.util.Format.date(v, format);  
13124             };
13125         },
13126
13127         // private
13128         stripTagsRE : /<\/?[^>]+>/gi,
13129         
13130         /**
13131          * Strips all HTML tags
13132          * @param {Mixed} value The text from which to strip tags
13133          * @return {String} The stripped text
13134          */
13135         stripTags : function(v){
13136             return !v ? v : String(v).replace(this.stripTagsRE, "");
13137         }
13138     };
13139 }();/*
13140  * Based on:
13141  * Ext JS Library 1.1.1
13142  * Copyright(c) 2006-2007, Ext JS, LLC.
13143  *
13144  * Originally Released Under LGPL - original licence link has changed is not relivant.
13145  *
13146  * Fork - LGPL
13147  * <script type="text/javascript">
13148  */
13149
13150
13151  
13152
13153 /**
13154  * @class Roo.MasterTemplate
13155  * @extends Roo.Template
13156  * Provides a template that can have child templates. The syntax is:
13157 <pre><code>
13158 var t = new Roo.MasterTemplate(
13159         '&lt;select name="{name}"&gt;',
13160                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13161         '&lt;/select&gt;'
13162 );
13163 t.add('options', {value: 'foo', text: 'bar'});
13164 // or you can add multiple child elements in one shot
13165 t.addAll('options', [
13166     {value: 'foo', text: 'bar'},
13167     {value: 'foo2', text: 'bar2'},
13168     {value: 'foo3', text: 'bar3'}
13169 ]);
13170 // then append, applying the master template values
13171 t.append('my-form', {name: 'my-select'});
13172 </code></pre>
13173 * A name attribute for the child template is not required if you have only one child
13174 * template or you want to refer to them by index.
13175  */
13176 Roo.MasterTemplate = function(){
13177     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13178     this.originalHtml = this.html;
13179     var st = {};
13180     var m, re = this.subTemplateRe;
13181     re.lastIndex = 0;
13182     var subIndex = 0;
13183     while(m = re.exec(this.html)){
13184         var name = m[1], content = m[2];
13185         st[subIndex] = {
13186             name: name,
13187             index: subIndex,
13188             buffer: [],
13189             tpl : new Roo.Template(content)
13190         };
13191         if(name){
13192             st[name] = st[subIndex];
13193         }
13194         st[subIndex].tpl.compile();
13195         st[subIndex].tpl.call = this.call.createDelegate(this);
13196         subIndex++;
13197     }
13198     this.subCount = subIndex;
13199     this.subs = st;
13200 };
13201 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13202     /**
13203     * The regular expression used to match sub templates
13204     * @type RegExp
13205     * @property
13206     */
13207     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13208
13209     /**
13210      * Applies the passed values to a child template.
13211      * @param {String/Number} name (optional) The name or index of the child template
13212      * @param {Array/Object} values The values to be applied to the template
13213      * @return {MasterTemplate} this
13214      */
13215      add : function(name, values){
13216         if(arguments.length == 1){
13217             values = arguments[0];
13218             name = 0;
13219         }
13220         var s = this.subs[name];
13221         s.buffer[s.buffer.length] = s.tpl.apply(values);
13222         return this;
13223     },
13224
13225     /**
13226      * Applies all the passed values to a child template.
13227      * @param {String/Number} name (optional) The name or index of the child template
13228      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13229      * @param {Boolean} reset (optional) True to reset the template first
13230      * @return {MasterTemplate} this
13231      */
13232     fill : function(name, values, reset){
13233         var a = arguments;
13234         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13235             values = a[0];
13236             name = 0;
13237             reset = a[1];
13238         }
13239         if(reset){
13240             this.reset();
13241         }
13242         for(var i = 0, len = values.length; i < len; i++){
13243             this.add(name, values[i]);
13244         }
13245         return this;
13246     },
13247
13248     /**
13249      * Resets the template for reuse
13250      * @return {MasterTemplate} this
13251      */
13252      reset : function(){
13253         var s = this.subs;
13254         for(var i = 0; i < this.subCount; i++){
13255             s[i].buffer = [];
13256         }
13257         return this;
13258     },
13259
13260     applyTemplate : function(values){
13261         var s = this.subs;
13262         var replaceIndex = -1;
13263         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13264             return s[++replaceIndex].buffer.join("");
13265         });
13266         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13267     },
13268
13269     apply : function(){
13270         return this.applyTemplate.apply(this, arguments);
13271     },
13272
13273     compile : function(){return this;}
13274 });
13275
13276 /**
13277  * Alias for fill().
13278  * @method
13279  */
13280 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13281  /**
13282  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13283  * var tpl = Roo.MasterTemplate.from('element-id');
13284  * @param {String/HTMLElement} el
13285  * @param {Object} config
13286  * @static
13287  */
13288 Roo.MasterTemplate.from = function(el, config){
13289     el = Roo.getDom(el);
13290     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13291 };/*
13292  * Based on:
13293  * Ext JS Library 1.1.1
13294  * Copyright(c) 2006-2007, Ext JS, LLC.
13295  *
13296  * Originally Released Under LGPL - original licence link has changed is not relivant.
13297  *
13298  * Fork - LGPL
13299  * <script type="text/javascript">
13300  */
13301
13302  
13303 /**
13304  * @class Roo.util.CSS
13305  * Utility class for manipulating CSS rules
13306  * @singleton
13307  */
13308 Roo.util.CSS = function(){
13309         var rules = null;
13310         var doc = document;
13311
13312     var camelRe = /(-[a-z])/gi;
13313     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13314
13315    return {
13316    /**
13317     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13318     * tag and appended to the HEAD of the document.
13319     * @param {String|Object} cssText The text containing the css rules
13320     * @param {String} id An id to add to the stylesheet for later removal
13321     * @return {StyleSheet}
13322     */
13323     createStyleSheet : function(cssText, id){
13324         var ss;
13325         var head = doc.getElementsByTagName("head")[0];
13326         var nrules = doc.createElement("style");
13327         nrules.setAttribute("type", "text/css");
13328         if(id){
13329             nrules.setAttribute("id", id);
13330         }
13331         if (typeof(cssText) != 'string') {
13332             // support object maps..
13333             // not sure if this a good idea.. 
13334             // perhaps it should be merged with the general css handling
13335             // and handle js style props.
13336             var cssTextNew = [];
13337             for(var n in cssText) {
13338                 var citems = [];
13339                 for(var k in cssText[n]) {
13340                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13341                 }
13342                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13343                 
13344             }
13345             cssText = cssTextNew.join("\n");
13346             
13347         }
13348        
13349        
13350        if(Roo.isIE){
13351            head.appendChild(nrules);
13352            ss = nrules.styleSheet;
13353            ss.cssText = cssText;
13354        }else{
13355            try{
13356                 nrules.appendChild(doc.createTextNode(cssText));
13357            }catch(e){
13358                nrules.cssText = cssText; 
13359            }
13360            head.appendChild(nrules);
13361            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13362        }
13363        this.cacheStyleSheet(ss);
13364        return ss;
13365    },
13366
13367    /**
13368     * Removes a style or link tag by id
13369     * @param {String} id The id of the tag
13370     */
13371    removeStyleSheet : function(id){
13372        var existing = doc.getElementById(id);
13373        if(existing){
13374            existing.parentNode.removeChild(existing);
13375        }
13376    },
13377
13378    /**
13379     * Dynamically swaps an existing stylesheet reference for a new one
13380     * @param {String} id The id of an existing link tag to remove
13381     * @param {String} url The href of the new stylesheet to include
13382     */
13383    swapStyleSheet : function(id, url){
13384        this.removeStyleSheet(id);
13385        var ss = doc.createElement("link");
13386        ss.setAttribute("rel", "stylesheet");
13387        ss.setAttribute("type", "text/css");
13388        ss.setAttribute("id", id);
13389        ss.setAttribute("href", url);
13390        doc.getElementsByTagName("head")[0].appendChild(ss);
13391    },
13392    
13393    /**
13394     * Refresh the rule cache if you have dynamically added stylesheets
13395     * @return {Object} An object (hash) of rules indexed by selector
13396     */
13397    refreshCache : function(){
13398        return this.getRules(true);
13399    },
13400
13401    // private
13402    cacheStyleSheet : function(stylesheet){
13403        if(!rules){
13404            rules = {};
13405        }
13406        try{// try catch for cross domain access issue
13407            var ssRules = stylesheet.cssRules || stylesheet.rules;
13408            for(var j = ssRules.length-1; j >= 0; --j){
13409                rules[ssRules[j].selectorText] = ssRules[j];
13410            }
13411        }catch(e){}
13412    },
13413    
13414    /**
13415     * Gets all css rules for the document
13416     * @param {Boolean} refreshCache true to refresh the internal cache
13417     * @return {Object} An object (hash) of rules indexed by selector
13418     */
13419    getRules : function(refreshCache){
13420                 if(rules == null || refreshCache){
13421                         rules = {};
13422                         var ds = doc.styleSheets;
13423                         for(var i =0, len = ds.length; i < len; i++){
13424                             try{
13425                         this.cacheStyleSheet(ds[i]);
13426                     }catch(e){} 
13427                 }
13428                 }
13429                 return rules;
13430         },
13431         
13432         /**
13433     * Gets an an individual CSS rule by selector(s)
13434     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13435     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13436     * @return {CSSRule} The CSS rule or null if one is not found
13437     */
13438    getRule : function(selector, refreshCache){
13439                 var rs = this.getRules(refreshCache);
13440                 if(!(selector instanceof Array)){
13441                     return rs[selector];
13442                 }
13443                 for(var i = 0; i < selector.length; i++){
13444                         if(rs[selector[i]]){
13445                                 return rs[selector[i]];
13446                         }
13447                 }
13448                 return null;
13449         },
13450         
13451         
13452         /**
13453     * Updates a rule property
13454     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13455     * @param {String} property The css property
13456     * @param {String} value The new value for the property
13457     * @return {Boolean} true If a rule was found and updated
13458     */
13459    updateRule : function(selector, property, value){
13460                 if(!(selector instanceof Array)){
13461                         var rule = this.getRule(selector);
13462                         if(rule){
13463                                 rule.style[property.replace(camelRe, camelFn)] = value;
13464                                 return true;
13465                         }
13466                 }else{
13467                         for(var i = 0; i < selector.length; i++){
13468                                 if(this.updateRule(selector[i], property, value)){
13469                                         return true;
13470                                 }
13471                         }
13472                 }
13473                 return false;
13474         }
13475    };   
13476 }();/*
13477  * Based on:
13478  * Ext JS Library 1.1.1
13479  * Copyright(c) 2006-2007, Ext JS, LLC.
13480  *
13481  * Originally Released Under LGPL - original licence link has changed is not relivant.
13482  *
13483  * Fork - LGPL
13484  * <script type="text/javascript">
13485  */
13486
13487  
13488
13489 /**
13490  * @class Roo.util.ClickRepeater
13491  * @extends Roo.util.Observable
13492  * 
13493  * A wrapper class which can be applied to any element. Fires a "click" event while the
13494  * mouse is pressed. The interval between firings may be specified in the config but
13495  * defaults to 10 milliseconds.
13496  * 
13497  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13498  * 
13499  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13500  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13501  * Similar to an autorepeat key delay.
13502  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13503  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13504  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13505  *           "interval" and "delay" are ignored. "immediate" is honored.
13506  * @cfg {Boolean} preventDefault True to prevent the default click event
13507  * @cfg {Boolean} stopDefault True to stop the default click event
13508  * 
13509  * @history
13510  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13511  *     2007-02-02 jvs Renamed to ClickRepeater
13512  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13513  *
13514  *  @constructor
13515  * @param {String/HTMLElement/Element} el The element to listen on
13516  * @param {Object} config
13517  **/
13518 Roo.util.ClickRepeater = function(el, config)
13519 {
13520     this.el = Roo.get(el);
13521     this.el.unselectable();
13522
13523     Roo.apply(this, config);
13524
13525     this.addEvents({
13526     /**
13527      * @event mousedown
13528      * Fires when the mouse button is depressed.
13529      * @param {Roo.util.ClickRepeater} this
13530      */
13531         "mousedown" : true,
13532     /**
13533      * @event click
13534      * Fires on a specified interval during the time the element is pressed.
13535      * @param {Roo.util.ClickRepeater} this
13536      */
13537         "click" : true,
13538     /**
13539      * @event mouseup
13540      * Fires when the mouse key is released.
13541      * @param {Roo.util.ClickRepeater} this
13542      */
13543         "mouseup" : true
13544     });
13545
13546     this.el.on("mousedown", this.handleMouseDown, this);
13547     if(this.preventDefault || this.stopDefault){
13548         this.el.on("click", function(e){
13549             if(this.preventDefault){
13550                 e.preventDefault();
13551             }
13552             if(this.stopDefault){
13553                 e.stopEvent();
13554             }
13555         }, this);
13556     }
13557
13558     // allow inline handler
13559     if(this.handler){
13560         this.on("click", this.handler,  this.scope || this);
13561     }
13562
13563     Roo.util.ClickRepeater.superclass.constructor.call(this);
13564 };
13565
13566 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13567     interval : 20,
13568     delay: 250,
13569     preventDefault : true,
13570     stopDefault : false,
13571     timer : 0,
13572
13573     // private
13574     handleMouseDown : function(){
13575         clearTimeout(this.timer);
13576         this.el.blur();
13577         if(this.pressClass){
13578             this.el.addClass(this.pressClass);
13579         }
13580         this.mousedownTime = new Date();
13581
13582         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13583         this.el.on("mouseout", this.handleMouseOut, this);
13584
13585         this.fireEvent("mousedown", this);
13586         this.fireEvent("click", this);
13587         
13588         this.timer = this.click.defer(this.delay || this.interval, this);
13589     },
13590
13591     // private
13592     click : function(){
13593         this.fireEvent("click", this);
13594         this.timer = this.click.defer(this.getInterval(), this);
13595     },
13596
13597     // private
13598     getInterval: function(){
13599         if(!this.accelerate){
13600             return this.interval;
13601         }
13602         var pressTime = this.mousedownTime.getElapsed();
13603         if(pressTime < 500){
13604             return 400;
13605         }else if(pressTime < 1700){
13606             return 320;
13607         }else if(pressTime < 2600){
13608             return 250;
13609         }else if(pressTime < 3500){
13610             return 180;
13611         }else if(pressTime < 4400){
13612             return 140;
13613         }else if(pressTime < 5300){
13614             return 80;
13615         }else if(pressTime < 6200){
13616             return 50;
13617         }else{
13618             return 10;
13619         }
13620     },
13621
13622     // private
13623     handleMouseOut : function(){
13624         clearTimeout(this.timer);
13625         if(this.pressClass){
13626             this.el.removeClass(this.pressClass);
13627         }
13628         this.el.on("mouseover", this.handleMouseReturn, this);
13629     },
13630
13631     // private
13632     handleMouseReturn : function(){
13633         this.el.un("mouseover", this.handleMouseReturn);
13634         if(this.pressClass){
13635             this.el.addClass(this.pressClass);
13636         }
13637         this.click();
13638     },
13639
13640     // private
13641     handleMouseUp : function(){
13642         clearTimeout(this.timer);
13643         this.el.un("mouseover", this.handleMouseReturn);
13644         this.el.un("mouseout", this.handleMouseOut);
13645         Roo.get(document).un("mouseup", this.handleMouseUp);
13646         this.el.removeClass(this.pressClass);
13647         this.fireEvent("mouseup", this);
13648     }
13649 });/*
13650  * Based on:
13651  * Ext JS Library 1.1.1
13652  * Copyright(c) 2006-2007, Ext JS, LLC.
13653  *
13654  * Originally Released Under LGPL - original licence link has changed is not relivant.
13655  *
13656  * Fork - LGPL
13657  * <script type="text/javascript">
13658  */
13659
13660  
13661 /**
13662  * @class Roo.KeyNav
13663  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13664  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13665  * way to implement custom navigation schemes for any UI component.</p>
13666  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13667  * pageUp, pageDown, del, home, end.  Usage:</p>
13668  <pre><code>
13669 var nav = new Roo.KeyNav("my-element", {
13670     "left" : function(e){
13671         this.moveLeft(e.ctrlKey);
13672     },
13673     "right" : function(e){
13674         this.moveRight(e.ctrlKey);
13675     },
13676     "enter" : function(e){
13677         this.save();
13678     },
13679     scope : this
13680 });
13681 </code></pre>
13682  * @constructor
13683  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13684  * @param {Object} config The config
13685  */
13686 Roo.KeyNav = function(el, config){
13687     this.el = Roo.get(el);
13688     Roo.apply(this, config);
13689     if(!this.disabled){
13690         this.disabled = true;
13691         this.enable();
13692     }
13693 };
13694
13695 Roo.KeyNav.prototype = {
13696     /**
13697      * @cfg {Boolean} disabled
13698      * True to disable this KeyNav instance (defaults to false)
13699      */
13700     disabled : false,
13701     /**
13702      * @cfg {String} defaultEventAction
13703      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13704      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13705      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13706      */
13707     defaultEventAction: "stopEvent",
13708     /**
13709      * @cfg {Boolean} forceKeyDown
13710      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13711      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13712      * handle keydown instead of keypress.
13713      */
13714     forceKeyDown : false,
13715
13716     // private
13717     prepareEvent : function(e){
13718         var k = e.getKey();
13719         var h = this.keyToHandler[k];
13720         //if(h && this[h]){
13721         //    e.stopPropagation();
13722         //}
13723         if(Roo.isSafari && h && k >= 37 && k <= 40){
13724             e.stopEvent();
13725         }
13726     },
13727
13728     // private
13729     relay : function(e){
13730         var k = e.getKey();
13731         var h = this.keyToHandler[k];
13732         if(h && this[h]){
13733             if(this.doRelay(e, this[h], h) !== true){
13734                 e[this.defaultEventAction]();
13735             }
13736         }
13737     },
13738
13739     // private
13740     doRelay : function(e, h, hname){
13741         return h.call(this.scope || this, e);
13742     },
13743
13744     // possible handlers
13745     enter : false,
13746     left : false,
13747     right : false,
13748     up : false,
13749     down : false,
13750     tab : false,
13751     esc : false,
13752     pageUp : false,
13753     pageDown : false,
13754     del : false,
13755     home : false,
13756     end : false,
13757
13758     // quick lookup hash
13759     keyToHandler : {
13760         37 : "left",
13761         39 : "right",
13762         38 : "up",
13763         40 : "down",
13764         33 : "pageUp",
13765         34 : "pageDown",
13766         46 : "del",
13767         36 : "home",
13768         35 : "end",
13769         13 : "enter",
13770         27 : "esc",
13771         9  : "tab"
13772     },
13773
13774         /**
13775          * Enable this KeyNav
13776          */
13777         enable: function(){
13778                 if(this.disabled){
13779             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13780             // the EventObject will normalize Safari automatically
13781             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13782                 this.el.on("keydown", this.relay,  this);
13783             }else{
13784                 this.el.on("keydown", this.prepareEvent,  this);
13785                 this.el.on("keypress", this.relay,  this);
13786             }
13787                     this.disabled = false;
13788                 }
13789         },
13790
13791         /**
13792          * Disable this KeyNav
13793          */
13794         disable: function(){
13795                 if(!this.disabled){
13796                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13797                 this.el.un("keydown", this.relay);
13798             }else{
13799                 this.el.un("keydown", this.prepareEvent);
13800                 this.el.un("keypress", this.relay);
13801             }
13802                     this.disabled = true;
13803                 }
13804         }
13805 };/*
13806  * Based on:
13807  * Ext JS Library 1.1.1
13808  * Copyright(c) 2006-2007, Ext JS, LLC.
13809  *
13810  * Originally Released Under LGPL - original licence link has changed is not relivant.
13811  *
13812  * Fork - LGPL
13813  * <script type="text/javascript">
13814  */
13815
13816  
13817 /**
13818  * @class Roo.KeyMap
13819  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13820  * The constructor accepts the same config object as defined by {@link #addBinding}.
13821  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13822  * combination it will call the function with this signature (if the match is a multi-key
13823  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13824  * A KeyMap can also handle a string representation of keys.<br />
13825  * Usage:
13826  <pre><code>
13827 // map one key by key code
13828 var map = new Roo.KeyMap("my-element", {
13829     key: 13, // or Roo.EventObject.ENTER
13830     fn: myHandler,
13831     scope: myObject
13832 });
13833
13834 // map multiple keys to one action by string
13835 var map = new Roo.KeyMap("my-element", {
13836     key: "a\r\n\t",
13837     fn: myHandler,
13838     scope: myObject
13839 });
13840
13841 // map multiple keys to multiple actions by strings and array of codes
13842 var map = new Roo.KeyMap("my-element", [
13843     {
13844         key: [10,13],
13845         fn: function(){ alert("Return was pressed"); }
13846     }, {
13847         key: "abc",
13848         fn: function(){ alert('a, b or c was pressed'); }
13849     }, {
13850         key: "\t",
13851         ctrl:true,
13852         shift:true,
13853         fn: function(){ alert('Control + shift + tab was pressed.'); }
13854     }
13855 ]);
13856 </code></pre>
13857  * <b>Note: A KeyMap starts enabled</b>
13858  * @constructor
13859  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13860  * @param {Object} config The config (see {@link #addBinding})
13861  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13862  */
13863 Roo.KeyMap = function(el, config, eventName){
13864     this.el  = Roo.get(el);
13865     this.eventName = eventName || "keydown";
13866     this.bindings = [];
13867     if(config){
13868         this.addBinding(config);
13869     }
13870     this.enable();
13871 };
13872
13873 Roo.KeyMap.prototype = {
13874     /**
13875      * True to stop the event from bubbling and prevent the default browser action if the
13876      * key was handled by the KeyMap (defaults to false)
13877      * @type Boolean
13878      */
13879     stopEvent : false,
13880
13881     /**
13882      * Add a new binding to this KeyMap. The following config object properties are supported:
13883      * <pre>
13884 Property    Type             Description
13885 ----------  ---------------  ----------------------------------------------------------------------
13886 key         String/Array     A single keycode or an array of keycodes to handle
13887 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13888 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13889 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13890 fn          Function         The function to call when KeyMap finds the expected key combination
13891 scope       Object           The scope of the callback function
13892 </pre>
13893      *
13894      * Usage:
13895      * <pre><code>
13896 // Create a KeyMap
13897 var map = new Roo.KeyMap(document, {
13898     key: Roo.EventObject.ENTER,
13899     fn: handleKey,
13900     scope: this
13901 });
13902
13903 //Add a new binding to the existing KeyMap later
13904 map.addBinding({
13905     key: 'abc',
13906     shift: true,
13907     fn: handleKey,
13908     scope: this
13909 });
13910 </code></pre>
13911      * @param {Object/Array} config A single KeyMap config or an array of configs
13912      */
13913         addBinding : function(config){
13914         if(config instanceof Array){
13915             for(var i = 0, len = config.length; i < len; i++){
13916                 this.addBinding(config[i]);
13917             }
13918             return;
13919         }
13920         var keyCode = config.key,
13921             shift = config.shift, 
13922             ctrl = config.ctrl, 
13923             alt = config.alt,
13924             fn = config.fn,
13925             scope = config.scope;
13926         if(typeof keyCode == "string"){
13927             var ks = [];
13928             var keyString = keyCode.toUpperCase();
13929             for(var j = 0, len = keyString.length; j < len; j++){
13930                 ks.push(keyString.charCodeAt(j));
13931             }
13932             keyCode = ks;
13933         }
13934         var keyArray = keyCode instanceof Array;
13935         var handler = function(e){
13936             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13937                 var k = e.getKey();
13938                 if(keyArray){
13939                     for(var i = 0, len = keyCode.length; i < len; i++){
13940                         if(keyCode[i] == k){
13941                           if(this.stopEvent){
13942                               e.stopEvent();
13943                           }
13944                           fn.call(scope || window, k, e);
13945                           return;
13946                         }
13947                     }
13948                 }else{
13949                     if(k == keyCode){
13950                         if(this.stopEvent){
13951                            e.stopEvent();
13952                         }
13953                         fn.call(scope || window, k, e);
13954                     }
13955                 }
13956             }
13957         };
13958         this.bindings.push(handler);  
13959         },
13960
13961     /**
13962      * Shorthand for adding a single key listener
13963      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13964      * following options:
13965      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13966      * @param {Function} fn The function to call
13967      * @param {Object} scope (optional) The scope of the function
13968      */
13969     on : function(key, fn, scope){
13970         var keyCode, shift, ctrl, alt;
13971         if(typeof key == "object" && !(key instanceof Array)){
13972             keyCode = key.key;
13973             shift = key.shift;
13974             ctrl = key.ctrl;
13975             alt = key.alt;
13976         }else{
13977             keyCode = key;
13978         }
13979         this.addBinding({
13980             key: keyCode,
13981             shift: shift,
13982             ctrl: ctrl,
13983             alt: alt,
13984             fn: fn,
13985             scope: scope
13986         })
13987     },
13988
13989     // private
13990     handleKeyDown : function(e){
13991             if(this.enabled){ //just in case
13992             var b = this.bindings;
13993             for(var i = 0, len = b.length; i < len; i++){
13994                 b[i].call(this, e);
13995             }
13996             }
13997         },
13998         
13999         /**
14000          * Returns true if this KeyMap is enabled
14001          * @return {Boolean} 
14002          */
14003         isEnabled : function(){
14004             return this.enabled;  
14005         },
14006         
14007         /**
14008          * Enables this KeyMap
14009          */
14010         enable: function(){
14011                 if(!this.enabled){
14012                     this.el.on(this.eventName, this.handleKeyDown, this);
14013                     this.enabled = true;
14014                 }
14015         },
14016
14017         /**
14018          * Disable this KeyMap
14019          */
14020         disable: function(){
14021                 if(this.enabled){
14022                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14023                     this.enabled = false;
14024                 }
14025         }
14026 };/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037  
14038 /**
14039  * @class Roo.util.TextMetrics
14040  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14041  * wide, in pixels, a given block of text will be.
14042  * @singleton
14043  */
14044 Roo.util.TextMetrics = function(){
14045     var shared;
14046     return {
14047         /**
14048          * Measures the size of the specified text
14049          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14050          * that can affect the size of the rendered text
14051          * @param {String} text The text to measure
14052          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14053          * in order to accurately measure the text height
14054          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14055          */
14056         measure : function(el, text, fixedWidth){
14057             if(!shared){
14058                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14059             }
14060             shared.bind(el);
14061             shared.setFixedWidth(fixedWidth || 'auto');
14062             return shared.getSize(text);
14063         },
14064
14065         /**
14066          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14067          * the overhead of multiple calls to initialize the style properties on each measurement.
14068          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14069          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14070          * in order to accurately measure the text height
14071          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14072          */
14073         createInstance : function(el, fixedWidth){
14074             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14075         }
14076     };
14077 }();
14078
14079  
14080
14081 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14082     var ml = new Roo.Element(document.createElement('div'));
14083     document.body.appendChild(ml.dom);
14084     ml.position('absolute');
14085     ml.setLeftTop(-1000, -1000);
14086     ml.hide();
14087
14088     if(fixedWidth){
14089         ml.setWidth(fixedWidth);
14090     }
14091      
14092     var instance = {
14093         /**
14094          * Returns the size of the specified text based on the internal element's style and width properties
14095          * @memberOf Roo.util.TextMetrics.Instance#
14096          * @param {String} text The text to measure
14097          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14098          */
14099         getSize : function(text){
14100             ml.update(text);
14101             var s = ml.getSize();
14102             ml.update('');
14103             return s;
14104         },
14105
14106         /**
14107          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14108          * that can affect the size of the rendered text
14109          * @memberOf Roo.util.TextMetrics.Instance#
14110          * @param {String/HTMLElement} el The element, dom node or id
14111          */
14112         bind : function(el){
14113             ml.setStyle(
14114                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14115             );
14116         },
14117
14118         /**
14119          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14120          * to set a fixed width in order to accurately measure the text height.
14121          * @memberOf Roo.util.TextMetrics.Instance#
14122          * @param {Number} width The width to set on the element
14123          */
14124         setFixedWidth : function(width){
14125             ml.setWidth(width);
14126         },
14127
14128         /**
14129          * Returns the measured width of the specified text
14130          * @memberOf Roo.util.TextMetrics.Instance#
14131          * @param {String} text The text to measure
14132          * @return {Number} width The width in pixels
14133          */
14134         getWidth : function(text){
14135             ml.dom.style.width = 'auto';
14136             return this.getSize(text).width;
14137         },
14138
14139         /**
14140          * Returns the measured height of the specified text.  For multiline text, be sure to call
14141          * {@link #setFixedWidth} if necessary.
14142          * @memberOf Roo.util.TextMetrics.Instance#
14143          * @param {String} text The text to measure
14144          * @return {Number} height The height in pixels
14145          */
14146         getHeight : function(text){
14147             return this.getSize(text).height;
14148         }
14149     };
14150
14151     instance.bind(bindTo);
14152
14153     return instance;
14154 };
14155
14156 // backwards compat
14157 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167
14168 /**
14169  * @class Roo.state.Provider
14170  * Abstract base class for state provider implementations. This class provides methods
14171  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14172  * Provider interface.
14173  */
14174 Roo.state.Provider = function(){
14175     /**
14176      * @event statechange
14177      * Fires when a state change occurs.
14178      * @param {Provider} this This state provider
14179      * @param {String} key The state key which was changed
14180      * @param {String} value The encoded value for the state
14181      */
14182     this.addEvents({
14183         "statechange": true
14184     });
14185     this.state = {};
14186     Roo.state.Provider.superclass.constructor.call(this);
14187 };
14188 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14189     /**
14190      * Returns the current value for a key
14191      * @param {String} name The key name
14192      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14193      * @return {Mixed} The state data
14194      */
14195     get : function(name, defaultValue){
14196         return typeof this.state[name] == "undefined" ?
14197             defaultValue : this.state[name];
14198     },
14199     
14200     /**
14201      * Clears a value from the state
14202      * @param {String} name The key name
14203      */
14204     clear : function(name){
14205         delete this.state[name];
14206         this.fireEvent("statechange", this, name, null);
14207     },
14208     
14209     /**
14210      * Sets the value for a key
14211      * @param {String} name The key name
14212      * @param {Mixed} value The value to set
14213      */
14214     set : function(name, value){
14215         this.state[name] = value;
14216         this.fireEvent("statechange", this, name, value);
14217     },
14218     
14219     /**
14220      * Decodes a string previously encoded with {@link #encodeValue}.
14221      * @param {String} value The value to decode
14222      * @return {Mixed} The decoded value
14223      */
14224     decodeValue : function(cookie){
14225         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14226         var matches = re.exec(unescape(cookie));
14227         if(!matches || !matches[1]) return; // non state cookie
14228         var type = matches[1];
14229         var v = matches[2];
14230         switch(type){
14231             case "n":
14232                 return parseFloat(v);
14233             case "d":
14234                 return new Date(Date.parse(v));
14235             case "b":
14236                 return (v == "1");
14237             case "a":
14238                 var all = [];
14239                 var values = v.split("^");
14240                 for(var i = 0, len = values.length; i < len; i++){
14241                     all.push(this.decodeValue(values[i]));
14242                 }
14243                 return all;
14244            case "o":
14245                 var all = {};
14246                 var values = v.split("^");
14247                 for(var i = 0, len = values.length; i < len; i++){
14248                     var kv = values[i].split("=");
14249                     all[kv[0]] = this.decodeValue(kv[1]);
14250                 }
14251                 return all;
14252            default:
14253                 return v;
14254         }
14255     },
14256     
14257     /**
14258      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14259      * @param {Mixed} value The value to encode
14260      * @return {String} The encoded value
14261      */
14262     encodeValue : function(v){
14263         var enc;
14264         if(typeof v == "number"){
14265             enc = "n:" + v;
14266         }else if(typeof v == "boolean"){
14267             enc = "b:" + (v ? "1" : "0");
14268         }else if(v instanceof Date){
14269             enc = "d:" + v.toGMTString();
14270         }else if(v instanceof Array){
14271             var flat = "";
14272             for(var i = 0, len = v.length; i < len; i++){
14273                 flat += this.encodeValue(v[i]);
14274                 if(i != len-1) flat += "^";
14275             }
14276             enc = "a:" + flat;
14277         }else if(typeof v == "object"){
14278             var flat = "";
14279             for(var key in v){
14280                 if(typeof v[key] != "function"){
14281                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14282                 }
14283             }
14284             enc = "o:" + flat.substring(0, flat.length-1);
14285         }else{
14286             enc = "s:" + v;
14287         }
14288         return escape(enc);        
14289     }
14290 });
14291
14292 /*
14293  * Based on:
14294  * Ext JS Library 1.1.1
14295  * Copyright(c) 2006-2007, Ext JS, LLC.
14296  *
14297  * Originally Released Under LGPL - original licence link has changed is not relivant.
14298  *
14299  * Fork - LGPL
14300  * <script type="text/javascript">
14301  */
14302 /**
14303  * @class Roo.state.Manager
14304  * This is the global state manager. By default all components that are "state aware" check this class
14305  * for state information if you don't pass them a custom state provider. In order for this class
14306  * to be useful, it must be initialized with a provider when your application initializes.
14307  <pre><code>
14308 // in your initialization function
14309 init : function(){
14310    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14311    ...
14312    // supposed you have a {@link Roo.BorderLayout}
14313    var layout = new Roo.BorderLayout(...);
14314    layout.restoreState();
14315    // or a {Roo.BasicDialog}
14316    var dialog = new Roo.BasicDialog(...);
14317    dialog.restoreState();
14318  </code></pre>
14319  * @singleton
14320  */
14321 Roo.state.Manager = function(){
14322     var provider = new Roo.state.Provider();
14323     
14324     return {
14325         /**
14326          * Configures the default state provider for your application
14327          * @param {Provider} stateProvider The state provider to set
14328          */
14329         setProvider : function(stateProvider){
14330             provider = stateProvider;
14331         },
14332         
14333         /**
14334          * Returns the current value for a key
14335          * @param {String} name The key name
14336          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14337          * @return {Mixed} The state data
14338          */
14339         get : function(key, defaultValue){
14340             return provider.get(key, defaultValue);
14341         },
14342         
14343         /**
14344          * Sets the value for a key
14345          * @param {String} name The key name
14346          * @param {Mixed} value The state data
14347          */
14348          set : function(key, value){
14349             provider.set(key, value);
14350         },
14351         
14352         /**
14353          * Clears a value from the state
14354          * @param {String} name The key name
14355          */
14356         clear : function(key){
14357             provider.clear(key);
14358         },
14359         
14360         /**
14361          * Gets the currently configured state provider
14362          * @return {Provider} The state provider
14363          */
14364         getProvider : function(){
14365             return provider;
14366         }
14367     };
14368 }();
14369 /*
14370  * Based on:
14371  * Ext JS Library 1.1.1
14372  * Copyright(c) 2006-2007, Ext JS, LLC.
14373  *
14374  * Originally Released Under LGPL - original licence link has changed is not relivant.
14375  *
14376  * Fork - LGPL
14377  * <script type="text/javascript">
14378  */
14379 /**
14380  * @class Roo.state.CookieProvider
14381  * @extends Roo.state.Provider
14382  * The default Provider implementation which saves state via cookies.
14383  * <br />Usage:
14384  <pre><code>
14385    var cp = new Roo.state.CookieProvider({
14386        path: "/cgi-bin/",
14387        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14388        domain: "roojs.com"
14389    })
14390    Roo.state.Manager.setProvider(cp);
14391  </code></pre>
14392  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14393  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14394  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14395  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14396  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14397  * domain the page is running on including the 'www' like 'www.roojs.com')
14398  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14399  * @constructor
14400  * Create a new CookieProvider
14401  * @param {Object} config The configuration object
14402  */
14403 Roo.state.CookieProvider = function(config){
14404     Roo.state.CookieProvider.superclass.constructor.call(this);
14405     this.path = "/";
14406     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14407     this.domain = null;
14408     this.secure = false;
14409     Roo.apply(this, config);
14410     this.state = this.readCookies();
14411 };
14412
14413 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14414     // private
14415     set : function(name, value){
14416         if(typeof value == "undefined" || value === null){
14417             this.clear(name);
14418             return;
14419         }
14420         this.setCookie(name, value);
14421         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14422     },
14423
14424     // private
14425     clear : function(name){
14426         this.clearCookie(name);
14427         Roo.state.CookieProvider.superclass.clear.call(this, name);
14428     },
14429
14430     // private
14431     readCookies : function(){
14432         var cookies = {};
14433         var c = document.cookie + ";";
14434         var re = /\s?(.*?)=(.*?);/g;
14435         var matches;
14436         while((matches = re.exec(c)) != null){
14437             var name = matches[1];
14438             var value = matches[2];
14439             if(name && name.substring(0,3) == "ys-"){
14440                 cookies[name.substr(3)] = this.decodeValue(value);
14441             }
14442         }
14443         return cookies;
14444     },
14445
14446     // private
14447     setCookie : function(name, value){
14448         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14449            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14450            ((this.path == null) ? "" : ("; path=" + this.path)) +
14451            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14452            ((this.secure == true) ? "; secure" : "");
14453     },
14454
14455     // private
14456     clearCookie : function(name){
14457         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14458            ((this.path == null) ? "" : ("; path=" + this.path)) +
14459            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14460            ((this.secure == true) ? "; secure" : "");
14461     }
14462 });/*
14463  * Based on:
14464  * Ext JS Library 1.1.1
14465  * Copyright(c) 2006-2007, Ext JS, LLC.
14466  *
14467  * Originally Released Under LGPL - original licence link has changed is not relivant.
14468  *
14469  * Fork - LGPL
14470  * <script type="text/javascript">
14471  */
14472
14473
14474
14475 /*
14476  * These classes are derivatives of the similarly named classes in the YUI Library.
14477  * The original license:
14478  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14479  * Code licensed under the BSD License:
14480  * http://developer.yahoo.net/yui/license.txt
14481  */
14482
14483 (function() {
14484
14485 var Event=Roo.EventManager;
14486 var Dom=Roo.lib.Dom;
14487
14488 /**
14489  * @class Roo.dd.DragDrop
14490  * @extends Roo.util.Observable
14491  * Defines the interface and base operation of items that that can be
14492  * dragged or can be drop targets.  It was designed to be extended, overriding
14493  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14494  * Up to three html elements can be associated with a DragDrop instance:
14495  * <ul>
14496  * <li>linked element: the element that is passed into the constructor.
14497  * This is the element which defines the boundaries for interaction with
14498  * other DragDrop objects.</li>
14499  * <li>handle element(s): The drag operation only occurs if the element that
14500  * was clicked matches a handle element.  By default this is the linked
14501  * element, but there are times that you will want only a portion of the
14502  * linked element to initiate the drag operation, and the setHandleElId()
14503  * method provides a way to define this.</li>
14504  * <li>drag element: this represents the element that would be moved along
14505  * with the cursor during a drag operation.  By default, this is the linked
14506  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14507  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14508  * </li>
14509  * </ul>
14510  * This class should not be instantiated until the onload event to ensure that
14511  * the associated elements are available.
14512  * The following would define a DragDrop obj that would interact with any
14513  * other DragDrop obj in the "group1" group:
14514  * <pre>
14515  *  dd = new Roo.dd.DragDrop("div1", "group1");
14516  * </pre>
14517  * Since none of the event handlers have been implemented, nothing would
14518  * actually happen if you were to run the code above.  Normally you would
14519  * override this class or one of the default implementations, but you can
14520  * also override the methods you want on an instance of the class...
14521  * <pre>
14522  *  dd.onDragDrop = function(e, id) {
14523  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14524  *  }
14525  * </pre>
14526  * @constructor
14527  * @param {String} id of the element that is linked to this instance
14528  * @param {String} sGroup the group of related DragDrop objects
14529  * @param {object} config an object containing configurable attributes
14530  *                Valid properties for DragDrop:
14531  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14532  */
14533 Roo.dd.DragDrop = function(id, sGroup, config) {
14534     if (id) {
14535         this.init(id, sGroup, config);
14536     }
14537     
14538 };
14539
14540 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14541
14542     /**
14543      * The id of the element associated with this object.  This is what we
14544      * refer to as the "linked element" because the size and position of
14545      * this element is used to determine when the drag and drop objects have
14546      * interacted.
14547      * @property id
14548      * @type String
14549      */
14550     id: null,
14551
14552     /**
14553      * Configuration attributes passed into the constructor
14554      * @property config
14555      * @type object
14556      */
14557     config: null,
14558
14559     /**
14560      * The id of the element that will be dragged.  By default this is same
14561      * as the linked element , but could be changed to another element. Ex:
14562      * Roo.dd.DDProxy
14563      * @property dragElId
14564      * @type String
14565      * @private
14566      */
14567     dragElId: null,
14568
14569     /**
14570      * the id of the element that initiates the drag operation.  By default
14571      * this is the linked element, but could be changed to be a child of this
14572      * element.  This lets us do things like only starting the drag when the
14573      * header element within the linked html element is clicked.
14574      * @property handleElId
14575      * @type String
14576      * @private
14577      */
14578     handleElId: null,
14579
14580     /**
14581      * An associative array of HTML tags that will be ignored if clicked.
14582      * @property invalidHandleTypes
14583      * @type {string: string}
14584      */
14585     invalidHandleTypes: null,
14586
14587     /**
14588      * An associative array of ids for elements that will be ignored if clicked
14589      * @property invalidHandleIds
14590      * @type {string: string}
14591      */
14592     invalidHandleIds: null,
14593
14594     /**
14595      * An indexted array of css class names for elements that will be ignored
14596      * if clicked.
14597      * @property invalidHandleClasses
14598      * @type string[]
14599      */
14600     invalidHandleClasses: null,
14601
14602     /**
14603      * The linked element's absolute X position at the time the drag was
14604      * started
14605      * @property startPageX
14606      * @type int
14607      * @private
14608      */
14609     startPageX: 0,
14610
14611     /**
14612      * The linked element's absolute X position at the time the drag was
14613      * started
14614      * @property startPageY
14615      * @type int
14616      * @private
14617      */
14618     startPageY: 0,
14619
14620     /**
14621      * The group defines a logical collection of DragDrop objects that are
14622      * related.  Instances only get events when interacting with other
14623      * DragDrop object in the same group.  This lets us define multiple
14624      * groups using a single DragDrop subclass if we want.
14625      * @property groups
14626      * @type {string: string}
14627      */
14628     groups: null,
14629
14630     /**
14631      * Individual drag/drop instances can be locked.  This will prevent
14632      * onmousedown start drag.
14633      * @property locked
14634      * @type boolean
14635      * @private
14636      */
14637     locked: false,
14638
14639     /**
14640      * Lock this instance
14641      * @method lock
14642      */
14643     lock: function() { this.locked = true; },
14644
14645     /**
14646      * Unlock this instace
14647      * @method unlock
14648      */
14649     unlock: function() { this.locked = false; },
14650
14651     /**
14652      * By default, all insances can be a drop target.  This can be disabled by
14653      * setting isTarget to false.
14654      * @method isTarget
14655      * @type boolean
14656      */
14657     isTarget: true,
14658
14659     /**
14660      * The padding configured for this drag and drop object for calculating
14661      * the drop zone intersection with this object.
14662      * @method padding
14663      * @type int[]
14664      */
14665     padding: null,
14666
14667     /**
14668      * Cached reference to the linked element
14669      * @property _domRef
14670      * @private
14671      */
14672     _domRef: null,
14673
14674     /**
14675      * Internal typeof flag
14676      * @property __ygDragDrop
14677      * @private
14678      */
14679     __ygDragDrop: true,
14680
14681     /**
14682      * Set to true when horizontal contraints are applied
14683      * @property constrainX
14684      * @type boolean
14685      * @private
14686      */
14687     constrainX: false,
14688
14689     /**
14690      * Set to true when vertical contraints are applied
14691      * @property constrainY
14692      * @type boolean
14693      * @private
14694      */
14695     constrainY: false,
14696
14697     /**
14698      * The left constraint
14699      * @property minX
14700      * @type int
14701      * @private
14702      */
14703     minX: 0,
14704
14705     /**
14706      * The right constraint
14707      * @property maxX
14708      * @type int
14709      * @private
14710      */
14711     maxX: 0,
14712
14713     /**
14714      * The up constraint
14715      * @property minY
14716      * @type int
14717      * @type int
14718      * @private
14719      */
14720     minY: 0,
14721
14722     /**
14723      * The down constraint
14724      * @property maxY
14725      * @type int
14726      * @private
14727      */
14728     maxY: 0,
14729
14730     /**
14731      * Maintain offsets when we resetconstraints.  Set to true when you want
14732      * the position of the element relative to its parent to stay the same
14733      * when the page changes
14734      *
14735      * @property maintainOffset
14736      * @type boolean
14737      */
14738     maintainOffset: false,
14739
14740     /**
14741      * Array of pixel locations the element will snap to if we specified a
14742      * horizontal graduation/interval.  This array is generated automatically
14743      * when you define a tick interval.
14744      * @property xTicks
14745      * @type int[]
14746      */
14747     xTicks: null,
14748
14749     /**
14750      * Array of pixel locations the element will snap to if we specified a
14751      * vertical graduation/interval.  This array is generated automatically
14752      * when you define a tick interval.
14753      * @property yTicks
14754      * @type int[]
14755      */
14756     yTicks: null,
14757
14758     /**
14759      * By default the drag and drop instance will only respond to the primary
14760      * button click (left button for a right-handed mouse).  Set to true to
14761      * allow drag and drop to start with any mouse click that is propogated
14762      * by the browser
14763      * @property primaryButtonOnly
14764      * @type boolean
14765      */
14766     primaryButtonOnly: true,
14767
14768     /**
14769      * The availabe property is false until the linked dom element is accessible.
14770      * @property available
14771      * @type boolean
14772      */
14773     available: false,
14774
14775     /**
14776      * By default, drags can only be initiated if the mousedown occurs in the
14777      * region the linked element is.  This is done in part to work around a
14778      * bug in some browsers that mis-report the mousedown if the previous
14779      * mouseup happened outside of the window.  This property is set to true
14780      * if outer handles are defined.
14781      *
14782      * @property hasOuterHandles
14783      * @type boolean
14784      * @default false
14785      */
14786     hasOuterHandles: false,
14787
14788     /**
14789      * Code that executes immediately before the startDrag event
14790      * @method b4StartDrag
14791      * @private
14792      */
14793     b4StartDrag: function(x, y) { },
14794
14795     /**
14796      * Abstract method called after a drag/drop object is clicked
14797      * and the drag or mousedown time thresholds have beeen met.
14798      * @method startDrag
14799      * @param {int} X click location
14800      * @param {int} Y click location
14801      */
14802     startDrag: function(x, y) { /* override this */ },
14803
14804     /**
14805      * Code that executes immediately before the onDrag event
14806      * @method b4Drag
14807      * @private
14808      */
14809     b4Drag: function(e) { },
14810
14811     /**
14812      * Abstract method called during the onMouseMove event while dragging an
14813      * object.
14814      * @method onDrag
14815      * @param {Event} e the mousemove event
14816      */
14817     onDrag: function(e) { /* override this */ },
14818
14819     /**
14820      * Abstract method called when this element fist begins hovering over
14821      * another DragDrop obj
14822      * @method onDragEnter
14823      * @param {Event} e the mousemove event
14824      * @param {String|DragDrop[]} id In POINT mode, the element
14825      * id this is hovering over.  In INTERSECT mode, an array of one or more
14826      * dragdrop items being hovered over.
14827      */
14828     onDragEnter: function(e, id) { /* override this */ },
14829
14830     /**
14831      * Code that executes immediately before the onDragOver event
14832      * @method b4DragOver
14833      * @private
14834      */
14835     b4DragOver: function(e) { },
14836
14837     /**
14838      * Abstract method called when this element is hovering over another
14839      * DragDrop obj
14840      * @method onDragOver
14841      * @param {Event} e the mousemove event
14842      * @param {String|DragDrop[]} id In POINT mode, the element
14843      * id this is hovering over.  In INTERSECT mode, an array of dd items
14844      * being hovered over.
14845      */
14846     onDragOver: function(e, id) { /* override this */ },
14847
14848     /**
14849      * Code that executes immediately before the onDragOut event
14850      * @method b4DragOut
14851      * @private
14852      */
14853     b4DragOut: function(e) { },
14854
14855     /**
14856      * Abstract method called when we are no longer hovering over an element
14857      * @method onDragOut
14858      * @param {Event} e the mousemove event
14859      * @param {String|DragDrop[]} id In POINT mode, the element
14860      * id this was hovering over.  In INTERSECT mode, an array of dd items
14861      * that the mouse is no longer over.
14862      */
14863     onDragOut: function(e, id) { /* override this */ },
14864
14865     /**
14866      * Code that executes immediately before the onDragDrop event
14867      * @method b4DragDrop
14868      * @private
14869      */
14870     b4DragDrop: function(e) { },
14871
14872     /**
14873      * Abstract method called when this item is dropped on another DragDrop
14874      * obj
14875      * @method onDragDrop
14876      * @param {Event} e the mouseup event
14877      * @param {String|DragDrop[]} id In POINT mode, the element
14878      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14879      * was dropped on.
14880      */
14881     onDragDrop: function(e, id) { /* override this */ },
14882
14883     /**
14884      * Abstract method called when this item is dropped on an area with no
14885      * drop target
14886      * @method onInvalidDrop
14887      * @param {Event} e the mouseup event
14888      */
14889     onInvalidDrop: function(e) { /* override this */ },
14890
14891     /**
14892      * Code that executes immediately before the endDrag event
14893      * @method b4EndDrag
14894      * @private
14895      */
14896     b4EndDrag: function(e) { },
14897
14898     /**
14899      * Fired when we are done dragging the object
14900      * @method endDrag
14901      * @param {Event} e the mouseup event
14902      */
14903     endDrag: function(e) { /* override this */ },
14904
14905     /**
14906      * Code executed immediately before the onMouseDown event
14907      * @method b4MouseDown
14908      * @param {Event} e the mousedown event
14909      * @private
14910      */
14911     b4MouseDown: function(e) {  },
14912
14913     /**
14914      * Event handler that fires when a drag/drop obj gets a mousedown
14915      * @method onMouseDown
14916      * @param {Event} e the mousedown event
14917      */
14918     onMouseDown: function(e) { /* override this */ },
14919
14920     /**
14921      * Event handler that fires when a drag/drop obj gets a mouseup
14922      * @method onMouseUp
14923      * @param {Event} e the mouseup event
14924      */
14925     onMouseUp: function(e) { /* override this */ },
14926
14927     /**
14928      * Override the onAvailable method to do what is needed after the initial
14929      * position was determined.
14930      * @method onAvailable
14931      */
14932     onAvailable: function () {
14933     },
14934
14935     /*
14936      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14937      * @type Object
14938      */
14939     defaultPadding : {left:0, right:0, top:0, bottom:0},
14940
14941     /*
14942      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14943  *
14944  * Usage:
14945  <pre><code>
14946  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14947                 { dragElId: "existingProxyDiv" });
14948  dd.startDrag = function(){
14949      this.constrainTo("parent-id");
14950  };
14951  </code></pre>
14952  * Or you can initalize it using the {@link Roo.Element} object:
14953  <pre><code>
14954  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14955      startDrag : function(){
14956          this.constrainTo("parent-id");
14957      }
14958  });
14959  </code></pre>
14960      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14961      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14962      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14963      * an object containing the sides to pad. For example: {right:10, bottom:10}
14964      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14965      */
14966     constrainTo : function(constrainTo, pad, inContent){
14967         if(typeof pad == "number"){
14968             pad = {left: pad, right:pad, top:pad, bottom:pad};
14969         }
14970         pad = pad || this.defaultPadding;
14971         var b = Roo.get(this.getEl()).getBox();
14972         var ce = Roo.get(constrainTo);
14973         var s = ce.getScroll();
14974         var c, cd = ce.dom;
14975         if(cd == document.body){
14976             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14977         }else{
14978             xy = ce.getXY();
14979             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14980         }
14981
14982
14983         var topSpace = b.y - c.y;
14984         var leftSpace = b.x - c.x;
14985
14986         this.resetConstraints();
14987         this.setXConstraint(leftSpace - (pad.left||0), // left
14988                 c.width - leftSpace - b.width - (pad.right||0) //right
14989         );
14990         this.setYConstraint(topSpace - (pad.top||0), //top
14991                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14992         );
14993     },
14994
14995     /**
14996      * Returns a reference to the linked element
14997      * @method getEl
14998      * @return {HTMLElement} the html element
14999      */
15000     getEl: function() {
15001         if (!this._domRef) {
15002             this._domRef = Roo.getDom(this.id);
15003         }
15004
15005         return this._domRef;
15006     },
15007
15008     /**
15009      * Returns a reference to the actual element to drag.  By default this is
15010      * the same as the html element, but it can be assigned to another
15011      * element. An example of this can be found in Roo.dd.DDProxy
15012      * @method getDragEl
15013      * @return {HTMLElement} the html element
15014      */
15015     getDragEl: function() {
15016         return Roo.getDom(this.dragElId);
15017     },
15018
15019     /**
15020      * Sets up the DragDrop object.  Must be called in the constructor of any
15021      * Roo.dd.DragDrop subclass
15022      * @method init
15023      * @param id the id of the linked element
15024      * @param {String} sGroup the group of related items
15025      * @param {object} config configuration attributes
15026      */
15027     init: function(id, sGroup, config) {
15028         this.initTarget(id, sGroup, config);
15029         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15030         // Event.on(this.id, "selectstart", Event.preventDefault);
15031     },
15032
15033     /**
15034      * Initializes Targeting functionality only... the object does not
15035      * get a mousedown handler.
15036      * @method initTarget
15037      * @param id the id of the linked element
15038      * @param {String} sGroup the group of related items
15039      * @param {object} config configuration attributes
15040      */
15041     initTarget: function(id, sGroup, config) {
15042
15043         // configuration attributes
15044         this.config = config || {};
15045
15046         // create a local reference to the drag and drop manager
15047         this.DDM = Roo.dd.DDM;
15048         // initialize the groups array
15049         this.groups = {};
15050
15051         // assume that we have an element reference instead of an id if the
15052         // parameter is not a string
15053         if (typeof id !== "string") {
15054             id = Roo.id(id);
15055         }
15056
15057         // set the id
15058         this.id = id;
15059
15060         // add to an interaction group
15061         this.addToGroup((sGroup) ? sGroup : "default");
15062
15063         // We don't want to register this as the handle with the manager
15064         // so we just set the id rather than calling the setter.
15065         this.handleElId = id;
15066
15067         // the linked element is the element that gets dragged by default
15068         this.setDragElId(id);
15069
15070         // by default, clicked anchors will not start drag operations.
15071         this.invalidHandleTypes = { A: "A" };
15072         this.invalidHandleIds = {};
15073         this.invalidHandleClasses = [];
15074
15075         this.applyConfig();
15076
15077         this.handleOnAvailable();
15078     },
15079
15080     /**
15081      * Applies the configuration parameters that were passed into the constructor.
15082      * This is supposed to happen at each level through the inheritance chain.  So
15083      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15084      * DragDrop in order to get all of the parameters that are available in
15085      * each object.
15086      * @method applyConfig
15087      */
15088     applyConfig: function() {
15089
15090         // configurable properties:
15091         //    padding, isTarget, maintainOffset, primaryButtonOnly
15092         this.padding           = this.config.padding || [0, 0, 0, 0];
15093         this.isTarget          = (this.config.isTarget !== false);
15094         this.maintainOffset    = (this.config.maintainOffset);
15095         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15096
15097     },
15098
15099     /**
15100      * Executed when the linked element is available
15101      * @method handleOnAvailable
15102      * @private
15103      */
15104     handleOnAvailable: function() {
15105         this.available = true;
15106         this.resetConstraints();
15107         this.onAvailable();
15108     },
15109
15110      /**
15111      * Configures the padding for the target zone in px.  Effectively expands
15112      * (or reduces) the virtual object size for targeting calculations.
15113      * Supports css-style shorthand; if only one parameter is passed, all sides
15114      * will have that padding, and if only two are passed, the top and bottom
15115      * will have the first param, the left and right the second.
15116      * @method setPadding
15117      * @param {int} iTop    Top pad
15118      * @param {int} iRight  Right pad
15119      * @param {int} iBot    Bot pad
15120      * @param {int} iLeft   Left pad
15121      */
15122     setPadding: function(iTop, iRight, iBot, iLeft) {
15123         // this.padding = [iLeft, iRight, iTop, iBot];
15124         if (!iRight && 0 !== iRight) {
15125             this.padding = [iTop, iTop, iTop, iTop];
15126         } else if (!iBot && 0 !== iBot) {
15127             this.padding = [iTop, iRight, iTop, iRight];
15128         } else {
15129             this.padding = [iTop, iRight, iBot, iLeft];
15130         }
15131     },
15132
15133     /**
15134      * Stores the initial placement of the linked element.
15135      * @method setInitialPosition
15136      * @param {int} diffX   the X offset, default 0
15137      * @param {int} diffY   the Y offset, default 0
15138      */
15139     setInitPosition: function(diffX, diffY) {
15140         var el = this.getEl();
15141
15142         if (!this.DDM.verifyEl(el)) {
15143             return;
15144         }
15145
15146         var dx = diffX || 0;
15147         var dy = diffY || 0;
15148
15149         var p = Dom.getXY( el );
15150
15151         this.initPageX = p[0] - dx;
15152         this.initPageY = p[1] - dy;
15153
15154         this.lastPageX = p[0];
15155         this.lastPageY = p[1];
15156
15157
15158         this.setStartPosition(p);
15159     },
15160
15161     /**
15162      * Sets the start position of the element.  This is set when the obj
15163      * is initialized, the reset when a drag is started.
15164      * @method setStartPosition
15165      * @param pos current position (from previous lookup)
15166      * @private
15167      */
15168     setStartPosition: function(pos) {
15169         var p = pos || Dom.getXY( this.getEl() );
15170         this.deltaSetXY = null;
15171
15172         this.startPageX = p[0];
15173         this.startPageY = p[1];
15174     },
15175
15176     /**
15177      * Add this instance to a group of related drag/drop objects.  All
15178      * instances belong to at least one group, and can belong to as many
15179      * groups as needed.
15180      * @method addToGroup
15181      * @param sGroup {string} the name of the group
15182      */
15183     addToGroup: function(sGroup) {
15184         this.groups[sGroup] = true;
15185         this.DDM.regDragDrop(this, sGroup);
15186     },
15187
15188     /**
15189      * Remove's this instance from the supplied interaction group
15190      * @method removeFromGroup
15191      * @param {string}  sGroup  The group to drop
15192      */
15193     removeFromGroup: function(sGroup) {
15194         if (this.groups[sGroup]) {
15195             delete this.groups[sGroup];
15196         }
15197
15198         this.DDM.removeDDFromGroup(this, sGroup);
15199     },
15200
15201     /**
15202      * Allows you to specify that an element other than the linked element
15203      * will be moved with the cursor during a drag
15204      * @method setDragElId
15205      * @param id {string} the id of the element that will be used to initiate the drag
15206      */
15207     setDragElId: function(id) {
15208         this.dragElId = id;
15209     },
15210
15211     /**
15212      * Allows you to specify a child of the linked element that should be
15213      * used to initiate the drag operation.  An example of this would be if
15214      * you have a content div with text and links.  Clicking anywhere in the
15215      * content area would normally start the drag operation.  Use this method
15216      * to specify that an element inside of the content div is the element
15217      * that starts the drag operation.
15218      * @method setHandleElId
15219      * @param id {string} the id of the element that will be used to
15220      * initiate the drag.
15221      */
15222     setHandleElId: function(id) {
15223         if (typeof id !== "string") {
15224             id = Roo.id(id);
15225         }
15226         this.handleElId = id;
15227         this.DDM.regHandle(this.id, id);
15228     },
15229
15230     /**
15231      * Allows you to set an element outside of the linked element as a drag
15232      * handle
15233      * @method setOuterHandleElId
15234      * @param id the id of the element that will be used to initiate the drag
15235      */
15236     setOuterHandleElId: function(id) {
15237         if (typeof id !== "string") {
15238             id = Roo.id(id);
15239         }
15240         Event.on(id, "mousedown",
15241                 this.handleMouseDown, this);
15242         this.setHandleElId(id);
15243
15244         this.hasOuterHandles = true;
15245     },
15246
15247     /**
15248      * Remove all drag and drop hooks for this element
15249      * @method unreg
15250      */
15251     unreg: function() {
15252         Event.un(this.id, "mousedown",
15253                 this.handleMouseDown);
15254         this._domRef = null;
15255         this.DDM._remove(this);
15256     },
15257
15258     destroy : function(){
15259         this.unreg();
15260     },
15261
15262     /**
15263      * Returns true if this instance is locked, or the drag drop mgr is locked
15264      * (meaning that all drag/drop is disabled on the page.)
15265      * @method isLocked
15266      * @return {boolean} true if this obj or all drag/drop is locked, else
15267      * false
15268      */
15269     isLocked: function() {
15270         return (this.DDM.isLocked() || this.locked);
15271     },
15272
15273     /**
15274      * Fired when this object is clicked
15275      * @method handleMouseDown
15276      * @param {Event} e
15277      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15278      * @private
15279      */
15280     handleMouseDown: function(e, oDD){
15281         if (this.primaryButtonOnly && e.button != 0) {
15282             return;
15283         }
15284
15285         if (this.isLocked()) {
15286             return;
15287         }
15288
15289         this.DDM.refreshCache(this.groups);
15290
15291         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15292         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15293         } else {
15294             if (this.clickValidator(e)) {
15295
15296                 // set the initial element position
15297                 this.setStartPosition();
15298
15299
15300                 this.b4MouseDown(e);
15301                 this.onMouseDown(e);
15302
15303                 this.DDM.handleMouseDown(e, this);
15304
15305                 this.DDM.stopEvent(e);
15306             } else {
15307
15308
15309             }
15310         }
15311     },
15312
15313     clickValidator: function(e) {
15314         var target = e.getTarget();
15315         return ( this.isValidHandleChild(target) &&
15316                     (this.id == this.handleElId ||
15317                         this.DDM.handleWasClicked(target, this.id)) );
15318     },
15319
15320     /**
15321      * Allows you to specify a tag name that should not start a drag operation
15322      * when clicked.  This is designed to facilitate embedding links within a
15323      * drag handle that do something other than start the drag.
15324      * @method addInvalidHandleType
15325      * @param {string} tagName the type of element to exclude
15326      */
15327     addInvalidHandleType: function(tagName) {
15328         var type = tagName.toUpperCase();
15329         this.invalidHandleTypes[type] = type;
15330     },
15331
15332     /**
15333      * Lets you to specify an element id for a child of a drag handle
15334      * that should not initiate a drag
15335      * @method addInvalidHandleId
15336      * @param {string} id the element id of the element you wish to ignore
15337      */
15338     addInvalidHandleId: function(id) {
15339         if (typeof id !== "string") {
15340             id = Roo.id(id);
15341         }
15342         this.invalidHandleIds[id] = id;
15343     },
15344
15345     /**
15346      * Lets you specify a css class of elements that will not initiate a drag
15347      * @method addInvalidHandleClass
15348      * @param {string} cssClass the class of the elements you wish to ignore
15349      */
15350     addInvalidHandleClass: function(cssClass) {
15351         this.invalidHandleClasses.push(cssClass);
15352     },
15353
15354     /**
15355      * Unsets an excluded tag name set by addInvalidHandleType
15356      * @method removeInvalidHandleType
15357      * @param {string} tagName the type of element to unexclude
15358      */
15359     removeInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         // this.invalidHandleTypes[type] = null;
15362         delete this.invalidHandleTypes[type];
15363     },
15364
15365     /**
15366      * Unsets an invalid handle id
15367      * @method removeInvalidHandleId
15368      * @param {string} id the id of the element to re-enable
15369      */
15370     removeInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         delete this.invalidHandleIds[id];
15375     },
15376
15377     /**
15378      * Unsets an invalid css class
15379      * @method removeInvalidHandleClass
15380      * @param {string} cssClass the class of the element(s) you wish to
15381      * re-enable
15382      */
15383     removeInvalidHandleClass: function(cssClass) {
15384         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15385             if (this.invalidHandleClasses[i] == cssClass) {
15386                 delete this.invalidHandleClasses[i];
15387             }
15388         }
15389     },
15390
15391     /**
15392      * Checks the tag exclusion list to see if this click should be ignored
15393      * @method isValidHandleChild
15394      * @param {HTMLElement} node the HTMLElement to evaluate
15395      * @return {boolean} true if this is a valid tag type, false if not
15396      */
15397     isValidHandleChild: function(node) {
15398
15399         var valid = true;
15400         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15401         var nodeName;
15402         try {
15403             nodeName = node.nodeName.toUpperCase();
15404         } catch(e) {
15405             nodeName = node.nodeName;
15406         }
15407         valid = valid && !this.invalidHandleTypes[nodeName];
15408         valid = valid && !this.invalidHandleIds[node.id];
15409
15410         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15411             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15412         }
15413
15414
15415         return valid;
15416
15417     },
15418
15419     /**
15420      * Create the array of horizontal tick marks if an interval was specified
15421      * in setXConstraint().
15422      * @method setXTicks
15423      * @private
15424      */
15425     setXTicks: function(iStartX, iTickSize) {
15426         this.xTicks = [];
15427         this.xTickSize = iTickSize;
15428
15429         var tickMap = {};
15430
15431         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15432             if (!tickMap[i]) {
15433                 this.xTicks[this.xTicks.length] = i;
15434                 tickMap[i] = true;
15435             }
15436         }
15437
15438         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15439             if (!tickMap[i]) {
15440                 this.xTicks[this.xTicks.length] = i;
15441                 tickMap[i] = true;
15442             }
15443         }
15444
15445         this.xTicks.sort(this.DDM.numericSort) ;
15446     },
15447
15448     /**
15449      * Create the array of vertical tick marks if an interval was specified in
15450      * setYConstraint().
15451      * @method setYTicks
15452      * @private
15453      */
15454     setYTicks: function(iStartY, iTickSize) {
15455         this.yTicks = [];
15456         this.yTickSize = iTickSize;
15457
15458         var tickMap = {};
15459
15460         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15461             if (!tickMap[i]) {
15462                 this.yTicks[this.yTicks.length] = i;
15463                 tickMap[i] = true;
15464             }
15465         }
15466
15467         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15468             if (!tickMap[i]) {
15469                 this.yTicks[this.yTicks.length] = i;
15470                 tickMap[i] = true;
15471             }
15472         }
15473
15474         this.yTicks.sort(this.DDM.numericSort) ;
15475     },
15476
15477     /**
15478      * By default, the element can be dragged any place on the screen.  Use
15479      * this method to limit the horizontal travel of the element.  Pass in
15480      * 0,0 for the parameters if you want to lock the drag to the y axis.
15481      * @method setXConstraint
15482      * @param {int} iLeft the number of pixels the element can move to the left
15483      * @param {int} iRight the number of pixels the element can move to the
15484      * right
15485      * @param {int} iTickSize optional parameter for specifying that the
15486      * element
15487      * should move iTickSize pixels at a time.
15488      */
15489     setXConstraint: function(iLeft, iRight, iTickSize) {
15490         this.leftConstraint = iLeft;
15491         this.rightConstraint = iRight;
15492
15493         this.minX = this.initPageX - iLeft;
15494         this.maxX = this.initPageX + iRight;
15495         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15496
15497         this.constrainX = true;
15498     },
15499
15500     /**
15501      * Clears any constraints applied to this instance.  Also clears ticks
15502      * since they can't exist independent of a constraint at this time.
15503      * @method clearConstraints
15504      */
15505     clearConstraints: function() {
15506         this.constrainX = false;
15507         this.constrainY = false;
15508         this.clearTicks();
15509     },
15510
15511     /**
15512      * Clears any tick interval defined for this instance
15513      * @method clearTicks
15514      */
15515     clearTicks: function() {
15516         this.xTicks = null;
15517         this.yTicks = null;
15518         this.xTickSize = 0;
15519         this.yTickSize = 0;
15520     },
15521
15522     /**
15523      * By default, the element can be dragged any place on the screen.  Set
15524      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15525      * parameters if you want to lock the drag to the x axis.
15526      * @method setYConstraint
15527      * @param {int} iUp the number of pixels the element can move up
15528      * @param {int} iDown the number of pixels the element can move down
15529      * @param {int} iTickSize optional parameter for specifying that the
15530      * element should move iTickSize pixels at a time.
15531      */
15532     setYConstraint: function(iUp, iDown, iTickSize) {
15533         this.topConstraint = iUp;
15534         this.bottomConstraint = iDown;
15535
15536         this.minY = this.initPageY - iUp;
15537         this.maxY = this.initPageY + iDown;
15538         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15539
15540         this.constrainY = true;
15541
15542     },
15543
15544     /**
15545      * resetConstraints must be called if you manually reposition a dd element.
15546      * @method resetConstraints
15547      * @param {boolean} maintainOffset
15548      */
15549     resetConstraints: function() {
15550
15551
15552         // Maintain offsets if necessary
15553         if (this.initPageX || this.initPageX === 0) {
15554             // figure out how much this thing has moved
15555             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15556             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15557
15558             this.setInitPosition(dx, dy);
15559
15560         // This is the first time we have detected the element's position
15561         } else {
15562             this.setInitPosition();
15563         }
15564
15565         if (this.constrainX) {
15566             this.setXConstraint( this.leftConstraint,
15567                                  this.rightConstraint,
15568                                  this.xTickSize        );
15569         }
15570
15571         if (this.constrainY) {
15572             this.setYConstraint( this.topConstraint,
15573                                  this.bottomConstraint,
15574                                  this.yTickSize         );
15575         }
15576     },
15577
15578     /**
15579      * Normally the drag element is moved pixel by pixel, but we can specify
15580      * that it move a number of pixels at a time.  This method resolves the
15581      * location when we have it set up like this.
15582      * @method getTick
15583      * @param {int} val where we want to place the object
15584      * @param {int[]} tickArray sorted array of valid points
15585      * @return {int} the closest tick
15586      * @private
15587      */
15588     getTick: function(val, tickArray) {
15589
15590         if (!tickArray) {
15591             // If tick interval is not defined, it is effectively 1 pixel,
15592             // so we return the value passed to us.
15593             return val;
15594         } else if (tickArray[0] >= val) {
15595             // The value is lower than the first tick, so we return the first
15596             // tick.
15597             return tickArray[0];
15598         } else {
15599             for (var i=0, len=tickArray.length; i<len; ++i) {
15600                 var next = i + 1;
15601                 if (tickArray[next] && tickArray[next] >= val) {
15602                     var diff1 = val - tickArray[i];
15603                     var diff2 = tickArray[next] - val;
15604                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15605                 }
15606             }
15607
15608             // The value is larger than the last tick, so we return the last
15609             // tick.
15610             return tickArray[tickArray.length - 1];
15611         }
15612     },
15613
15614     /**
15615      * toString method
15616      * @method toString
15617      * @return {string} string representation of the dd obj
15618      */
15619     toString: function() {
15620         return ("DragDrop " + this.id);
15621     }
15622
15623 });
15624
15625 })();
15626 /*
15627  * Based on:
15628  * Ext JS Library 1.1.1
15629  * Copyright(c) 2006-2007, Ext JS, LLC.
15630  *
15631  * Originally Released Under LGPL - original licence link has changed is not relivant.
15632  *
15633  * Fork - LGPL
15634  * <script type="text/javascript">
15635  */
15636
15637
15638 /**
15639  * The drag and drop utility provides a framework for building drag and drop
15640  * applications.  In addition to enabling drag and drop for specific elements,
15641  * the drag and drop elements are tracked by the manager class, and the
15642  * interactions between the various elements are tracked during the drag and
15643  * the implementing code is notified about these important moments.
15644  */
15645
15646 // Only load the library once.  Rewriting the manager class would orphan
15647 // existing drag and drop instances.
15648 if (!Roo.dd.DragDropMgr) {
15649
15650 /**
15651  * @class Roo.dd.DragDropMgr
15652  * DragDropMgr is a singleton that tracks the element interaction for
15653  * all DragDrop items in the window.  Generally, you will not call
15654  * this class directly, but it does have helper methods that could
15655  * be useful in your DragDrop implementations.
15656  * @singleton
15657  */
15658 Roo.dd.DragDropMgr = function() {
15659
15660     var Event = Roo.EventManager;
15661
15662     return {
15663
15664         /**
15665          * Two dimensional Array of registered DragDrop objects.  The first
15666          * dimension is the DragDrop item group, the second the DragDrop
15667          * object.
15668          * @property ids
15669          * @type {string: string}
15670          * @private
15671          * @static
15672          */
15673         ids: {},
15674
15675         /**
15676          * Array of element ids defined as drag handles.  Used to determine
15677          * if the element that generated the mousedown event is actually the
15678          * handle and not the html element itself.
15679          * @property handleIds
15680          * @type {string: string}
15681          * @private
15682          * @static
15683          */
15684         handleIds: {},
15685
15686         /**
15687          * the DragDrop object that is currently being dragged
15688          * @property dragCurrent
15689          * @type DragDrop
15690          * @private
15691          * @static
15692          **/
15693         dragCurrent: null,
15694
15695         /**
15696          * the DragDrop object(s) that are being hovered over
15697          * @property dragOvers
15698          * @type Array
15699          * @private
15700          * @static
15701          */
15702         dragOvers: {},
15703
15704         /**
15705          * the X distance between the cursor and the object being dragged
15706          * @property deltaX
15707          * @type int
15708          * @private
15709          * @static
15710          */
15711         deltaX: 0,
15712
15713         /**
15714          * the Y distance between the cursor and the object being dragged
15715          * @property deltaY
15716          * @type int
15717          * @private
15718          * @static
15719          */
15720         deltaY: 0,
15721
15722         /**
15723          * Flag to determine if we should prevent the default behavior of the
15724          * events we define. By default this is true, but this can be set to
15725          * false if you need the default behavior (not recommended)
15726          * @property preventDefault
15727          * @type boolean
15728          * @static
15729          */
15730         preventDefault: true,
15731
15732         /**
15733          * Flag to determine if we should stop the propagation of the events
15734          * we generate. This is true by default but you may want to set it to
15735          * false if the html element contains other features that require the
15736          * mouse click.
15737          * @property stopPropagation
15738          * @type boolean
15739          * @static
15740          */
15741         stopPropagation: true,
15742
15743         /**
15744          * Internal flag that is set to true when drag and drop has been
15745          * intialized
15746          * @property initialized
15747          * @private
15748          * @static
15749          */
15750         initalized: false,
15751
15752         /**
15753          * All drag and drop can be disabled.
15754          * @property locked
15755          * @private
15756          * @static
15757          */
15758         locked: false,
15759
15760         /**
15761          * Called the first time an element is registered.
15762          * @method init
15763          * @private
15764          * @static
15765          */
15766         init: function() {
15767             this.initialized = true;
15768         },
15769
15770         /**
15771          * In point mode, drag and drop interaction is defined by the
15772          * location of the cursor during the drag/drop
15773          * @property POINT
15774          * @type int
15775          * @static
15776          */
15777         POINT: 0,
15778
15779         /**
15780          * In intersect mode, drag and drop interactio nis defined by the
15781          * overlap of two or more drag and drop objects.
15782          * @property INTERSECT
15783          * @type int
15784          * @static
15785          */
15786         INTERSECT: 1,
15787
15788         /**
15789          * The current drag and drop mode.  Default: POINT
15790          * @property mode
15791          * @type int
15792          * @static
15793          */
15794         mode: 0,
15795
15796         /**
15797          * Runs method on all drag and drop objects
15798          * @method _execOnAll
15799          * @private
15800          * @static
15801          */
15802         _execOnAll: function(sMethod, args) {
15803             for (var i in this.ids) {
15804                 for (var j in this.ids[i]) {
15805                     var oDD = this.ids[i][j];
15806                     if (! this.isTypeOfDD(oDD)) {
15807                         continue;
15808                     }
15809                     oDD[sMethod].apply(oDD, args);
15810                 }
15811             }
15812         },
15813
15814         /**
15815          * Drag and drop initialization.  Sets up the global event handlers
15816          * @method _onLoad
15817          * @private
15818          * @static
15819          */
15820         _onLoad: function() {
15821
15822             this.init();
15823
15824
15825             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15826             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15827             Event.on(window,   "unload",    this._onUnload, this, true);
15828             Event.on(window,   "resize",    this._onResize, this, true);
15829             // Event.on(window,   "mouseout",    this._test);
15830
15831         },
15832
15833         /**
15834          * Reset constraints on all drag and drop objs
15835          * @method _onResize
15836          * @private
15837          * @static
15838          */
15839         _onResize: function(e) {
15840             this._execOnAll("resetConstraints", []);
15841         },
15842
15843         /**
15844          * Lock all drag and drop functionality
15845          * @method lock
15846          * @static
15847          */
15848         lock: function() { this.locked = true; },
15849
15850         /**
15851          * Unlock all drag and drop functionality
15852          * @method unlock
15853          * @static
15854          */
15855         unlock: function() { this.locked = false; },
15856
15857         /**
15858          * Is drag and drop locked?
15859          * @method isLocked
15860          * @return {boolean} True if drag and drop is locked, false otherwise.
15861          * @static
15862          */
15863         isLocked: function() { return this.locked; },
15864
15865         /**
15866          * Location cache that is set for all drag drop objects when a drag is
15867          * initiated, cleared when the drag is finished.
15868          * @property locationCache
15869          * @private
15870          * @static
15871          */
15872         locationCache: {},
15873
15874         /**
15875          * Set useCache to false if you want to force object the lookup of each
15876          * drag and drop linked element constantly during a drag.
15877          * @property useCache
15878          * @type boolean
15879          * @static
15880          */
15881         useCache: true,
15882
15883         /**
15884          * The number of pixels that the mouse needs to move after the
15885          * mousedown before the drag is initiated.  Default=3;
15886          * @property clickPixelThresh
15887          * @type int
15888          * @static
15889          */
15890         clickPixelThresh: 3,
15891
15892         /**
15893          * The number of milliseconds after the mousedown event to initiate the
15894          * drag if we don't get a mouseup event. Default=1000
15895          * @property clickTimeThresh
15896          * @type int
15897          * @static
15898          */
15899         clickTimeThresh: 350,
15900
15901         /**
15902          * Flag that indicates that either the drag pixel threshold or the
15903          * mousdown time threshold has been met
15904          * @property dragThreshMet
15905          * @type boolean
15906          * @private
15907          * @static
15908          */
15909         dragThreshMet: false,
15910
15911         /**
15912          * Timeout used for the click time threshold
15913          * @property clickTimeout
15914          * @type Object
15915          * @private
15916          * @static
15917          */
15918         clickTimeout: null,
15919
15920         /**
15921          * The X position of the mousedown event stored for later use when a
15922          * drag threshold is met.
15923          * @property startX
15924          * @type int
15925          * @private
15926          * @static
15927          */
15928         startX: 0,
15929
15930         /**
15931          * The Y position of the mousedown event stored for later use when a
15932          * drag threshold is met.
15933          * @property startY
15934          * @type int
15935          * @private
15936          * @static
15937          */
15938         startY: 0,
15939
15940         /**
15941          * Each DragDrop instance must be registered with the DragDropMgr.
15942          * This is executed in DragDrop.init()
15943          * @method regDragDrop
15944          * @param {DragDrop} oDD the DragDrop object to register
15945          * @param {String} sGroup the name of the group this element belongs to
15946          * @static
15947          */
15948         regDragDrop: function(oDD, sGroup) {
15949             if (!this.initialized) { this.init(); }
15950
15951             if (!this.ids[sGroup]) {
15952                 this.ids[sGroup] = {};
15953             }
15954             this.ids[sGroup][oDD.id] = oDD;
15955         },
15956
15957         /**
15958          * Removes the supplied dd instance from the supplied group. Executed
15959          * by DragDrop.removeFromGroup, so don't call this function directly.
15960          * @method removeDDFromGroup
15961          * @private
15962          * @static
15963          */
15964         removeDDFromGroup: function(oDD, sGroup) {
15965             if (!this.ids[sGroup]) {
15966                 this.ids[sGroup] = {};
15967             }
15968
15969             var obj = this.ids[sGroup];
15970             if (obj && obj[oDD.id]) {
15971                 delete obj[oDD.id];
15972             }
15973         },
15974
15975         /**
15976          * Unregisters a drag and drop item.  This is executed in
15977          * DragDrop.unreg, use that method instead of calling this directly.
15978          * @method _remove
15979          * @private
15980          * @static
15981          */
15982         _remove: function(oDD) {
15983             for (var g in oDD.groups) {
15984                 if (g && this.ids[g][oDD.id]) {
15985                     delete this.ids[g][oDD.id];
15986                 }
15987             }
15988             delete this.handleIds[oDD.id];
15989         },
15990
15991         /**
15992          * Each DragDrop handle element must be registered.  This is done
15993          * automatically when executing DragDrop.setHandleElId()
15994          * @method regHandle
15995          * @param {String} sDDId the DragDrop id this element is a handle for
15996          * @param {String} sHandleId the id of the element that is the drag
15997          * handle
15998          * @static
15999          */
16000         regHandle: function(sDDId, sHandleId) {
16001             if (!this.handleIds[sDDId]) {
16002                 this.handleIds[sDDId] = {};
16003             }
16004             this.handleIds[sDDId][sHandleId] = sHandleId;
16005         },
16006
16007         /**
16008          * Utility function to determine if a given element has been
16009          * registered as a drag drop item.
16010          * @method isDragDrop
16011          * @param {String} id the element id to check
16012          * @return {boolean} true if this element is a DragDrop item,
16013          * false otherwise
16014          * @static
16015          */
16016         isDragDrop: function(id) {
16017             return ( this.getDDById(id) ) ? true : false;
16018         },
16019
16020         /**
16021          * Returns the drag and drop instances that are in all groups the
16022          * passed in instance belongs to.
16023          * @method getRelated
16024          * @param {DragDrop} p_oDD the obj to get related data for
16025          * @param {boolean} bTargetsOnly if true, only return targetable objs
16026          * @return {DragDrop[]} the related instances
16027          * @static
16028          */
16029         getRelated: function(p_oDD, bTargetsOnly) {
16030             var oDDs = [];
16031             for (var i in p_oDD.groups) {
16032                 for (j in this.ids[i]) {
16033                     var dd = this.ids[i][j];
16034                     if (! this.isTypeOfDD(dd)) {
16035                         continue;
16036                     }
16037                     if (!bTargetsOnly || dd.isTarget) {
16038                         oDDs[oDDs.length] = dd;
16039                     }
16040                 }
16041             }
16042
16043             return oDDs;
16044         },
16045
16046         /**
16047          * Returns true if the specified dd target is a legal target for
16048          * the specifice drag obj
16049          * @method isLegalTarget
16050          * @param {DragDrop} the drag obj
16051          * @param {DragDrop} the target
16052          * @return {boolean} true if the target is a legal target for the
16053          * dd obj
16054          * @static
16055          */
16056         isLegalTarget: function (oDD, oTargetDD) {
16057             var targets = this.getRelated(oDD, true);
16058             for (var i=0, len=targets.length;i<len;++i) {
16059                 if (targets[i].id == oTargetDD.id) {
16060                     return true;
16061                 }
16062             }
16063
16064             return false;
16065         },
16066
16067         /**
16068          * My goal is to be able to transparently determine if an object is
16069          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16070          * returns "object", oDD.constructor.toString() always returns
16071          * "DragDrop" and not the name of the subclass.  So for now it just
16072          * evaluates a well-known variable in DragDrop.
16073          * @method isTypeOfDD
16074          * @param {Object} the object to evaluate
16075          * @return {boolean} true if typeof oDD = DragDrop
16076          * @static
16077          */
16078         isTypeOfDD: function (oDD) {
16079             return (oDD && oDD.__ygDragDrop);
16080         },
16081
16082         /**
16083          * Utility function to determine if a given element has been
16084          * registered as a drag drop handle for the given Drag Drop object.
16085          * @method isHandle
16086          * @param {String} id the element id to check
16087          * @return {boolean} true if this element is a DragDrop handle, false
16088          * otherwise
16089          * @static
16090          */
16091         isHandle: function(sDDId, sHandleId) {
16092             return ( this.handleIds[sDDId] &&
16093                             this.handleIds[sDDId][sHandleId] );
16094         },
16095
16096         /**
16097          * Returns the DragDrop instance for a given id
16098          * @method getDDById
16099          * @param {String} id the id of the DragDrop object
16100          * @return {DragDrop} the drag drop object, null if it is not found
16101          * @static
16102          */
16103         getDDById: function(id) {
16104             for (var i in this.ids) {
16105                 if (this.ids[i][id]) {
16106                     return this.ids[i][id];
16107                 }
16108             }
16109             return null;
16110         },
16111
16112         /**
16113          * Fired after a registered DragDrop object gets the mousedown event.
16114          * Sets up the events required to track the object being dragged
16115          * @method handleMouseDown
16116          * @param {Event} e the event
16117          * @param oDD the DragDrop object being dragged
16118          * @private
16119          * @static
16120          */
16121         handleMouseDown: function(e, oDD) {
16122             if(Roo.QuickTips){
16123                 Roo.QuickTips.disable();
16124             }
16125             this.currentTarget = e.getTarget();
16126
16127             this.dragCurrent = oDD;
16128
16129             var el = oDD.getEl();
16130
16131             // track start position
16132             this.startX = e.getPageX();
16133             this.startY = e.getPageY();
16134
16135             this.deltaX = this.startX - el.offsetLeft;
16136             this.deltaY = this.startY - el.offsetTop;
16137
16138             this.dragThreshMet = false;
16139
16140             this.clickTimeout = setTimeout(
16141                     function() {
16142                         var DDM = Roo.dd.DDM;
16143                         DDM.startDrag(DDM.startX, DDM.startY);
16144                     },
16145                     this.clickTimeThresh );
16146         },
16147
16148         /**
16149          * Fired when either the drag pixel threshol or the mousedown hold
16150          * time threshold has been met.
16151          * @method startDrag
16152          * @param x {int} the X position of the original mousedown
16153          * @param y {int} the Y position of the original mousedown
16154          * @static
16155          */
16156         startDrag: function(x, y) {
16157             clearTimeout(this.clickTimeout);
16158             if (this.dragCurrent) {
16159                 this.dragCurrent.b4StartDrag(x, y);
16160                 this.dragCurrent.startDrag(x, y);
16161             }
16162             this.dragThreshMet = true;
16163         },
16164
16165         /**
16166          * Internal function to handle the mouseup event.  Will be invoked
16167          * from the context of the document.
16168          * @method handleMouseUp
16169          * @param {Event} e the event
16170          * @private
16171          * @static
16172          */
16173         handleMouseUp: function(e) {
16174
16175             if(Roo.QuickTips){
16176                 Roo.QuickTips.enable();
16177             }
16178             if (! this.dragCurrent) {
16179                 return;
16180             }
16181
16182             clearTimeout(this.clickTimeout);
16183
16184             if (this.dragThreshMet) {
16185                 this.fireEvents(e, true);
16186             } else {
16187             }
16188
16189             this.stopDrag(e);
16190
16191             this.stopEvent(e);
16192         },
16193
16194         /**
16195          * Utility to stop event propagation and event default, if these
16196          * features are turned on.
16197          * @method stopEvent
16198          * @param {Event} e the event as returned by this.getEvent()
16199          * @static
16200          */
16201         stopEvent: function(e){
16202             if(this.stopPropagation) {
16203                 e.stopPropagation();
16204             }
16205
16206             if (this.preventDefault) {
16207                 e.preventDefault();
16208             }
16209         },
16210
16211         /**
16212          * Internal function to clean up event handlers after the drag
16213          * operation is complete
16214          * @method stopDrag
16215          * @param {Event} e the event
16216          * @private
16217          * @static
16218          */
16219         stopDrag: function(e) {
16220             // Fire the drag end event for the item that was dragged
16221             if (this.dragCurrent) {
16222                 if (this.dragThreshMet) {
16223                     this.dragCurrent.b4EndDrag(e);
16224                     this.dragCurrent.endDrag(e);
16225                 }
16226
16227                 this.dragCurrent.onMouseUp(e);
16228             }
16229
16230             this.dragCurrent = null;
16231             this.dragOvers = {};
16232         },
16233
16234         /**
16235          * Internal function to handle the mousemove event.  Will be invoked
16236          * from the context of the html element.
16237          *
16238          * @TODO figure out what we can do about mouse events lost when the
16239          * user drags objects beyond the window boundary.  Currently we can
16240          * detect this in internet explorer by verifying that the mouse is
16241          * down during the mousemove event.  Firefox doesn't give us the
16242          * button state on the mousemove event.
16243          * @method handleMouseMove
16244          * @param {Event} e the event
16245          * @private
16246          * @static
16247          */
16248         handleMouseMove: function(e) {
16249             if (! this.dragCurrent) {
16250                 return true;
16251             }
16252
16253             // var button = e.which || e.button;
16254
16255             // check for IE mouseup outside of page boundary
16256             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16257                 this.stopEvent(e);
16258                 return this.handleMouseUp(e);
16259             }
16260
16261             if (!this.dragThreshMet) {
16262                 var diffX = Math.abs(this.startX - e.getPageX());
16263                 var diffY = Math.abs(this.startY - e.getPageY());
16264                 if (diffX > this.clickPixelThresh ||
16265                             diffY > this.clickPixelThresh) {
16266                     this.startDrag(this.startX, this.startY);
16267                 }
16268             }
16269
16270             if (this.dragThreshMet) {
16271                 this.dragCurrent.b4Drag(e);
16272                 this.dragCurrent.onDrag(e);
16273                 if(!this.dragCurrent.moveOnly){
16274                     this.fireEvents(e, false);
16275                 }
16276             }
16277
16278             this.stopEvent(e);
16279
16280             return true;
16281         },
16282
16283         /**
16284          * Iterates over all of the DragDrop elements to find ones we are
16285          * hovering over or dropping on
16286          * @method fireEvents
16287          * @param {Event} e the event
16288          * @param {boolean} isDrop is this a drop op or a mouseover op?
16289          * @private
16290          * @static
16291          */
16292         fireEvents: function(e, isDrop) {
16293             var dc = this.dragCurrent;
16294
16295             // If the user did the mouse up outside of the window, we could
16296             // get here even though we have ended the drag.
16297             if (!dc || dc.isLocked()) {
16298                 return;
16299             }
16300
16301             var pt = e.getPoint();
16302
16303             // cache the previous dragOver array
16304             var oldOvers = [];
16305
16306             var outEvts   = [];
16307             var overEvts  = [];
16308             var dropEvts  = [];
16309             var enterEvts = [];
16310
16311             // Check to see if the object(s) we were hovering over is no longer
16312             // being hovered over so we can fire the onDragOut event
16313             for (var i in this.dragOvers) {
16314
16315                 var ddo = this.dragOvers[i];
16316
16317                 if (! this.isTypeOfDD(ddo)) {
16318                     continue;
16319                 }
16320
16321                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16322                     outEvts.push( ddo );
16323                 }
16324
16325                 oldOvers[i] = true;
16326                 delete this.dragOvers[i];
16327             }
16328
16329             for (var sGroup in dc.groups) {
16330
16331                 if ("string" != typeof sGroup) {
16332                     continue;
16333                 }
16334
16335                 for (i in this.ids[sGroup]) {
16336                     var oDD = this.ids[sGroup][i];
16337                     if (! this.isTypeOfDD(oDD)) {
16338                         continue;
16339                     }
16340
16341                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16342                         if (this.isOverTarget(pt, oDD, this.mode)) {
16343                             // look for drop interactions
16344                             if (isDrop) {
16345                                 dropEvts.push( oDD );
16346                             // look for drag enter and drag over interactions
16347                             } else {
16348
16349                                 // initial drag over: dragEnter fires
16350                                 if (!oldOvers[oDD.id]) {
16351                                     enterEvts.push( oDD );
16352                                 // subsequent drag overs: dragOver fires
16353                                 } else {
16354                                     overEvts.push( oDD );
16355                                 }
16356
16357                                 this.dragOvers[oDD.id] = oDD;
16358                             }
16359                         }
16360                     }
16361                 }
16362             }
16363
16364             if (this.mode) {
16365                 if (outEvts.length) {
16366                     dc.b4DragOut(e, outEvts);
16367                     dc.onDragOut(e, outEvts);
16368                 }
16369
16370                 if (enterEvts.length) {
16371                     dc.onDragEnter(e, enterEvts);
16372                 }
16373
16374                 if (overEvts.length) {
16375                     dc.b4DragOver(e, overEvts);
16376                     dc.onDragOver(e, overEvts);
16377                 }
16378
16379                 if (dropEvts.length) {
16380                     dc.b4DragDrop(e, dropEvts);
16381                     dc.onDragDrop(e, dropEvts);
16382                 }
16383
16384             } else {
16385                 // fire dragout events
16386                 var len = 0;
16387                 for (i=0, len=outEvts.length; i<len; ++i) {
16388                     dc.b4DragOut(e, outEvts[i].id);
16389                     dc.onDragOut(e, outEvts[i].id);
16390                 }
16391
16392                 // fire enter events
16393                 for (i=0,len=enterEvts.length; i<len; ++i) {
16394                     // dc.b4DragEnter(e, oDD.id);
16395                     dc.onDragEnter(e, enterEvts[i].id);
16396                 }
16397
16398                 // fire over events
16399                 for (i=0,len=overEvts.length; i<len; ++i) {
16400                     dc.b4DragOver(e, overEvts[i].id);
16401                     dc.onDragOver(e, overEvts[i].id);
16402                 }
16403
16404                 // fire drop events
16405                 for (i=0, len=dropEvts.length; i<len; ++i) {
16406                     dc.b4DragDrop(e, dropEvts[i].id);
16407                     dc.onDragDrop(e, dropEvts[i].id);
16408                 }
16409
16410             }
16411
16412             // notify about a drop that did not find a target
16413             if (isDrop && !dropEvts.length) {
16414                 dc.onInvalidDrop(e);
16415             }
16416
16417         },
16418
16419         /**
16420          * Helper function for getting the best match from the list of drag
16421          * and drop objects returned by the drag and drop events when we are
16422          * in INTERSECT mode.  It returns either the first object that the
16423          * cursor is over, or the object that has the greatest overlap with
16424          * the dragged element.
16425          * @method getBestMatch
16426          * @param  {DragDrop[]} dds The array of drag and drop objects
16427          * targeted
16428          * @return {DragDrop}       The best single match
16429          * @static
16430          */
16431         getBestMatch: function(dds) {
16432             var winner = null;
16433             // Return null if the input is not what we expect
16434             //if (!dds || !dds.length || dds.length == 0) {
16435                // winner = null;
16436             // If there is only one item, it wins
16437             //} else if (dds.length == 1) {
16438
16439             var len = dds.length;
16440
16441             if (len == 1) {
16442                 winner = dds[0];
16443             } else {
16444                 // Loop through the targeted items
16445                 for (var i=0; i<len; ++i) {
16446                     var dd = dds[i];
16447                     // If the cursor is over the object, it wins.  If the
16448                     // cursor is over multiple matches, the first one we come
16449                     // to wins.
16450                     if (dd.cursorIsOver) {
16451                         winner = dd;
16452                         break;
16453                     // Otherwise the object with the most overlap wins
16454                     } else {
16455                         if (!winner ||
16456                             winner.overlap.getArea() < dd.overlap.getArea()) {
16457                             winner = dd;
16458                         }
16459                     }
16460                 }
16461             }
16462
16463             return winner;
16464         },
16465
16466         /**
16467          * Refreshes the cache of the top-left and bottom-right points of the
16468          * drag and drop objects in the specified group(s).  This is in the
16469          * format that is stored in the drag and drop instance, so typical
16470          * usage is:
16471          * <code>
16472          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16473          * </code>
16474          * Alternatively:
16475          * <code>
16476          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16477          * </code>
16478          * @TODO this really should be an indexed array.  Alternatively this
16479          * method could accept both.
16480          * @method refreshCache
16481          * @param {Object} groups an associative array of groups to refresh
16482          * @static
16483          */
16484         refreshCache: function(groups) {
16485             for (var sGroup in groups) {
16486                 if ("string" != typeof sGroup) {
16487                     continue;
16488                 }
16489                 for (var i in this.ids[sGroup]) {
16490                     var oDD = this.ids[sGroup][i];
16491
16492                     if (this.isTypeOfDD(oDD)) {
16493                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16494                         var loc = this.getLocation(oDD);
16495                         if (loc) {
16496                             this.locationCache[oDD.id] = loc;
16497                         } else {
16498                             delete this.locationCache[oDD.id];
16499                             // this will unregister the drag and drop object if
16500                             // the element is not in a usable state
16501                             // oDD.unreg();
16502                         }
16503                     }
16504                 }
16505             }
16506         },
16507
16508         /**
16509          * This checks to make sure an element exists and is in the DOM.  The
16510          * main purpose is to handle cases where innerHTML is used to remove
16511          * drag and drop objects from the DOM.  IE provides an 'unspecified
16512          * error' when trying to access the offsetParent of such an element
16513          * @method verifyEl
16514          * @param {HTMLElement} el the element to check
16515          * @return {boolean} true if the element looks usable
16516          * @static
16517          */
16518         verifyEl: function(el) {
16519             if (el) {
16520                 var parent;
16521                 if(Roo.isIE){
16522                     try{
16523                         parent = el.offsetParent;
16524                     }catch(e){}
16525                 }else{
16526                     parent = el.offsetParent;
16527                 }
16528                 if (parent) {
16529                     return true;
16530                 }
16531             }
16532
16533             return false;
16534         },
16535
16536         /**
16537          * Returns a Region object containing the drag and drop element's position
16538          * and size, including the padding configured for it
16539          * @method getLocation
16540          * @param {DragDrop} oDD the drag and drop object to get the
16541          *                       location for
16542          * @return {Roo.lib.Region} a Region object representing the total area
16543          *                             the element occupies, including any padding
16544          *                             the instance is configured for.
16545          * @static
16546          */
16547         getLocation: function(oDD) {
16548             if (! this.isTypeOfDD(oDD)) {
16549                 return null;
16550             }
16551
16552             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16553
16554             try {
16555                 pos= Roo.lib.Dom.getXY(el);
16556             } catch (e) { }
16557
16558             if (!pos) {
16559                 return null;
16560             }
16561
16562             x1 = pos[0];
16563             x2 = x1 + el.offsetWidth;
16564             y1 = pos[1];
16565             y2 = y1 + el.offsetHeight;
16566
16567             t = y1 - oDD.padding[0];
16568             r = x2 + oDD.padding[1];
16569             b = y2 + oDD.padding[2];
16570             l = x1 - oDD.padding[3];
16571
16572             return new Roo.lib.Region( t, r, b, l );
16573         },
16574
16575         /**
16576          * Checks the cursor location to see if it over the target
16577          * @method isOverTarget
16578          * @param {Roo.lib.Point} pt The point to evaluate
16579          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16580          * @return {boolean} true if the mouse is over the target
16581          * @private
16582          * @static
16583          */
16584         isOverTarget: function(pt, oTarget, intersect) {
16585             // use cache if available
16586             var loc = this.locationCache[oTarget.id];
16587             if (!loc || !this.useCache) {
16588                 loc = this.getLocation(oTarget);
16589                 this.locationCache[oTarget.id] = loc;
16590
16591             }
16592
16593             if (!loc) {
16594                 return false;
16595             }
16596
16597             oTarget.cursorIsOver = loc.contains( pt );
16598
16599             // DragDrop is using this as a sanity check for the initial mousedown
16600             // in this case we are done.  In POINT mode, if the drag obj has no
16601             // contraints, we are also done. Otherwise we need to evaluate the
16602             // location of the target as related to the actual location of the
16603             // dragged element.
16604             var dc = this.dragCurrent;
16605             if (!dc || !dc.getTargetCoord ||
16606                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16607                 return oTarget.cursorIsOver;
16608             }
16609
16610             oTarget.overlap = null;
16611
16612             // Get the current location of the drag element, this is the
16613             // location of the mouse event less the delta that represents
16614             // where the original mousedown happened on the element.  We
16615             // need to consider constraints and ticks as well.
16616             var pos = dc.getTargetCoord(pt.x, pt.y);
16617
16618             var el = dc.getDragEl();
16619             var curRegion = new Roo.lib.Region( pos.y,
16620                                                    pos.x + el.offsetWidth,
16621                                                    pos.y + el.offsetHeight,
16622                                                    pos.x );
16623
16624             var overlap = curRegion.intersect(loc);
16625
16626             if (overlap) {
16627                 oTarget.overlap = overlap;
16628                 return (intersect) ? true : oTarget.cursorIsOver;
16629             } else {
16630                 return false;
16631             }
16632         },
16633
16634         /**
16635          * unload event handler
16636          * @method _onUnload
16637          * @private
16638          * @static
16639          */
16640         _onUnload: function(e, me) {
16641             Roo.dd.DragDropMgr.unregAll();
16642         },
16643
16644         /**
16645          * Cleans up the drag and drop events and objects.
16646          * @method unregAll
16647          * @private
16648          * @static
16649          */
16650         unregAll: function() {
16651
16652             if (this.dragCurrent) {
16653                 this.stopDrag();
16654                 this.dragCurrent = null;
16655             }
16656
16657             this._execOnAll("unreg", []);
16658
16659             for (i in this.elementCache) {
16660                 delete this.elementCache[i];
16661             }
16662
16663             this.elementCache = {};
16664             this.ids = {};
16665         },
16666
16667         /**
16668          * A cache of DOM elements
16669          * @property elementCache
16670          * @private
16671          * @static
16672          */
16673         elementCache: {},
16674
16675         /**
16676          * Get the wrapper for the DOM element specified
16677          * @method getElWrapper
16678          * @param {String} id the id of the element to get
16679          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16680          * @private
16681          * @deprecated This wrapper isn't that useful
16682          * @static
16683          */
16684         getElWrapper: function(id) {
16685             var oWrapper = this.elementCache[id];
16686             if (!oWrapper || !oWrapper.el) {
16687                 oWrapper = this.elementCache[id] =
16688                     new this.ElementWrapper(Roo.getDom(id));
16689             }
16690             return oWrapper;
16691         },
16692
16693         /**
16694          * Returns the actual DOM element
16695          * @method getElement
16696          * @param {String} id the id of the elment to get
16697          * @return {Object} The element
16698          * @deprecated use Roo.getDom instead
16699          * @static
16700          */
16701         getElement: function(id) {
16702             return Roo.getDom(id);
16703         },
16704
16705         /**
16706          * Returns the style property for the DOM element (i.e.,
16707          * document.getElById(id).style)
16708          * @method getCss
16709          * @param {String} id the id of the elment to get
16710          * @return {Object} The style property of the element
16711          * @deprecated use Roo.getDom instead
16712          * @static
16713          */
16714         getCss: function(id) {
16715             var el = Roo.getDom(id);
16716             return (el) ? el.style : null;
16717         },
16718
16719         /**
16720          * Inner class for cached elements
16721          * @class DragDropMgr.ElementWrapper
16722          * @for DragDropMgr
16723          * @private
16724          * @deprecated
16725          */
16726         ElementWrapper: function(el) {
16727                 /**
16728                  * The element
16729                  * @property el
16730                  */
16731                 this.el = el || null;
16732                 /**
16733                  * The element id
16734                  * @property id
16735                  */
16736                 this.id = this.el && el.id;
16737                 /**
16738                  * A reference to the style property
16739                  * @property css
16740                  */
16741                 this.css = this.el && el.style;
16742             },
16743
16744         /**
16745          * Returns the X position of an html element
16746          * @method getPosX
16747          * @param el the element for which to get the position
16748          * @return {int} the X coordinate
16749          * @for DragDropMgr
16750          * @deprecated use Roo.lib.Dom.getX instead
16751          * @static
16752          */
16753         getPosX: function(el) {
16754             return Roo.lib.Dom.getX(el);
16755         },
16756
16757         /**
16758          * Returns the Y position of an html element
16759          * @method getPosY
16760          * @param el the element for which to get the position
16761          * @return {int} the Y coordinate
16762          * @deprecated use Roo.lib.Dom.getY instead
16763          * @static
16764          */
16765         getPosY: function(el) {
16766             return Roo.lib.Dom.getY(el);
16767         },
16768
16769         /**
16770          * Swap two nodes.  In IE, we use the native method, for others we
16771          * emulate the IE behavior
16772          * @method swapNode
16773          * @param n1 the first node to swap
16774          * @param n2 the other node to swap
16775          * @static
16776          */
16777         swapNode: function(n1, n2) {
16778             if (n1.swapNode) {
16779                 n1.swapNode(n2);
16780             } else {
16781                 var p = n2.parentNode;
16782                 var s = n2.nextSibling;
16783
16784                 if (s == n1) {
16785                     p.insertBefore(n1, n2);
16786                 } else if (n2 == n1.nextSibling) {
16787                     p.insertBefore(n2, n1);
16788                 } else {
16789                     n1.parentNode.replaceChild(n2, n1);
16790                     p.insertBefore(n1, s);
16791                 }
16792             }
16793         },
16794
16795         /**
16796          * Returns the current scroll position
16797          * @method getScroll
16798          * @private
16799          * @static
16800          */
16801         getScroll: function () {
16802             var t, l, dde=document.documentElement, db=document.body;
16803             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16804                 t = dde.scrollTop;
16805                 l = dde.scrollLeft;
16806             } else if (db) {
16807                 t = db.scrollTop;
16808                 l = db.scrollLeft;
16809             } else {
16810
16811             }
16812             return { top: t, left: l };
16813         },
16814
16815         /**
16816          * Returns the specified element style property
16817          * @method getStyle
16818          * @param {HTMLElement} el          the element
16819          * @param {string}      styleProp   the style property
16820          * @return {string} The value of the style property
16821          * @deprecated use Roo.lib.Dom.getStyle
16822          * @static
16823          */
16824         getStyle: function(el, styleProp) {
16825             return Roo.fly(el).getStyle(styleProp);
16826         },
16827
16828         /**
16829          * Gets the scrollTop
16830          * @method getScrollTop
16831          * @return {int} the document's scrollTop
16832          * @static
16833          */
16834         getScrollTop: function () { return this.getScroll().top; },
16835
16836         /**
16837          * Gets the scrollLeft
16838          * @method getScrollLeft
16839          * @return {int} the document's scrollTop
16840          * @static
16841          */
16842         getScrollLeft: function () { return this.getScroll().left; },
16843
16844         /**
16845          * Sets the x/y position of an element to the location of the
16846          * target element.
16847          * @method moveToEl
16848          * @param {HTMLElement} moveEl      The element to move
16849          * @param {HTMLElement} targetEl    The position reference element
16850          * @static
16851          */
16852         moveToEl: function (moveEl, targetEl) {
16853             var aCoord = Roo.lib.Dom.getXY(targetEl);
16854             Roo.lib.Dom.setXY(moveEl, aCoord);
16855         },
16856
16857         /**
16858          * Numeric array sort function
16859          * @method numericSort
16860          * @static
16861          */
16862         numericSort: function(a, b) { return (a - b); },
16863
16864         /**
16865          * Internal counter
16866          * @property _timeoutCount
16867          * @private
16868          * @static
16869          */
16870         _timeoutCount: 0,
16871
16872         /**
16873          * Trying to make the load order less important.  Without this we get
16874          * an error if this file is loaded before the Event Utility.
16875          * @method _addListeners
16876          * @private
16877          * @static
16878          */
16879         _addListeners: function() {
16880             var DDM = Roo.dd.DDM;
16881             if ( Roo.lib.Event && document ) {
16882                 DDM._onLoad();
16883             } else {
16884                 if (DDM._timeoutCount > 2000) {
16885                 } else {
16886                     setTimeout(DDM._addListeners, 10);
16887                     if (document && document.body) {
16888                         DDM._timeoutCount += 1;
16889                     }
16890                 }
16891             }
16892         },
16893
16894         /**
16895          * Recursively searches the immediate parent and all child nodes for
16896          * the handle element in order to determine wheter or not it was
16897          * clicked.
16898          * @method handleWasClicked
16899          * @param node the html element to inspect
16900          * @static
16901          */
16902         handleWasClicked: function(node, id) {
16903             if (this.isHandle(id, node.id)) {
16904                 return true;
16905             } else {
16906                 // check to see if this is a text node child of the one we want
16907                 var p = node.parentNode;
16908
16909                 while (p) {
16910                     if (this.isHandle(id, p.id)) {
16911                         return true;
16912                     } else {
16913                         p = p.parentNode;
16914                     }
16915                 }
16916             }
16917
16918             return false;
16919         }
16920
16921     };
16922
16923 }();
16924
16925 // shorter alias, save a few bytes
16926 Roo.dd.DDM = Roo.dd.DragDropMgr;
16927 Roo.dd.DDM._addListeners();
16928
16929 }/*
16930  * Based on:
16931  * Ext JS Library 1.1.1
16932  * Copyright(c) 2006-2007, Ext JS, LLC.
16933  *
16934  * Originally Released Under LGPL - original licence link has changed is not relivant.
16935  *
16936  * Fork - LGPL
16937  * <script type="text/javascript">
16938  */
16939
16940 /**
16941  * @class Roo.dd.DD
16942  * A DragDrop implementation where the linked element follows the
16943  * mouse cursor during a drag.
16944  * @extends Roo.dd.DragDrop
16945  * @constructor
16946  * @param {String} id the id of the linked element
16947  * @param {String} sGroup the group of related DragDrop items
16948  * @param {object} config an object containing configurable attributes
16949  *                Valid properties for DD:
16950  *                    scroll
16951  */
16952 Roo.dd.DD = function(id, sGroup, config) {
16953     if (id) {
16954         this.init(id, sGroup, config);
16955     }
16956 };
16957
16958 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16959
16960     /**
16961      * When set to true, the utility automatically tries to scroll the browser
16962      * window wehn a drag and drop element is dragged near the viewport boundary.
16963      * Defaults to true.
16964      * @property scroll
16965      * @type boolean
16966      */
16967     scroll: true,
16968
16969     /**
16970      * Sets the pointer offset to the distance between the linked element's top
16971      * left corner and the location the element was clicked
16972      * @method autoOffset
16973      * @param {int} iPageX the X coordinate of the click
16974      * @param {int} iPageY the Y coordinate of the click
16975      */
16976     autoOffset: function(iPageX, iPageY) {
16977         var x = iPageX - this.startPageX;
16978         var y = iPageY - this.startPageY;
16979         this.setDelta(x, y);
16980     },
16981
16982     /**
16983      * Sets the pointer offset.  You can call this directly to force the
16984      * offset to be in a particular location (e.g., pass in 0,0 to set it
16985      * to the center of the object)
16986      * @method setDelta
16987      * @param {int} iDeltaX the distance from the left
16988      * @param {int} iDeltaY the distance from the top
16989      */
16990     setDelta: function(iDeltaX, iDeltaY) {
16991         this.deltaX = iDeltaX;
16992         this.deltaY = iDeltaY;
16993     },
16994
16995     /**
16996      * Sets the drag element to the location of the mousedown or click event,
16997      * maintaining the cursor location relative to the location on the element
16998      * that was clicked.  Override this if you want to place the element in a
16999      * location other than where the cursor is.
17000      * @method setDragElPos
17001      * @param {int} iPageX the X coordinate of the mousedown or drag event
17002      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17003      */
17004     setDragElPos: function(iPageX, iPageY) {
17005         // the first time we do this, we are going to check to make sure
17006         // the element has css positioning
17007
17008         var el = this.getDragEl();
17009         this.alignElWithMouse(el, iPageX, iPageY);
17010     },
17011
17012     /**
17013      * Sets the element to the location of the mousedown or click event,
17014      * maintaining the cursor location relative to the location on the element
17015      * that was clicked.  Override this if you want to place the element in a
17016      * location other than where the cursor is.
17017      * @method alignElWithMouse
17018      * @param {HTMLElement} el the element to move
17019      * @param {int} iPageX the X coordinate of the mousedown or drag event
17020      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17021      */
17022     alignElWithMouse: function(el, iPageX, iPageY) {
17023         var oCoord = this.getTargetCoord(iPageX, iPageY);
17024         var fly = el.dom ? el : Roo.fly(el);
17025         if (!this.deltaSetXY) {
17026             var aCoord = [oCoord.x, oCoord.y];
17027             fly.setXY(aCoord);
17028             var newLeft = fly.getLeft(true);
17029             var newTop  = fly.getTop(true);
17030             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17031         } else {
17032             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17033         }
17034
17035         this.cachePosition(oCoord.x, oCoord.y);
17036         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17037         return oCoord;
17038     },
17039
17040     /**
17041      * Saves the most recent position so that we can reset the constraints and
17042      * tick marks on-demand.  We need to know this so that we can calculate the
17043      * number of pixels the element is offset from its original position.
17044      * @method cachePosition
17045      * @param iPageX the current x position (optional, this just makes it so we
17046      * don't have to look it up again)
17047      * @param iPageY the current y position (optional, this just makes it so we
17048      * don't have to look it up again)
17049      */
17050     cachePosition: function(iPageX, iPageY) {
17051         if (iPageX) {
17052             this.lastPageX = iPageX;
17053             this.lastPageY = iPageY;
17054         } else {
17055             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17056             this.lastPageX = aCoord[0];
17057             this.lastPageY = aCoord[1];
17058         }
17059     },
17060
17061     /**
17062      * Auto-scroll the window if the dragged object has been moved beyond the
17063      * visible window boundary.
17064      * @method autoScroll
17065      * @param {int} x the drag element's x position
17066      * @param {int} y the drag element's y position
17067      * @param {int} h the height of the drag element
17068      * @param {int} w the width of the drag element
17069      * @private
17070      */
17071     autoScroll: function(x, y, h, w) {
17072
17073         if (this.scroll) {
17074             // The client height
17075             var clientH = Roo.lib.Dom.getViewWidth();
17076
17077             // The client width
17078             var clientW = Roo.lib.Dom.getViewHeight();
17079
17080             // The amt scrolled down
17081             var st = this.DDM.getScrollTop();
17082
17083             // The amt scrolled right
17084             var sl = this.DDM.getScrollLeft();
17085
17086             // Location of the bottom of the element
17087             var bot = h + y;
17088
17089             // Location of the right of the element
17090             var right = w + x;
17091
17092             // The distance from the cursor to the bottom of the visible area,
17093             // adjusted so that we don't scroll if the cursor is beyond the
17094             // element drag constraints
17095             var toBot = (clientH + st - y - this.deltaY);
17096
17097             // The distance from the cursor to the right of the visible area
17098             var toRight = (clientW + sl - x - this.deltaX);
17099
17100
17101             // How close to the edge the cursor must be before we scroll
17102             // var thresh = (document.all) ? 100 : 40;
17103             var thresh = 40;
17104
17105             // How many pixels to scroll per autoscroll op.  This helps to reduce
17106             // clunky scrolling. IE is more sensitive about this ... it needs this
17107             // value to be higher.
17108             var scrAmt = (document.all) ? 80 : 30;
17109
17110             // Scroll down if we are near the bottom of the visible page and the
17111             // obj extends below the crease
17112             if ( bot > clientH && toBot < thresh ) {
17113                 window.scrollTo(sl, st + scrAmt);
17114             }
17115
17116             // Scroll up if the window is scrolled down and the top of the object
17117             // goes above the top border
17118             if ( y < st && st > 0 && y - st < thresh ) {
17119                 window.scrollTo(sl, st - scrAmt);
17120             }
17121
17122             // Scroll right if the obj is beyond the right border and the cursor is
17123             // near the border.
17124             if ( right > clientW && toRight < thresh ) {
17125                 window.scrollTo(sl + scrAmt, st);
17126             }
17127
17128             // Scroll left if the window has been scrolled to the right and the obj
17129             // extends past the left border
17130             if ( x < sl && sl > 0 && x - sl < thresh ) {
17131                 window.scrollTo(sl - scrAmt, st);
17132             }
17133         }
17134     },
17135
17136     /**
17137      * Finds the location the element should be placed if we want to move
17138      * it to where the mouse location less the click offset would place us.
17139      * @method getTargetCoord
17140      * @param {int} iPageX the X coordinate of the click
17141      * @param {int} iPageY the Y coordinate of the click
17142      * @return an object that contains the coordinates (Object.x and Object.y)
17143      * @private
17144      */
17145     getTargetCoord: function(iPageX, iPageY) {
17146
17147
17148         var x = iPageX - this.deltaX;
17149         var y = iPageY - this.deltaY;
17150
17151         if (this.constrainX) {
17152             if (x < this.minX) { x = this.minX; }
17153             if (x > this.maxX) { x = this.maxX; }
17154         }
17155
17156         if (this.constrainY) {
17157             if (y < this.minY) { y = this.minY; }
17158             if (y > this.maxY) { y = this.maxY; }
17159         }
17160
17161         x = this.getTick(x, this.xTicks);
17162         y = this.getTick(y, this.yTicks);
17163
17164
17165         return {x:x, y:y};
17166     },
17167
17168     /*
17169      * Sets up config options specific to this class. Overrides
17170      * Roo.dd.DragDrop, but all versions of this method through the
17171      * inheritance chain are called
17172      */
17173     applyConfig: function() {
17174         Roo.dd.DD.superclass.applyConfig.call(this);
17175         this.scroll = (this.config.scroll !== false);
17176     },
17177
17178     /*
17179      * Event that fires prior to the onMouseDown event.  Overrides
17180      * Roo.dd.DragDrop.
17181      */
17182     b4MouseDown: function(e) {
17183         // this.resetConstraints();
17184         this.autoOffset(e.getPageX(),
17185                             e.getPageY());
17186     },
17187
17188     /*
17189      * Event that fires prior to the onDrag event.  Overrides
17190      * Roo.dd.DragDrop.
17191      */
17192     b4Drag: function(e) {
17193         this.setDragElPos(e.getPageX(),
17194                             e.getPageY());
17195     },
17196
17197     toString: function() {
17198         return ("DD " + this.id);
17199     }
17200
17201     //////////////////////////////////////////////////////////////////////////
17202     // Debugging ygDragDrop events that can be overridden
17203     //////////////////////////////////////////////////////////////////////////
17204     /*
17205     startDrag: function(x, y) {
17206     },
17207
17208     onDrag: function(e) {
17209     },
17210
17211     onDragEnter: function(e, id) {
17212     },
17213
17214     onDragOver: function(e, id) {
17215     },
17216
17217     onDragOut: function(e, id) {
17218     },
17219
17220     onDragDrop: function(e, id) {
17221     },
17222
17223     endDrag: function(e) {
17224     }
17225
17226     */
17227
17228 });/*
17229  * Based on:
17230  * Ext JS Library 1.1.1
17231  * Copyright(c) 2006-2007, Ext JS, LLC.
17232  *
17233  * Originally Released Under LGPL - original licence link has changed is not relivant.
17234  *
17235  * Fork - LGPL
17236  * <script type="text/javascript">
17237  */
17238
17239 /**
17240  * @class Roo.dd.DDProxy
17241  * A DragDrop implementation that inserts an empty, bordered div into
17242  * the document that follows the cursor during drag operations.  At the time of
17243  * the click, the frame div is resized to the dimensions of the linked html
17244  * element, and moved to the exact location of the linked element.
17245  *
17246  * References to the "frame" element refer to the single proxy element that
17247  * was created to be dragged in place of all DDProxy elements on the
17248  * page.
17249  *
17250  * @extends Roo.dd.DD
17251  * @constructor
17252  * @param {String} id the id of the linked html element
17253  * @param {String} sGroup the group of related DragDrop objects
17254  * @param {object} config an object containing configurable attributes
17255  *                Valid properties for DDProxy in addition to those in DragDrop:
17256  *                   resizeFrame, centerFrame, dragElId
17257  */
17258 Roo.dd.DDProxy = function(id, sGroup, config) {
17259     if (id) {
17260         this.init(id, sGroup, config);
17261         this.initFrame();
17262     }
17263 };
17264
17265 /**
17266  * The default drag frame div id
17267  * @property Roo.dd.DDProxy.dragElId
17268  * @type String
17269  * @static
17270  */
17271 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17272
17273 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17274
17275     /**
17276      * By default we resize the drag frame to be the same size as the element
17277      * we want to drag (this is to get the frame effect).  We can turn it off
17278      * if we want a different behavior.
17279      * @property resizeFrame
17280      * @type boolean
17281      */
17282     resizeFrame: true,
17283
17284     /**
17285      * By default the frame is positioned exactly where the drag element is, so
17286      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17287      * you do not have constraints on the obj is to have the drag frame centered
17288      * around the cursor.  Set centerFrame to true for this effect.
17289      * @property centerFrame
17290      * @type boolean
17291      */
17292     centerFrame: false,
17293
17294     /**
17295      * Creates the proxy element if it does not yet exist
17296      * @method createFrame
17297      */
17298     createFrame: function() {
17299         var self = this;
17300         var body = document.body;
17301
17302         if (!body || !body.firstChild) {
17303             setTimeout( function() { self.createFrame(); }, 50 );
17304             return;
17305         }
17306
17307         var div = this.getDragEl();
17308
17309         if (!div) {
17310             div    = document.createElement("div");
17311             div.id = this.dragElId;
17312             var s  = div.style;
17313
17314             s.position   = "absolute";
17315             s.visibility = "hidden";
17316             s.cursor     = "move";
17317             s.border     = "2px solid #aaa";
17318             s.zIndex     = 999;
17319
17320             // appendChild can blow up IE if invoked prior to the window load event
17321             // while rendering a table.  It is possible there are other scenarios
17322             // that would cause this to happen as well.
17323             body.insertBefore(div, body.firstChild);
17324         }
17325     },
17326
17327     /**
17328      * Initialization for the drag frame element.  Must be called in the
17329      * constructor of all subclasses
17330      * @method initFrame
17331      */
17332     initFrame: function() {
17333         this.createFrame();
17334     },
17335
17336     applyConfig: function() {
17337         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17338
17339         this.resizeFrame = (this.config.resizeFrame !== false);
17340         this.centerFrame = (this.config.centerFrame);
17341         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17342     },
17343
17344     /**
17345      * Resizes the drag frame to the dimensions of the clicked object, positions
17346      * it over the object, and finally displays it
17347      * @method showFrame
17348      * @param {int} iPageX X click position
17349      * @param {int} iPageY Y click position
17350      * @private
17351      */
17352     showFrame: function(iPageX, iPageY) {
17353         var el = this.getEl();
17354         var dragEl = this.getDragEl();
17355         var s = dragEl.style;
17356
17357         this._resizeProxy();
17358
17359         if (this.centerFrame) {
17360             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17361                            Math.round(parseInt(s.height, 10)/2) );
17362         }
17363
17364         this.setDragElPos(iPageX, iPageY);
17365
17366         Roo.fly(dragEl).show();
17367     },
17368
17369     /**
17370      * The proxy is automatically resized to the dimensions of the linked
17371      * element when a drag is initiated, unless resizeFrame is set to false
17372      * @method _resizeProxy
17373      * @private
17374      */
17375     _resizeProxy: function() {
17376         if (this.resizeFrame) {
17377             var el = this.getEl();
17378             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17379         }
17380     },
17381
17382     // overrides Roo.dd.DragDrop
17383     b4MouseDown: function(e) {
17384         var x = e.getPageX();
17385         var y = e.getPageY();
17386         this.autoOffset(x, y);
17387         this.setDragElPos(x, y);
17388     },
17389
17390     // overrides Roo.dd.DragDrop
17391     b4StartDrag: function(x, y) {
17392         // show the drag frame
17393         this.showFrame(x, y);
17394     },
17395
17396     // overrides Roo.dd.DragDrop
17397     b4EndDrag: function(e) {
17398         Roo.fly(this.getDragEl()).hide();
17399     },
17400
17401     // overrides Roo.dd.DragDrop
17402     // By default we try to move the element to the last location of the frame.
17403     // This is so that the default behavior mirrors that of Roo.dd.DD.
17404     endDrag: function(e) {
17405
17406         var lel = this.getEl();
17407         var del = this.getDragEl();
17408
17409         // Show the drag frame briefly so we can get its position
17410         del.style.visibility = "";
17411
17412         this.beforeMove();
17413         // Hide the linked element before the move to get around a Safari
17414         // rendering bug.
17415         lel.style.visibility = "hidden";
17416         Roo.dd.DDM.moveToEl(lel, del);
17417         del.style.visibility = "hidden";
17418         lel.style.visibility = "";
17419
17420         this.afterDrag();
17421     },
17422
17423     beforeMove : function(){
17424
17425     },
17426
17427     afterDrag : function(){
17428
17429     },
17430
17431     toString: function() {
17432         return ("DDProxy " + this.id);
17433     }
17434
17435 });
17436 /*
17437  * Based on:
17438  * Ext JS Library 1.1.1
17439  * Copyright(c) 2006-2007, Ext JS, LLC.
17440  *
17441  * Originally Released Under LGPL - original licence link has changed is not relivant.
17442  *
17443  * Fork - LGPL
17444  * <script type="text/javascript">
17445  */
17446
17447  /**
17448  * @class Roo.dd.DDTarget
17449  * A DragDrop implementation that does not move, but can be a drop
17450  * target.  You would get the same result by simply omitting implementation
17451  * for the event callbacks, but this way we reduce the processing cost of the
17452  * event listener and the callbacks.
17453  * @extends Roo.dd.DragDrop
17454  * @constructor
17455  * @param {String} id the id of the element that is a drop target
17456  * @param {String} sGroup the group of related DragDrop objects
17457  * @param {object} config an object containing configurable attributes
17458  *                 Valid properties for DDTarget in addition to those in
17459  *                 DragDrop:
17460  *                    none
17461  */
17462 Roo.dd.DDTarget = function(id, sGroup, config) {
17463     if (id) {
17464         this.initTarget(id, sGroup, config);
17465     }
17466     if (config.listeners || config.events) { 
17467        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17468             listeners : config.listeners || {}, 
17469             events : config.events || {} 
17470         });    
17471     }
17472 };
17473
17474 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17475 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17476     toString: function() {
17477         return ("DDTarget " + this.id);
17478     }
17479 });
17480 /*
17481  * Based on:
17482  * Ext JS Library 1.1.1
17483  * Copyright(c) 2006-2007, Ext JS, LLC.
17484  *
17485  * Originally Released Under LGPL - original licence link has changed is not relivant.
17486  *
17487  * Fork - LGPL
17488  * <script type="text/javascript">
17489  */
17490  
17491
17492 /**
17493  * @class Roo.dd.ScrollManager
17494  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17495  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17496  * @singleton
17497  */
17498 Roo.dd.ScrollManager = function(){
17499     var ddm = Roo.dd.DragDropMgr;
17500     var els = {};
17501     var dragEl = null;
17502     var proc = {};
17503     
17504     var onStop = function(e){
17505         dragEl = null;
17506         clearProc();
17507     };
17508     
17509     var triggerRefresh = function(){
17510         if(ddm.dragCurrent){
17511              ddm.refreshCache(ddm.dragCurrent.groups);
17512         }
17513     };
17514     
17515     var doScroll = function(){
17516         if(ddm.dragCurrent){
17517             var dds = Roo.dd.ScrollManager;
17518             if(!dds.animate){
17519                 if(proc.el.scroll(proc.dir, dds.increment)){
17520                     triggerRefresh();
17521                 }
17522             }else{
17523                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17524             }
17525         }
17526     };
17527     
17528     var clearProc = function(){
17529         if(proc.id){
17530             clearInterval(proc.id);
17531         }
17532         proc.id = 0;
17533         proc.el = null;
17534         proc.dir = "";
17535     };
17536     
17537     var startProc = function(el, dir){
17538         clearProc();
17539         proc.el = el;
17540         proc.dir = dir;
17541         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17542     };
17543     
17544     var onFire = function(e, isDrop){
17545         if(isDrop || !ddm.dragCurrent){ return; }
17546         var dds = Roo.dd.ScrollManager;
17547         if(!dragEl || dragEl != ddm.dragCurrent){
17548             dragEl = ddm.dragCurrent;
17549             // refresh regions on drag start
17550             dds.refreshCache();
17551         }
17552         
17553         var xy = Roo.lib.Event.getXY(e);
17554         var pt = new Roo.lib.Point(xy[0], xy[1]);
17555         for(var id in els){
17556             var el = els[id], r = el._region;
17557             if(r && r.contains(pt) && el.isScrollable()){
17558                 if(r.bottom - pt.y <= dds.thresh){
17559                     if(proc.el != el){
17560                         startProc(el, "down");
17561                     }
17562                     return;
17563                 }else if(r.right - pt.x <= dds.thresh){
17564                     if(proc.el != el){
17565                         startProc(el, "left");
17566                     }
17567                     return;
17568                 }else if(pt.y - r.top <= dds.thresh){
17569                     if(proc.el != el){
17570                         startProc(el, "up");
17571                     }
17572                     return;
17573                 }else if(pt.x - r.left <= dds.thresh){
17574                     if(proc.el != el){
17575                         startProc(el, "right");
17576                     }
17577                     return;
17578                 }
17579             }
17580         }
17581         clearProc();
17582     };
17583     
17584     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17585     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17586     
17587     return {
17588         /**
17589          * Registers new overflow element(s) to auto scroll
17590          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17591          */
17592         register : function(el){
17593             if(el instanceof Array){
17594                 for(var i = 0, len = el.length; i < len; i++) {
17595                         this.register(el[i]);
17596                 }
17597             }else{
17598                 el = Roo.get(el);
17599                 els[el.id] = el;
17600             }
17601         },
17602         
17603         /**
17604          * Unregisters overflow element(s) so they are no longer scrolled
17605          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17606          */
17607         unregister : function(el){
17608             if(el instanceof Array){
17609                 for(var i = 0, len = el.length; i < len; i++) {
17610                         this.unregister(el[i]);
17611                 }
17612             }else{
17613                 el = Roo.get(el);
17614                 delete els[el.id];
17615             }
17616         },
17617         
17618         /**
17619          * The number of pixels from the edge of a container the pointer needs to be to 
17620          * trigger scrolling (defaults to 25)
17621          * @type Number
17622          */
17623         thresh : 25,
17624         
17625         /**
17626          * The number of pixels to scroll in each scroll increment (defaults to 50)
17627          * @type Number
17628          */
17629         increment : 100,
17630         
17631         /**
17632          * The frequency of scrolls in milliseconds (defaults to 500)
17633          * @type Number
17634          */
17635         frequency : 500,
17636         
17637         /**
17638          * True to animate the scroll (defaults to true)
17639          * @type Boolean
17640          */
17641         animate: true,
17642         
17643         /**
17644          * The animation duration in seconds - 
17645          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17646          * @type Number
17647          */
17648         animDuration: .4,
17649         
17650         /**
17651          * Manually trigger a cache refresh.
17652          */
17653         refreshCache : function(){
17654             for(var id in els){
17655                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17656                     els[id]._region = els[id].getRegion();
17657                 }
17658             }
17659         }
17660     };
17661 }();/*
17662  * Based on:
17663  * Ext JS Library 1.1.1
17664  * Copyright(c) 2006-2007, Ext JS, LLC.
17665  *
17666  * Originally Released Under LGPL - original licence link has changed is not relivant.
17667  *
17668  * Fork - LGPL
17669  * <script type="text/javascript">
17670  */
17671  
17672
17673 /**
17674  * @class Roo.dd.Registry
17675  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17676  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17677  * @singleton
17678  */
17679 Roo.dd.Registry = function(){
17680     var elements = {}; 
17681     var handles = {}; 
17682     var autoIdSeed = 0;
17683
17684     var getId = function(el, autogen){
17685         if(typeof el == "string"){
17686             return el;
17687         }
17688         var id = el.id;
17689         if(!id && autogen !== false){
17690             id = "roodd-" + (++autoIdSeed);
17691             el.id = id;
17692         }
17693         return id;
17694     };
17695     
17696     return {
17697     /**
17698      * Register a drag drop element
17699      * @param {String|HTMLElement} element The id or DOM node to register
17700      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17701      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17702      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17703      * populated in the data object (if applicable):
17704      * <pre>
17705 Value      Description<br />
17706 ---------  ------------------------------------------<br />
17707 handles    Array of DOM nodes that trigger dragging<br />
17708            for the element being registered<br />
17709 isHandle   True if the element passed in triggers<br />
17710            dragging itself, else false
17711 </pre>
17712      */
17713         register : function(el, data){
17714             data = data || {};
17715             if(typeof el == "string"){
17716                 el = document.getElementById(el);
17717             }
17718             data.ddel = el;
17719             elements[getId(el)] = data;
17720             if(data.isHandle !== false){
17721                 handles[data.ddel.id] = data;
17722             }
17723             if(data.handles){
17724                 var hs = data.handles;
17725                 for(var i = 0, len = hs.length; i < len; i++){
17726                         handles[getId(hs[i])] = data;
17727                 }
17728             }
17729         },
17730
17731     /**
17732      * Unregister a drag drop element
17733      * @param {String|HTMLElement}  element The id or DOM node to unregister
17734      */
17735         unregister : function(el){
17736             var id = getId(el, false);
17737             var data = elements[id];
17738             if(data){
17739                 delete elements[id];
17740                 if(data.handles){
17741                     var hs = data.handles;
17742                     for(var i = 0, len = hs.length; i < len; i++){
17743                         delete handles[getId(hs[i], false)];
17744                     }
17745                 }
17746             }
17747         },
17748
17749     /**
17750      * Returns the handle registered for a DOM Node by id
17751      * @param {String|HTMLElement} id The DOM node or id to look up
17752      * @return {Object} handle The custom handle data
17753      */
17754         getHandle : function(id){
17755             if(typeof id != "string"){ // must be element?
17756                 id = id.id;
17757             }
17758             return handles[id];
17759         },
17760
17761     /**
17762      * Returns the handle that is registered for the DOM node that is the target of the event
17763      * @param {Event} e The event
17764      * @return {Object} handle The custom handle data
17765      */
17766         getHandleFromEvent : function(e){
17767             var t = Roo.lib.Event.getTarget(e);
17768             return t ? handles[t.id] : null;
17769         },
17770
17771     /**
17772      * Returns a custom data object that is registered for a DOM node by id
17773      * @param {String|HTMLElement} id The DOM node or id to look up
17774      * @return {Object} data The custom data
17775      */
17776         getTarget : function(id){
17777             if(typeof id != "string"){ // must be element?
17778                 id = id.id;
17779             }
17780             return elements[id];
17781         },
17782
17783     /**
17784      * Returns a custom data object that is registered for the DOM node that is the target of the event
17785      * @param {Event} e The event
17786      * @return {Object} data The custom data
17787      */
17788         getTargetFromEvent : function(e){
17789             var t = Roo.lib.Event.getTarget(e);
17790             return t ? elements[t.id] || handles[t.id] : null;
17791         }
17792     };
17793 }();/*
17794  * Based on:
17795  * Ext JS Library 1.1.1
17796  * Copyright(c) 2006-2007, Ext JS, LLC.
17797  *
17798  * Originally Released Under LGPL - original licence link has changed is not relivant.
17799  *
17800  * Fork - LGPL
17801  * <script type="text/javascript">
17802  */
17803  
17804
17805 /**
17806  * @class Roo.dd.StatusProxy
17807  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17808  * default drag proxy used by all Roo.dd components.
17809  * @constructor
17810  * @param {Object} config
17811  */
17812 Roo.dd.StatusProxy = function(config){
17813     Roo.apply(this, config);
17814     this.id = this.id || Roo.id();
17815     this.el = new Roo.Layer({
17816         dh: {
17817             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17818                 {tag: "div", cls: "x-dd-drop-icon"},
17819                 {tag: "div", cls: "x-dd-drag-ghost"}
17820             ]
17821         }, 
17822         shadow: !config || config.shadow !== false
17823     });
17824     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17825     this.dropStatus = this.dropNotAllowed;
17826 };
17827
17828 Roo.dd.StatusProxy.prototype = {
17829     /**
17830      * @cfg {String} dropAllowed
17831      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17832      */
17833     dropAllowed : "x-dd-drop-ok",
17834     /**
17835      * @cfg {String} dropNotAllowed
17836      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17837      */
17838     dropNotAllowed : "x-dd-drop-nodrop",
17839
17840     /**
17841      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17842      * over the current target element.
17843      * @param {String} cssClass The css class for the new drop status indicator image
17844      */
17845     setStatus : function(cssClass){
17846         cssClass = cssClass || this.dropNotAllowed;
17847         if(this.dropStatus != cssClass){
17848             this.el.replaceClass(this.dropStatus, cssClass);
17849             this.dropStatus = cssClass;
17850         }
17851     },
17852
17853     /**
17854      * Resets the status indicator to the default dropNotAllowed value
17855      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17856      */
17857     reset : function(clearGhost){
17858         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17859         this.dropStatus = this.dropNotAllowed;
17860         if(clearGhost){
17861             this.ghost.update("");
17862         }
17863     },
17864
17865     /**
17866      * Updates the contents of the ghost element
17867      * @param {String} html The html that will replace the current innerHTML of the ghost element
17868      */
17869     update : function(html){
17870         if(typeof html == "string"){
17871             this.ghost.update(html);
17872         }else{
17873             this.ghost.update("");
17874             html.style.margin = "0";
17875             this.ghost.dom.appendChild(html);
17876         }
17877         // ensure float = none set?? cant remember why though.
17878         var el = this.ghost.dom.firstChild;
17879                 if(el){
17880                         Roo.fly(el).setStyle('float', 'none');
17881                 }
17882     },
17883     
17884     /**
17885      * Returns the underlying proxy {@link Roo.Layer}
17886      * @return {Roo.Layer} el
17887     */
17888     getEl : function(){
17889         return this.el;
17890     },
17891
17892     /**
17893      * Returns the ghost element
17894      * @return {Roo.Element} el
17895      */
17896     getGhost : function(){
17897         return this.ghost;
17898     },
17899
17900     /**
17901      * Hides the proxy
17902      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17903      */
17904     hide : function(clear){
17905         this.el.hide();
17906         if(clear){
17907             this.reset(true);
17908         }
17909     },
17910
17911     /**
17912      * Stops the repair animation if it's currently running
17913      */
17914     stop : function(){
17915         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17916             this.anim.stop();
17917         }
17918     },
17919
17920     /**
17921      * Displays this proxy
17922      */
17923     show : function(){
17924         this.el.show();
17925     },
17926
17927     /**
17928      * Force the Layer to sync its shadow and shim positions to the element
17929      */
17930     sync : function(){
17931         this.el.sync();
17932     },
17933
17934     /**
17935      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17936      * invalid drop operation by the item being dragged.
17937      * @param {Array} xy The XY position of the element ([x, y])
17938      * @param {Function} callback The function to call after the repair is complete
17939      * @param {Object} scope The scope in which to execute the callback
17940      */
17941     repair : function(xy, callback, scope){
17942         this.callback = callback;
17943         this.scope = scope;
17944         if(xy && this.animRepair !== false){
17945             this.el.addClass("x-dd-drag-repair");
17946             this.el.hideUnders(true);
17947             this.anim = this.el.shift({
17948                 duration: this.repairDuration || .5,
17949                 easing: 'easeOut',
17950                 xy: xy,
17951                 stopFx: true,
17952                 callback: this.afterRepair,
17953                 scope: this
17954             });
17955         }else{
17956             this.afterRepair();
17957         }
17958     },
17959
17960     // private
17961     afterRepair : function(){
17962         this.hide(true);
17963         if(typeof this.callback == "function"){
17964             this.callback.call(this.scope || this);
17965         }
17966         this.callback = null;
17967         this.scope = null;
17968     }
17969 };/*
17970  * Based on:
17971  * Ext JS Library 1.1.1
17972  * Copyright(c) 2006-2007, Ext JS, LLC.
17973  *
17974  * Originally Released Under LGPL - original licence link has changed is not relivant.
17975  *
17976  * Fork - LGPL
17977  * <script type="text/javascript">
17978  */
17979
17980 /**
17981  * @class Roo.dd.DragSource
17982  * @extends Roo.dd.DDProxy
17983  * A simple class that provides the basic implementation needed to make any element draggable.
17984  * @constructor
17985  * @param {String/HTMLElement/Element} el The container element
17986  * @param {Object} config
17987  */
17988 Roo.dd.DragSource = function(el, config){
17989     this.el = Roo.get(el);
17990     this.dragData = {};
17991     
17992     Roo.apply(this, config);
17993     
17994     if(!this.proxy){
17995         this.proxy = new Roo.dd.StatusProxy();
17996     }
17997
17998     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17999           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18000     
18001     this.dragging = false;
18002 };
18003
18004 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18005     /**
18006      * @cfg {String} dropAllowed
18007      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18008      */
18009     dropAllowed : "x-dd-drop-ok",
18010     /**
18011      * @cfg {String} dropNotAllowed
18012      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18013      */
18014     dropNotAllowed : "x-dd-drop-nodrop",
18015
18016     /**
18017      * Returns the data object associated with this drag source
18018      * @return {Object} data An object containing arbitrary data
18019      */
18020     getDragData : function(e){
18021         return this.dragData;
18022     },
18023
18024     // private
18025     onDragEnter : function(e, id){
18026         var target = Roo.dd.DragDropMgr.getDDById(id);
18027         this.cachedTarget = target;
18028         if(this.beforeDragEnter(target, e, id) !== false){
18029             if(target.isNotifyTarget){
18030                 var status = target.notifyEnter(this, e, this.dragData);
18031                 this.proxy.setStatus(status);
18032             }else{
18033                 this.proxy.setStatus(this.dropAllowed);
18034             }
18035             
18036             if(this.afterDragEnter){
18037                 /**
18038                  * An empty function by default, but provided so that you can perform a custom action
18039                  * when the dragged item enters the drop target by providing an implementation.
18040                  * @param {Roo.dd.DragDrop} target The drop target
18041                  * @param {Event} e The event object
18042                  * @param {String} id The id of the dragged element
18043                  * @method afterDragEnter
18044                  */
18045                 this.afterDragEnter(target, e, id);
18046             }
18047         }
18048     },
18049
18050     /**
18051      * An empty function by default, but provided so that you can perform a custom action
18052      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18053      * @param {Roo.dd.DragDrop} target The drop target
18054      * @param {Event} e The event object
18055      * @param {String} id The id of the dragged element
18056      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18057      */
18058     beforeDragEnter : function(target, e, id){
18059         return true;
18060     },
18061
18062     // private
18063     alignElWithMouse: function() {
18064         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18065         this.proxy.sync();
18066     },
18067
18068     // private
18069     onDragOver : function(e, id){
18070         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18071         if(this.beforeDragOver(target, e, id) !== false){
18072             if(target.isNotifyTarget){
18073                 var status = target.notifyOver(this, e, this.dragData);
18074                 this.proxy.setStatus(status);
18075             }
18076
18077             if(this.afterDragOver){
18078                 /**
18079                  * An empty function by default, but provided so that you can perform a custom action
18080                  * while the dragged item is over the drop target by providing an implementation.
18081                  * @param {Roo.dd.DragDrop} target The drop target
18082                  * @param {Event} e The event object
18083                  * @param {String} id The id of the dragged element
18084                  * @method afterDragOver
18085                  */
18086                 this.afterDragOver(target, e, id);
18087             }
18088         }
18089     },
18090
18091     /**
18092      * An empty function by default, but provided so that you can perform a custom action
18093      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18094      * @param {Roo.dd.DragDrop} target The drop target
18095      * @param {Event} e The event object
18096      * @param {String} id The id of the dragged element
18097      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18098      */
18099     beforeDragOver : function(target, e, id){
18100         return true;
18101     },
18102
18103     // private
18104     onDragOut : function(e, id){
18105         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18106         if(this.beforeDragOut(target, e, id) !== false){
18107             if(target.isNotifyTarget){
18108                 target.notifyOut(this, e, this.dragData);
18109             }
18110             this.proxy.reset();
18111             if(this.afterDragOut){
18112                 /**
18113                  * An empty function by default, but provided so that you can perform a custom action
18114                  * after the dragged item is dragged out of the target without dropping.
18115                  * @param {Roo.dd.DragDrop} target The drop target
18116                  * @param {Event} e The event object
18117                  * @param {String} id The id of the dragged element
18118                  * @method afterDragOut
18119                  */
18120                 this.afterDragOut(target, e, id);
18121             }
18122         }
18123         this.cachedTarget = null;
18124     },
18125
18126     /**
18127      * An empty function by default, but provided so that you can perform a custom action before the dragged
18128      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18129      * @param {Roo.dd.DragDrop} target The drop target
18130      * @param {Event} e The event object
18131      * @param {String} id The id of the dragged element
18132      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18133      */
18134     beforeDragOut : function(target, e, id){
18135         return true;
18136     },
18137     
18138     // private
18139     onDragDrop : function(e, id){
18140         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18141         if(this.beforeDragDrop(target, e, id) !== false){
18142             if(target.isNotifyTarget){
18143                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18144                     this.onValidDrop(target, e, id);
18145                 }else{
18146                     this.onInvalidDrop(target, e, id);
18147                 }
18148             }else{
18149                 this.onValidDrop(target, e, id);
18150             }
18151             
18152             if(this.afterDragDrop){
18153                 /**
18154                  * An empty function by default, but provided so that you can perform a custom action
18155                  * after a valid drag drop has occurred by providing an implementation.
18156                  * @param {Roo.dd.DragDrop} target The drop target
18157                  * @param {Event} e The event object
18158                  * @param {String} id The id of the dropped element
18159                  * @method afterDragDrop
18160                  */
18161                 this.afterDragDrop(target, e, id);
18162             }
18163         }
18164         delete this.cachedTarget;
18165     },
18166
18167     /**
18168      * An empty function by default, but provided so that you can perform a custom action before the dragged
18169      * item is dropped onto the target and optionally cancel the onDragDrop.
18170      * @param {Roo.dd.DragDrop} target The drop target
18171      * @param {Event} e The event object
18172      * @param {String} id The id of the dragged element
18173      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18174      */
18175     beforeDragDrop : function(target, e, id){
18176         return true;
18177     },
18178
18179     // private
18180     onValidDrop : function(target, e, id){
18181         this.hideProxy();
18182         if(this.afterValidDrop){
18183             /**
18184              * An empty function by default, but provided so that you can perform a custom action
18185              * after a valid drop has occurred by providing an implementation.
18186              * @param {Object} target The target DD 
18187              * @param {Event} e The event object
18188              * @param {String} id The id of the dropped element
18189              * @method afterInvalidDrop
18190              */
18191             this.afterValidDrop(target, e, id);
18192         }
18193     },
18194
18195     // private
18196     getRepairXY : function(e, data){
18197         return this.el.getXY();  
18198     },
18199
18200     // private
18201     onInvalidDrop : function(target, e, id){
18202         this.beforeInvalidDrop(target, e, id);
18203         if(this.cachedTarget){
18204             if(this.cachedTarget.isNotifyTarget){
18205                 this.cachedTarget.notifyOut(this, e, this.dragData);
18206             }
18207             this.cacheTarget = null;
18208         }
18209         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18210
18211         if(this.afterInvalidDrop){
18212             /**
18213              * An empty function by default, but provided so that you can perform a custom action
18214              * after an invalid drop has occurred by providing an implementation.
18215              * @param {Event} e The event object
18216              * @param {String} id The id of the dropped element
18217              * @method afterInvalidDrop
18218              */
18219             this.afterInvalidDrop(e, id);
18220         }
18221     },
18222
18223     // private
18224     afterRepair : function(){
18225         if(Roo.enableFx){
18226             this.el.highlight(this.hlColor || "c3daf9");
18227         }
18228         this.dragging = false;
18229     },
18230
18231     /**
18232      * An empty function by default, but provided so that you can perform a custom action after an invalid
18233      * drop has occurred.
18234      * @param {Roo.dd.DragDrop} target The drop target
18235      * @param {Event} e The event object
18236      * @param {String} id The id of the dragged element
18237      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18238      */
18239     beforeInvalidDrop : function(target, e, id){
18240         return true;
18241     },
18242
18243     // private
18244     handleMouseDown : function(e){
18245         if(this.dragging) {
18246             return;
18247         }
18248         var data = this.getDragData(e);
18249         if(data && this.onBeforeDrag(data, e) !== false){
18250             this.dragData = data;
18251             this.proxy.stop();
18252             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18253         } 
18254     },
18255
18256     /**
18257      * An empty function by default, but provided so that you can perform a custom action before the initial
18258      * drag event begins and optionally cancel it.
18259      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18260      * @param {Event} e The event object
18261      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18262      */
18263     onBeforeDrag : function(data, e){
18264         return true;
18265     },
18266
18267     /**
18268      * An empty function by default, but provided so that you can perform a custom action once the initial
18269      * drag event has begun.  The drag cannot be canceled from this function.
18270      * @param {Number} x The x position of the click on the dragged object
18271      * @param {Number} y The y position of the click on the dragged object
18272      */
18273     onStartDrag : Roo.emptyFn,
18274
18275     // private - YUI override
18276     startDrag : function(x, y){
18277         this.proxy.reset();
18278         this.dragging = true;
18279         this.proxy.update("");
18280         this.onInitDrag(x, y);
18281         this.proxy.show();
18282     },
18283
18284     // private
18285     onInitDrag : function(x, y){
18286         var clone = this.el.dom.cloneNode(true);
18287         clone.id = Roo.id(); // prevent duplicate ids
18288         this.proxy.update(clone);
18289         this.onStartDrag(x, y);
18290         return true;
18291     },
18292
18293     /**
18294      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18295      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18296      */
18297     getProxy : function(){
18298         return this.proxy;  
18299     },
18300
18301     /**
18302      * Hides the drag source's {@link Roo.dd.StatusProxy}
18303      */
18304     hideProxy : function(){
18305         this.proxy.hide();  
18306         this.proxy.reset(true);
18307         this.dragging = false;
18308     },
18309
18310     // private
18311     triggerCacheRefresh : function(){
18312         Roo.dd.DDM.refreshCache(this.groups);
18313     },
18314
18315     // private - override to prevent hiding
18316     b4EndDrag: function(e) {
18317     },
18318
18319     // private - override to prevent moving
18320     endDrag : function(e){
18321         this.onEndDrag(this.dragData, e);
18322     },
18323
18324     // private
18325     onEndDrag : function(data, e){
18326     },
18327     
18328     // private - pin to cursor
18329     autoOffset : function(x, y) {
18330         this.setDelta(-12, -20);
18331     }    
18332 });/*
18333  * Based on:
18334  * Ext JS Library 1.1.1
18335  * Copyright(c) 2006-2007, Ext JS, LLC.
18336  *
18337  * Originally Released Under LGPL - original licence link has changed is not relivant.
18338  *
18339  * Fork - LGPL
18340  * <script type="text/javascript">
18341  */
18342
18343
18344 /**
18345  * @class Roo.dd.DropTarget
18346  * @extends Roo.dd.DDTarget
18347  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18348  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18349  * @constructor
18350  * @param {String/HTMLElement/Element} el The container element
18351  * @param {Object} config
18352  */
18353 Roo.dd.DropTarget = function(el, config){
18354     this.el = Roo.get(el);
18355     
18356     var listeners = false; ;
18357     if (config && config.listeners) {
18358         listeners= config.listeners;
18359         delete config.listeners;
18360     }
18361     Roo.apply(this, config);
18362     
18363     if(this.containerScroll){
18364         Roo.dd.ScrollManager.register(this.el);
18365     }
18366     this.addEvents( {
18367          /**
18368          * @scope Roo.dd.DropTarget
18369          */
18370          
18371          /**
18372          * @event enter
18373          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18374          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18375          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18376          * 
18377          * IMPORTANT : it should set this.overClass and this.dropAllowed
18378          * 
18379          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18380          * @param {Event} e The event
18381          * @param {Object} data An object containing arbitrary data supplied by the drag source
18382          */
18383         "enter" : true,
18384         
18385          /**
18386          * @event over
18387          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18388          * This method will be called on every mouse movement while the drag source is over the drop target.
18389          * This default implementation simply returns the dropAllowed config value.
18390          * 
18391          * IMPORTANT : it should set this.dropAllowed
18392          * 
18393          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18394          * @param {Event} e The event
18395          * @param {Object} data An object containing arbitrary data supplied by the drag source
18396          
18397          */
18398         "over" : true,
18399         /**
18400          * @event out
18401          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18402          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18403          * overClass (if any) from the drop element.
18404          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18405          * @param {Event} e The event
18406          * @param {Object} data An object containing arbitrary data supplied by the drag source
18407          */
18408          "out" : true,
18409          
18410         /**
18411          * @event drop
18412          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18413          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18414          * implementation that does something to process the drop event and returns true so that the drag source's
18415          * repair action does not run.
18416          * 
18417          * IMPORTANT : it should set this.success
18418          * 
18419          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18420          * @param {Event} e The event
18421          * @param {Object} data An object containing arbitrary data supplied by the drag source
18422         */
18423          "drop" : true
18424     });
18425             
18426      
18427     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18428         this.el.dom, 
18429         this.ddGroup || this.group,
18430         {
18431             isTarget: true,
18432             listeners : listeners || {} 
18433            
18434         
18435         }
18436     );
18437
18438 };
18439
18440 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18441     /**
18442      * @cfg {String} overClass
18443      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18444      */
18445      /**
18446      * @cfg {String} ddGroup
18447      * The drag drop group to handle drop events for
18448      */
18449      
18450     /**
18451      * @cfg {String} dropAllowed
18452      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18453      */
18454     dropAllowed : "x-dd-drop-ok",
18455     /**
18456      * @cfg {String} dropNotAllowed
18457      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18458      */
18459     dropNotAllowed : "x-dd-drop-nodrop",
18460     /**
18461      * @cfg {boolean} success
18462      * set this after drop listener.. 
18463      */
18464     success : false,
18465     /**
18466      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18467      * if the drop point is valid for over/enter..
18468      */
18469     valid : false,
18470     // private
18471     isTarget : true,
18472
18473     // private
18474     isNotifyTarget : true,
18475     
18476     /**
18477      * @hide
18478      */
18479     notifyEnter : function(dd, e, data)
18480     {
18481         this.valid = true;
18482         this.fireEvent('enter', dd, e, data);
18483         if(this.overClass){
18484             this.el.addClass(this.overClass);
18485         }
18486         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18487             this.valid ? this.dropAllowed : this.dropNotAllowed
18488         );
18489     },
18490
18491     /**
18492      * @hide
18493      */
18494     notifyOver : function(dd, e, data)
18495     {
18496         this.valid = true;
18497         this.fireEvent('over', dd, e, data);
18498         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18499             this.valid ? this.dropAllowed : this.dropNotAllowed
18500         );
18501     },
18502
18503     /**
18504      * @hide
18505      */
18506     notifyOut : function(dd, e, data)
18507     {
18508         this.fireEvent('out', dd, e, data);
18509         if(this.overClass){
18510             this.el.removeClass(this.overClass);
18511         }
18512     },
18513
18514     /**
18515      * @hide
18516      */
18517     notifyDrop : function(dd, e, data)
18518     {
18519         this.success = false;
18520         this.fireEvent('drop', dd, e, data);
18521         return this.success;
18522     }
18523 });/*
18524  * Based on:
18525  * Ext JS Library 1.1.1
18526  * Copyright(c) 2006-2007, Ext JS, LLC.
18527  *
18528  * Originally Released Under LGPL - original licence link has changed is not relivant.
18529  *
18530  * Fork - LGPL
18531  * <script type="text/javascript">
18532  */
18533
18534
18535 /**
18536  * @class Roo.dd.DragZone
18537  * @extends Roo.dd.DragSource
18538  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18539  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18540  * @constructor
18541  * @param {String/HTMLElement/Element} el The container element
18542  * @param {Object} config
18543  */
18544 Roo.dd.DragZone = function(el, config){
18545     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18546     if(this.containerScroll){
18547         Roo.dd.ScrollManager.register(this.el);
18548     }
18549 };
18550
18551 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18552     /**
18553      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18554      * for auto scrolling during drag operations.
18555      */
18556     /**
18557      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18558      * method after a failed drop (defaults to "c3daf9" - light blue)
18559      */
18560
18561     /**
18562      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18563      * for a valid target to drag based on the mouse down. Override this method
18564      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18565      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18566      * @param {EventObject} e The mouse down event
18567      * @return {Object} The dragData
18568      */
18569     getDragData : function(e){
18570         return Roo.dd.Registry.getHandleFromEvent(e);
18571     },
18572     
18573     /**
18574      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18575      * this.dragData.ddel
18576      * @param {Number} x The x position of the click on the dragged object
18577      * @param {Number} y The y position of the click on the dragged object
18578      * @return {Boolean} true to continue the drag, false to cancel
18579      */
18580     onInitDrag : function(x, y){
18581         this.proxy.update(this.dragData.ddel.cloneNode(true));
18582         this.onStartDrag(x, y);
18583         return true;
18584     },
18585     
18586     /**
18587      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18588      */
18589     afterRepair : function(){
18590         if(Roo.enableFx){
18591             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18592         }
18593         this.dragging = false;
18594     },
18595
18596     /**
18597      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18598      * the XY of this.dragData.ddel
18599      * @param {EventObject} e The mouse up event
18600      * @return {Array} The xy location (e.g. [100, 200])
18601      */
18602     getRepairXY : function(e){
18603         return Roo.Element.fly(this.dragData.ddel).getXY();  
18604     }
18605 });/*
18606  * Based on:
18607  * Ext JS Library 1.1.1
18608  * Copyright(c) 2006-2007, Ext JS, LLC.
18609  *
18610  * Originally Released Under LGPL - original licence link has changed is not relivant.
18611  *
18612  * Fork - LGPL
18613  * <script type="text/javascript">
18614  */
18615 /**
18616  * @class Roo.dd.DropZone
18617  * @extends Roo.dd.DropTarget
18618  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18619  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18620  * @constructor
18621  * @param {String/HTMLElement/Element} el The container element
18622  * @param {Object} config
18623  */
18624 Roo.dd.DropZone = function(el, config){
18625     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18626 };
18627
18628 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18629     /**
18630      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18631      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18632      * provide your own custom lookup.
18633      * @param {Event} e The event
18634      * @return {Object} data The custom data
18635      */
18636     getTargetFromEvent : function(e){
18637         return Roo.dd.Registry.getTargetFromEvent(e);
18638     },
18639
18640     /**
18641      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18642      * that it has registered.  This method has no default implementation and should be overridden to provide
18643      * node-specific processing if necessary.
18644      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18645      * {@link #getTargetFromEvent} for this node)
18646      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18647      * @param {Event} e The event
18648      * @param {Object} data An object containing arbitrary data supplied by the drag source
18649      */
18650     onNodeEnter : function(n, dd, e, data){
18651         
18652     },
18653
18654     /**
18655      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18656      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18657      * overridden to provide the proper feedback.
18658      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18659      * {@link #getTargetFromEvent} for this node)
18660      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18661      * @param {Event} e The event
18662      * @param {Object} data An object containing arbitrary data supplied by the drag source
18663      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18664      * underlying {@link Roo.dd.StatusProxy} can be updated
18665      */
18666     onNodeOver : function(n, dd, e, data){
18667         return this.dropAllowed;
18668     },
18669
18670     /**
18671      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18672      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18673      * node-specific processing if necessary.
18674      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18675      * {@link #getTargetFromEvent} for this node)
18676      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18677      * @param {Event} e The event
18678      * @param {Object} data An object containing arbitrary data supplied by the drag source
18679      */
18680     onNodeOut : function(n, dd, e, data){
18681         
18682     },
18683
18684     /**
18685      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18686      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18687      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18688      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18689      * {@link #getTargetFromEvent} for this node)
18690      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18691      * @param {Event} e The event
18692      * @param {Object} data An object containing arbitrary data supplied by the drag source
18693      * @return {Boolean} True if the drop was valid, else false
18694      */
18695     onNodeDrop : function(n, dd, e, data){
18696         return false;
18697     },
18698
18699     /**
18700      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18701      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18702      * it should be overridden to provide the proper feedback if necessary.
18703      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18704      * @param {Event} e The event
18705      * @param {Object} data An object containing arbitrary data supplied by the drag source
18706      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18707      * underlying {@link Roo.dd.StatusProxy} can be updated
18708      */
18709     onContainerOver : function(dd, e, data){
18710         return this.dropNotAllowed;
18711     },
18712
18713     /**
18714      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18715      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18716      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18717      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18718      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18719      * @param {Event} e The event
18720      * @param {Object} data An object containing arbitrary data supplied by the drag source
18721      * @return {Boolean} True if the drop was valid, else false
18722      */
18723     onContainerDrop : function(dd, e, data){
18724         return false;
18725     },
18726
18727     /**
18728      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18729      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18730      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18731      * you should override this method and provide a custom implementation.
18732      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18733      * @param {Event} e The event
18734      * @param {Object} data An object containing arbitrary data supplied by the drag source
18735      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18736      * underlying {@link Roo.dd.StatusProxy} can be updated
18737      */
18738     notifyEnter : function(dd, e, data){
18739         return this.dropNotAllowed;
18740     },
18741
18742     /**
18743      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18744      * This method will be called on every mouse movement while the drag source is over the drop zone.
18745      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18746      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18747      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18748      * registered node, it will call {@link #onContainerOver}.
18749      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18750      * @param {Event} e The event
18751      * @param {Object} data An object containing arbitrary data supplied by the drag source
18752      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18753      * underlying {@link Roo.dd.StatusProxy} can be updated
18754      */
18755     notifyOver : function(dd, e, data){
18756         var n = this.getTargetFromEvent(e);
18757         if(!n){ // not over valid drop target
18758             if(this.lastOverNode){
18759                 this.onNodeOut(this.lastOverNode, dd, e, data);
18760                 this.lastOverNode = null;
18761             }
18762             return this.onContainerOver(dd, e, data);
18763         }
18764         if(this.lastOverNode != n){
18765             if(this.lastOverNode){
18766                 this.onNodeOut(this.lastOverNode, dd, e, data);
18767             }
18768             this.onNodeEnter(n, dd, e, data);
18769             this.lastOverNode = n;
18770         }
18771         return this.onNodeOver(n, dd, e, data);
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18776      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18777      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18778      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18779      * @param {Event} e The event
18780      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18781      */
18782     notifyOut : function(dd, e, data){
18783         if(this.lastOverNode){
18784             this.onNodeOut(this.lastOverNode, dd, e, data);
18785             this.lastOverNode = null;
18786         }
18787     },
18788
18789     /**
18790      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18791      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18792      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18793      * otherwise it will call {@link #onContainerDrop}.
18794      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18795      * @param {Event} e The event
18796      * @param {Object} data An object containing arbitrary data supplied by the drag source
18797      * @return {Boolean} True if the drop was valid, else false
18798      */
18799     notifyDrop : function(dd, e, data){
18800         if(this.lastOverNode){
18801             this.onNodeOut(this.lastOverNode, dd, e, data);
18802             this.lastOverNode = null;
18803         }
18804         var n = this.getTargetFromEvent(e);
18805         return n ?
18806             this.onNodeDrop(n, dd, e, data) :
18807             this.onContainerDrop(dd, e, data);
18808     },
18809
18810     // private
18811     triggerCacheRefresh : function(){
18812         Roo.dd.DDM.refreshCache(this.groups);
18813     }  
18814 });/*
18815  * Based on:
18816  * Ext JS Library 1.1.1
18817  * Copyright(c) 2006-2007, Ext JS, LLC.
18818  *
18819  * Originally Released Under LGPL - original licence link has changed is not relivant.
18820  *
18821  * Fork - LGPL
18822  * <script type="text/javascript">
18823  */
18824
18825
18826 /**
18827  * @class Roo.data.SortTypes
18828  * @singleton
18829  * Defines the default sorting (casting?) comparison functions used when sorting data.
18830  */
18831 Roo.data.SortTypes = {
18832     /**
18833      * Default sort that does nothing
18834      * @param {Mixed} s The value being converted
18835      * @return {Mixed} The comparison value
18836      */
18837     none : function(s){
18838         return s;
18839     },
18840     
18841     /**
18842      * The regular expression used to strip tags
18843      * @type {RegExp}
18844      * @property
18845      */
18846     stripTagsRE : /<\/?[^>]+>/gi,
18847     
18848     /**
18849      * Strips all HTML tags to sort on text only
18850      * @param {Mixed} s The value being converted
18851      * @return {String} The comparison value
18852      */
18853     asText : function(s){
18854         return String(s).replace(this.stripTagsRE, "");
18855     },
18856     
18857     /**
18858      * Strips all HTML tags to sort on text only - Case insensitive
18859      * @param {Mixed} s The value being converted
18860      * @return {String} The comparison value
18861      */
18862     asUCText : function(s){
18863         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18864     },
18865     
18866     /**
18867      * Case insensitive string
18868      * @param {Mixed} s The value being converted
18869      * @return {String} The comparison value
18870      */
18871     asUCString : function(s) {
18872         return String(s).toUpperCase();
18873     },
18874     
18875     /**
18876      * Date sorting
18877      * @param {Mixed} s The value being converted
18878      * @return {Number} The comparison value
18879      */
18880     asDate : function(s) {
18881         if(!s){
18882             return 0;
18883         }
18884         if(s instanceof Date){
18885             return s.getTime();
18886         }
18887         return Date.parse(String(s));
18888     },
18889     
18890     /**
18891      * Float sorting
18892      * @param {Mixed} s The value being converted
18893      * @return {Float} The comparison value
18894      */
18895     asFloat : function(s) {
18896         var val = parseFloat(String(s).replace(/,/g, ""));
18897         if(isNaN(val)) val = 0;
18898         return val;
18899     },
18900     
18901     /**
18902      * Integer sorting
18903      * @param {Mixed} s The value being converted
18904      * @return {Number} The comparison value
18905      */
18906     asInt : function(s) {
18907         var val = parseInt(String(s).replace(/,/g, ""));
18908         if(isNaN(val)) val = 0;
18909         return val;
18910     }
18911 };/*
18912  * Based on:
18913  * Ext JS Library 1.1.1
18914  * Copyright(c) 2006-2007, Ext JS, LLC.
18915  *
18916  * Originally Released Under LGPL - original licence link has changed is not relivant.
18917  *
18918  * Fork - LGPL
18919  * <script type="text/javascript">
18920  */
18921
18922 /**
18923 * @class Roo.data.Record
18924  * Instances of this class encapsulate both record <em>definition</em> information, and record
18925  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18926  * to access Records cached in an {@link Roo.data.Store} object.<br>
18927  * <p>
18928  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18929  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18930  * objects.<br>
18931  * <p>
18932  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18933  * @constructor
18934  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18935  * {@link #create}. The parameters are the same.
18936  * @param {Array} data An associative Array of data values keyed by the field name.
18937  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18938  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18939  * not specified an integer id is generated.
18940  */
18941 Roo.data.Record = function(data, id){
18942     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18943     this.data = data;
18944 };
18945
18946 /**
18947  * Generate a constructor for a specific record layout.
18948  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18949  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18950  * Each field definition object may contain the following properties: <ul>
18951  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18952  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18953  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18954  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18955  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18956  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18957  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18958  * this may be omitted.</p></li>
18959  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18960  * <ul><li>auto (Default, implies no conversion)</li>
18961  * <li>string</li>
18962  * <li>int</li>
18963  * <li>float</li>
18964  * <li>boolean</li>
18965  * <li>date</li></ul></p></li>
18966  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18967  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18968  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18969  * by the Reader into an object that will be stored in the Record. It is passed the
18970  * following parameters:<ul>
18971  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18972  * </ul></p></li>
18973  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18974  * </ul>
18975  * <br>usage:<br><pre><code>
18976 var TopicRecord = Roo.data.Record.create(
18977     {name: 'title', mapping: 'topic_title'},
18978     {name: 'author', mapping: 'username'},
18979     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18980     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18981     {name: 'lastPoster', mapping: 'user2'},
18982     {name: 'excerpt', mapping: 'post_text'}
18983 );
18984
18985 var myNewRecord = new TopicRecord({
18986     title: 'Do my job please',
18987     author: 'noobie',
18988     totalPosts: 1,
18989     lastPost: new Date(),
18990     lastPoster: 'Animal',
18991     excerpt: 'No way dude!'
18992 });
18993 myStore.add(myNewRecord);
18994 </code></pre>
18995  * @method create
18996  * @static
18997  */
18998 Roo.data.Record.create = function(o){
18999     var f = function(){
19000         f.superclass.constructor.apply(this, arguments);
19001     };
19002     Roo.extend(f, Roo.data.Record);
19003     var p = f.prototype;
19004     p.fields = new Roo.util.MixedCollection(false, function(field){
19005         return field.name;
19006     });
19007     for(var i = 0, len = o.length; i < len; i++){
19008         p.fields.add(new Roo.data.Field(o[i]));
19009     }
19010     f.getField = function(name){
19011         return p.fields.get(name);  
19012     };
19013     return f;
19014 };
19015
19016 Roo.data.Record.AUTO_ID = 1000;
19017 Roo.data.Record.EDIT = 'edit';
19018 Roo.data.Record.REJECT = 'reject';
19019 Roo.data.Record.COMMIT = 'commit';
19020
19021 Roo.data.Record.prototype = {
19022     /**
19023      * Readonly flag - true if this record has been modified.
19024      * @type Boolean
19025      */
19026     dirty : false,
19027     editing : false,
19028     error: null,
19029     modified: null,
19030
19031     // private
19032     join : function(store){
19033         this.store = store;
19034     },
19035
19036     /**
19037      * Set the named field to the specified value.
19038      * @param {String} name The name of the field to set.
19039      * @param {Object} value The value to set the field to.
19040      */
19041     set : function(name, value){
19042         if(this.data[name] == value){
19043             return;
19044         }
19045         this.dirty = true;
19046         if(!this.modified){
19047             this.modified = {};
19048         }
19049         if(typeof this.modified[name] == 'undefined'){
19050             this.modified[name] = this.data[name];
19051         }
19052         this.data[name] = value;
19053         if(!this.editing && this.store){
19054             this.store.afterEdit(this);
19055         }       
19056     },
19057
19058     /**
19059      * Get the value of the named field.
19060      * @param {String} name The name of the field to get the value of.
19061      * @return {Object} The value of the field.
19062      */
19063     get : function(name){
19064         return this.data[name]; 
19065     },
19066
19067     // private
19068     beginEdit : function(){
19069         this.editing = true;
19070         this.modified = {}; 
19071     },
19072
19073     // private
19074     cancelEdit : function(){
19075         this.editing = false;
19076         delete this.modified;
19077     },
19078
19079     // private
19080     endEdit : function(){
19081         this.editing = false;
19082         if(this.dirty && this.store){
19083             this.store.afterEdit(this);
19084         }
19085     },
19086
19087     /**
19088      * Usually called by the {@link Roo.data.Store} which owns the Record.
19089      * Rejects all changes made to the Record since either creation, or the last commit operation.
19090      * Modified fields are reverted to their original values.
19091      * <p>
19092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19093      * of reject operations.
19094      */
19095     reject : function(){
19096         var m = this.modified;
19097         for(var n in m){
19098             if(typeof m[n] != "function"){
19099                 this.data[n] = m[n];
19100             }
19101         }
19102         this.dirty = false;
19103         delete this.modified;
19104         this.editing = false;
19105         if(this.store){
19106             this.store.afterReject(this);
19107         }
19108     },
19109
19110     /**
19111      * Usually called by the {@link Roo.data.Store} which owns the Record.
19112      * Commits all changes made to the Record since either creation, or the last commit operation.
19113      * <p>
19114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19115      * of commit operations.
19116      */
19117     commit : function(){
19118         this.dirty = false;
19119         delete this.modified;
19120         this.editing = false;
19121         if(this.store){
19122             this.store.afterCommit(this);
19123         }
19124     },
19125
19126     // private
19127     hasError : function(){
19128         return this.error != null;
19129     },
19130
19131     // private
19132     clearError : function(){
19133         this.error = null;
19134     },
19135
19136     /**
19137      * Creates a copy of this record.
19138      * @param {String} id (optional) A new record id if you don't want to use this record's id
19139      * @return {Record}
19140      */
19141     copy : function(newId) {
19142         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19143     }
19144 };/*
19145  * Based on:
19146  * Ext JS Library 1.1.1
19147  * Copyright(c) 2006-2007, Ext JS, LLC.
19148  *
19149  * Originally Released Under LGPL - original licence link has changed is not relivant.
19150  *
19151  * Fork - LGPL
19152  * <script type="text/javascript">
19153  */
19154
19155
19156
19157 /**
19158  * @class Roo.data.Store
19159  * @extends Roo.util.Observable
19160  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19161  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19162  * <p>
19163  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19164  * has no knowledge of the format of the data returned by the Proxy.<br>
19165  * <p>
19166  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19167  * instances from the data object. These records are cached and made available through accessor functions.
19168  * @constructor
19169  * Creates a new Store.
19170  * @param {Object} config A config object containing the objects needed for the Store to access data,
19171  * and read the data into Records.
19172  */
19173 Roo.data.Store = function(config){
19174     this.data = new Roo.util.MixedCollection(false);
19175     this.data.getKey = function(o){
19176         return o.id;
19177     };
19178     this.baseParams = {};
19179     // private
19180     this.paramNames = {
19181         "start" : "start",
19182         "limit" : "limit",
19183         "sort" : "sort",
19184         "dir" : "dir",
19185         "multisort" : "_multisort"
19186     };
19187
19188     if(config && config.data){
19189         this.inlineData = config.data;
19190         delete config.data;
19191     }
19192
19193     Roo.apply(this, config);
19194     
19195     if(this.reader){ // reader passed
19196         this.reader = Roo.factory(this.reader, Roo.data);
19197         this.reader.xmodule = this.xmodule || false;
19198         if(!this.recordType){
19199             this.recordType = this.reader.recordType;
19200         }
19201         if(this.reader.onMetaChange){
19202             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19203         }
19204     }
19205
19206     if(this.recordType){
19207         this.fields = this.recordType.prototype.fields;
19208     }
19209     this.modified = [];
19210
19211     this.addEvents({
19212         /**
19213          * @event datachanged
19214          * Fires when the data cache has changed, and a widget which is using this Store
19215          * as a Record cache should refresh its view.
19216          * @param {Store} this
19217          */
19218         datachanged : true,
19219         /**
19220          * @event metachange
19221          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19222          * @param {Store} this
19223          * @param {Object} meta The JSON metadata
19224          */
19225         metachange : true,
19226         /**
19227          * @event add
19228          * Fires when Records have been added to the Store
19229          * @param {Store} this
19230          * @param {Roo.data.Record[]} records The array of Records added
19231          * @param {Number} index The index at which the record(s) were added
19232          */
19233         add : true,
19234         /**
19235          * @event remove
19236          * Fires when a Record has been removed from the Store
19237          * @param {Store} this
19238          * @param {Roo.data.Record} record The Record that was removed
19239          * @param {Number} index The index at which the record was removed
19240          */
19241         remove : true,
19242         /**
19243          * @event update
19244          * Fires when a Record has been updated
19245          * @param {Store} this
19246          * @param {Roo.data.Record} record The Record that was updated
19247          * @param {String} operation The update operation being performed.  Value may be one of:
19248          * <pre><code>
19249  Roo.data.Record.EDIT
19250  Roo.data.Record.REJECT
19251  Roo.data.Record.COMMIT
19252          * </code></pre>
19253          */
19254         update : true,
19255         /**
19256          * @event clear
19257          * Fires when the data cache has been cleared.
19258          * @param {Store} this
19259          */
19260         clear : true,
19261         /**
19262          * @event beforeload
19263          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19264          * the load action will be canceled.
19265          * @param {Store} this
19266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19267          */
19268         beforeload : true,
19269         /**
19270          * @event load
19271          * Fires after a new set of Records has been loaded.
19272          * @param {Store} this
19273          * @param {Roo.data.Record[]} records The Records that were loaded
19274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19275          */
19276         load : true,
19277         /**
19278          * @event loadexception
19279          * Fires if an exception occurs in the Proxy during loading.
19280          * Called with the signature of the Proxy's "loadexception" event.
19281          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19282          * 
19283          * @param {Proxy} 
19284          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19285          * @param {Object} load options 
19286          * @param {Object} jsonData from your request (normally this contains the Exception)
19287          */
19288         loadexception : true
19289     });
19290     
19291     if(this.proxy){
19292         this.proxy = Roo.factory(this.proxy, Roo.data);
19293         this.proxy.xmodule = this.xmodule || false;
19294         this.relayEvents(this.proxy,  ["loadexception"]);
19295     }
19296     this.sortToggle = {};
19297     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19298
19299     Roo.data.Store.superclass.constructor.call(this);
19300
19301     if(this.inlineData){
19302         this.loadData(this.inlineData);
19303         delete this.inlineData;
19304     }
19305 };
19306 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19307      /**
19308     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19309     * without a remote query - used by combo/forms at present.
19310     */
19311     
19312     /**
19313     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19314     */
19315     /**
19316     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19317     */
19318     /**
19319     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19320     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19321     */
19322     /**
19323     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19324     * on any HTTP request
19325     */
19326     /**
19327     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19328     */
19329     /**
19330     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19331     */
19332     multiSort: false,
19333     /**
19334     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19335     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19336     */
19337     remoteSort : false,
19338
19339     /**
19340     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19341      * loaded or when a record is removed. (defaults to false).
19342     */
19343     pruneModifiedRecords : false,
19344
19345     // private
19346     lastOptions : null,
19347
19348     /**
19349      * Add Records to the Store and fires the add event.
19350      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19351      */
19352     add : function(records){
19353         records = [].concat(records);
19354         for(var i = 0, len = records.length; i < len; i++){
19355             records[i].join(this);
19356         }
19357         var index = this.data.length;
19358         this.data.addAll(records);
19359         this.fireEvent("add", this, records, index);
19360     },
19361
19362     /**
19363      * Remove a Record from the Store and fires the remove event.
19364      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19365      */
19366     remove : function(record){
19367         var index = this.data.indexOf(record);
19368         this.data.removeAt(index);
19369         if(this.pruneModifiedRecords){
19370             this.modified.remove(record);
19371         }
19372         this.fireEvent("remove", this, record, index);
19373     },
19374
19375     /**
19376      * Remove all Records from the Store and fires the clear event.
19377      */
19378     removeAll : function(){
19379         this.data.clear();
19380         if(this.pruneModifiedRecords){
19381             this.modified = [];
19382         }
19383         this.fireEvent("clear", this);
19384     },
19385
19386     /**
19387      * Inserts Records to the Store at the given index and fires the add event.
19388      * @param {Number} index The start index at which to insert the passed Records.
19389      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19390      */
19391     insert : function(index, records){
19392         records = [].concat(records);
19393         for(var i = 0, len = records.length; i < len; i++){
19394             this.data.insert(index, records[i]);
19395             records[i].join(this);
19396         }
19397         this.fireEvent("add", this, records, index);
19398     },
19399
19400     /**
19401      * Get the index within the cache of the passed Record.
19402      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19403      * @return {Number} The index of the passed Record. Returns -1 if not found.
19404      */
19405     indexOf : function(record){
19406         return this.data.indexOf(record);
19407     },
19408
19409     /**
19410      * Get the index within the cache of the Record with the passed id.
19411      * @param {String} id The id of the Record to find.
19412      * @return {Number} The index of the Record. Returns -1 if not found.
19413      */
19414     indexOfId : function(id){
19415         return this.data.indexOfKey(id);
19416     },
19417
19418     /**
19419      * Get the Record with the specified id.
19420      * @param {String} id The id of the Record to find.
19421      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19422      */
19423     getById : function(id){
19424         return this.data.key(id);
19425     },
19426
19427     /**
19428      * Get the Record at the specified index.
19429      * @param {Number} index The index of the Record to find.
19430      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19431      */
19432     getAt : function(index){
19433         return this.data.itemAt(index);
19434     },
19435
19436     /**
19437      * Returns a range of Records between specified indices.
19438      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19439      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19440      * @return {Roo.data.Record[]} An array of Records
19441      */
19442     getRange : function(start, end){
19443         return this.data.getRange(start, end);
19444     },
19445
19446     // private
19447     storeOptions : function(o){
19448         o = Roo.apply({}, o);
19449         delete o.callback;
19450         delete o.scope;
19451         this.lastOptions = o;
19452     },
19453
19454     /**
19455      * Loads the Record cache from the configured Proxy using the configured Reader.
19456      * <p>
19457      * If using remote paging, then the first load call must specify the <em>start</em>
19458      * and <em>limit</em> properties in the options.params property to establish the initial
19459      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19460      * <p>
19461      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19462      * and this call will return before the new data has been loaded. Perform any post-processing
19463      * in a callback function, or in a "load" event handler.</strong>
19464      * <p>
19465      * @param {Object} options An object containing properties which control loading options:<ul>
19466      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19467      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19468      * passed the following arguments:<ul>
19469      * <li>r : Roo.data.Record[]</li>
19470      * <li>options: Options object from the load call</li>
19471      * <li>success: Boolean success indicator</li></ul></li>
19472      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19473      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19474      * </ul>
19475      */
19476     load : function(options){
19477         options = options || {};
19478         if(this.fireEvent("beforeload", this, options) !== false){
19479             this.storeOptions(options);
19480             var p = Roo.apply(options.params || {}, this.baseParams);
19481             // if meta was not loaded from remote source.. try requesting it.
19482             if (!this.reader.metaFromRemote) {
19483                 p._requestMeta = 1;
19484             }
19485             if(this.sortInfo && this.remoteSort){
19486                 var pn = this.paramNames;
19487                 p[pn["sort"]] = this.sortInfo.field;
19488                 p[pn["dir"]] = this.sortInfo.direction;
19489             }
19490             if (this.multiSort) {
19491                 var pn = this.paramNames;
19492                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19493             }
19494             
19495             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19496         }
19497     },
19498
19499     /**
19500      * Reloads the Record cache from the configured Proxy using the configured Reader and
19501      * the options from the last load operation performed.
19502      * @param {Object} options (optional) An object containing properties which may override the options
19503      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19504      * the most recently used options are reused).
19505      */
19506     reload : function(options){
19507         this.load(Roo.applyIf(options||{}, this.lastOptions));
19508     },
19509
19510     // private
19511     // Called as a callback by the Reader during a load operation.
19512     loadRecords : function(o, options, success){
19513         if(!o || success === false){
19514             if(success !== false){
19515                 this.fireEvent("load", this, [], options);
19516             }
19517             if(options.callback){
19518                 options.callback.call(options.scope || this, [], options, false);
19519             }
19520             return;
19521         }
19522         // if data returned failure - throw an exception.
19523         if (o.success === false) {
19524             // show a message if no listener is registered.
19525             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19526                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19527             }
19528             // loadmask wil be hooked into this..
19529             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19530             return;
19531         }
19532         var r = o.records, t = o.totalRecords || r.length;
19533         if(!options || options.add !== true){
19534             if(this.pruneModifiedRecords){
19535                 this.modified = [];
19536             }
19537             for(var i = 0, len = r.length; i < len; i++){
19538                 r[i].join(this);
19539             }
19540             if(this.snapshot){
19541                 this.data = this.snapshot;
19542                 delete this.snapshot;
19543             }
19544             this.data.clear();
19545             this.data.addAll(r);
19546             this.totalLength = t;
19547             this.applySort();
19548             this.fireEvent("datachanged", this);
19549         }else{
19550             this.totalLength = Math.max(t, this.data.length+r.length);
19551             this.add(r);
19552         }
19553         this.fireEvent("load", this, r, options);
19554         if(options.callback){
19555             options.callback.call(options.scope || this, r, options, true);
19556         }
19557     },
19558
19559
19560     /**
19561      * Loads data from a passed data block. A Reader which understands the format of the data
19562      * must have been configured in the constructor.
19563      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19564      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19565      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19566      */
19567     loadData : function(o, append){
19568         var r = this.reader.readRecords(o);
19569         this.loadRecords(r, {add: append}, true);
19570     },
19571
19572     /**
19573      * Gets the number of cached records.
19574      * <p>
19575      * <em>If using paging, this may not be the total size of the dataset. If the data object
19576      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19577      * the data set size</em>
19578      */
19579     getCount : function(){
19580         return this.data.length || 0;
19581     },
19582
19583     /**
19584      * Gets the total number of records in the dataset as returned by the server.
19585      * <p>
19586      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19587      * the dataset size</em>
19588      */
19589     getTotalCount : function(){
19590         return this.totalLength || 0;
19591     },
19592
19593     /**
19594      * Returns the sort state of the Store as an object with two properties:
19595      * <pre><code>
19596  field {String} The name of the field by which the Records are sorted
19597  direction {String} The sort order, "ASC" or "DESC"
19598      * </code></pre>
19599      */
19600     getSortState : function(){
19601         return this.sortInfo;
19602     },
19603
19604     // private
19605     applySort : function(){
19606         if(this.sortInfo && !this.remoteSort){
19607             var s = this.sortInfo, f = s.field;
19608             var st = this.fields.get(f).sortType;
19609             var fn = function(r1, r2){
19610                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19611                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19612             };
19613             this.data.sort(s.direction, fn);
19614             if(this.snapshot && this.snapshot != this.data){
19615                 this.snapshot.sort(s.direction, fn);
19616             }
19617         }
19618     },
19619
19620     /**
19621      * Sets the default sort column and order to be used by the next load operation.
19622      * @param {String} fieldName The name of the field to sort by.
19623      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19624      */
19625     setDefaultSort : function(field, dir){
19626         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19627     },
19628
19629     /**
19630      * Sort the Records.
19631      * If remote sorting is used, the sort is performed on the server, and the cache is
19632      * reloaded. If local sorting is used, the cache is sorted internally.
19633      * @param {String} fieldName The name of the field to sort by.
19634      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19635      */
19636     sort : function(fieldName, dir){
19637         var f = this.fields.get(fieldName);
19638         if(!dir){
19639             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19640             
19641             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19642                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19643             }else{
19644                 dir = f.sortDir;
19645             }
19646         }
19647         this.sortToggle[f.name] = dir;
19648         this.sortInfo = {field: f.name, direction: dir};
19649         if(!this.remoteSort){
19650             this.applySort();
19651             this.fireEvent("datachanged", this);
19652         }else{
19653             this.load(this.lastOptions);
19654         }
19655     },
19656
19657     /**
19658      * Calls the specified function for each of the Records in the cache.
19659      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19660      * Returning <em>false</em> aborts and exits the iteration.
19661      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19662      */
19663     each : function(fn, scope){
19664         this.data.each(fn, scope);
19665     },
19666
19667     /**
19668      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19669      * (e.g., during paging).
19670      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19671      */
19672     getModifiedRecords : function(){
19673         return this.modified;
19674     },
19675
19676     // private
19677     createFilterFn : function(property, value, anyMatch){
19678         if(!value.exec){ // not a regex
19679             value = String(value);
19680             if(value.length == 0){
19681                 return false;
19682             }
19683             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19684         }
19685         return function(r){
19686             return value.test(r.data[property]);
19687         };
19688     },
19689
19690     /**
19691      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19692      * @param {String} property A field on your records
19693      * @param {Number} start The record index to start at (defaults to 0)
19694      * @param {Number} end The last record index to include (defaults to length - 1)
19695      * @return {Number} The sum
19696      */
19697     sum : function(property, start, end){
19698         var rs = this.data.items, v = 0;
19699         start = start || 0;
19700         end = (end || end === 0) ? end : rs.length-1;
19701
19702         for(var i = start; i <= end; i++){
19703             v += (rs[i].data[property] || 0);
19704         }
19705         return v;
19706     },
19707
19708     /**
19709      * Filter the records by a specified property.
19710      * @param {String} field A field on your records
19711      * @param {String/RegExp} value Either a string that the field
19712      * should start with or a RegExp to test against the field
19713      * @param {Boolean} anyMatch True to match any part not just the beginning
19714      */
19715     filter : function(property, value, anyMatch){
19716         var fn = this.createFilterFn(property, value, anyMatch);
19717         return fn ? this.filterBy(fn) : this.clearFilter();
19718     },
19719
19720     /**
19721      * Filter by a function. The specified function will be called with each
19722      * record in this data source. If the function returns true the record is included,
19723      * otherwise it is filtered.
19724      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19725      * @param {Object} scope (optional) The scope of the function (defaults to this)
19726      */
19727     filterBy : function(fn, scope){
19728         this.snapshot = this.snapshot || this.data;
19729         this.data = this.queryBy(fn, scope||this);
19730         this.fireEvent("datachanged", this);
19731     },
19732
19733     /**
19734      * Query the records by a specified property.
19735      * @param {String} field A field on your records
19736      * @param {String/RegExp} value Either a string that the field
19737      * should start with or a RegExp to test against the field
19738      * @param {Boolean} anyMatch True to match any part not just the beginning
19739      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19740      */
19741     query : function(property, value, anyMatch){
19742         var fn = this.createFilterFn(property, value, anyMatch);
19743         return fn ? this.queryBy(fn) : this.data.clone();
19744     },
19745
19746     /**
19747      * Query by a function. The specified function will be called with each
19748      * record in this data source. If the function returns true the record is included
19749      * in the results.
19750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19751      * @param {Object} scope (optional) The scope of the function (defaults to this)
19752       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19753      **/
19754     queryBy : function(fn, scope){
19755         var data = this.snapshot || this.data;
19756         return data.filterBy(fn, scope||this);
19757     },
19758
19759     /**
19760      * Collects unique values for a particular dataIndex from this store.
19761      * @param {String} dataIndex The property to collect
19762      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19763      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19764      * @return {Array} An array of the unique values
19765      **/
19766     collect : function(dataIndex, allowNull, bypassFilter){
19767         var d = (bypassFilter === true && this.snapshot) ?
19768                 this.snapshot.items : this.data.items;
19769         var v, sv, r = [], l = {};
19770         for(var i = 0, len = d.length; i < len; i++){
19771             v = d[i].data[dataIndex];
19772             sv = String(v);
19773             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19774                 l[sv] = true;
19775                 r[r.length] = v;
19776             }
19777         }
19778         return r;
19779     },
19780
19781     /**
19782      * Revert to a view of the Record cache with no filtering applied.
19783      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19784      */
19785     clearFilter : function(suppressEvent){
19786         if(this.snapshot && this.snapshot != this.data){
19787             this.data = this.snapshot;
19788             delete this.snapshot;
19789             if(suppressEvent !== true){
19790                 this.fireEvent("datachanged", this);
19791             }
19792         }
19793     },
19794
19795     // private
19796     afterEdit : function(record){
19797         if(this.modified.indexOf(record) == -1){
19798             this.modified.push(record);
19799         }
19800         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19801     },
19802     
19803     // private
19804     afterReject : function(record){
19805         this.modified.remove(record);
19806         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19807     },
19808
19809     // private
19810     afterCommit : function(record){
19811         this.modified.remove(record);
19812         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19813     },
19814
19815     /**
19816      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19817      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19818      */
19819     commitChanges : function(){
19820         var m = this.modified.slice(0);
19821         this.modified = [];
19822         for(var i = 0, len = m.length; i < len; i++){
19823             m[i].commit();
19824         }
19825     },
19826
19827     /**
19828      * Cancel outstanding changes on all changed records.
19829      */
19830     rejectChanges : function(){
19831         var m = this.modified.slice(0);
19832         this.modified = [];
19833         for(var i = 0, len = m.length; i < len; i++){
19834             m[i].reject();
19835         }
19836     },
19837
19838     onMetaChange : function(meta, rtype, o){
19839         this.recordType = rtype;
19840         this.fields = rtype.prototype.fields;
19841         delete this.snapshot;
19842         this.sortInfo = meta.sortInfo || this.sortInfo;
19843         this.modified = [];
19844         this.fireEvent('metachange', this, this.reader.meta);
19845     }
19846 });/*
19847  * Based on:
19848  * Ext JS Library 1.1.1
19849  * Copyright(c) 2006-2007, Ext JS, LLC.
19850  *
19851  * Originally Released Under LGPL - original licence link has changed is not relivant.
19852  *
19853  * Fork - LGPL
19854  * <script type="text/javascript">
19855  */
19856
19857 /**
19858  * @class Roo.data.SimpleStore
19859  * @extends Roo.data.Store
19860  * Small helper class to make creating Stores from Array data easier.
19861  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19862  * @cfg {Array} fields An array of field definition objects, or field name strings.
19863  * @cfg {Array} data The multi-dimensional array of data
19864  * @constructor
19865  * @param {Object} config
19866  */
19867 Roo.data.SimpleStore = function(config){
19868     Roo.data.SimpleStore.superclass.constructor.call(this, {
19869         isLocal : true,
19870         reader: new Roo.data.ArrayReader({
19871                 id: config.id
19872             },
19873             Roo.data.Record.create(config.fields)
19874         ),
19875         proxy : new Roo.data.MemoryProxy(config.data)
19876     });
19877     this.load();
19878 };
19879 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19880  * Based on:
19881  * Ext JS Library 1.1.1
19882  * Copyright(c) 2006-2007, Ext JS, LLC.
19883  *
19884  * Originally Released Under LGPL - original licence link has changed is not relivant.
19885  *
19886  * Fork - LGPL
19887  * <script type="text/javascript">
19888  */
19889
19890 /**
19891 /**
19892  * @extends Roo.data.Store
19893  * @class Roo.data.JsonStore
19894  * Small helper class to make creating Stores for JSON data easier. <br/>
19895 <pre><code>
19896 var store = new Roo.data.JsonStore({
19897     url: 'get-images.php',
19898     root: 'images',
19899     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19900 });
19901 </code></pre>
19902  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19903  * JsonReader and HttpProxy (unless inline data is provided).</b>
19904  * @cfg {Array} fields An array of field definition objects, or field name strings.
19905  * @constructor
19906  * @param {Object} config
19907  */
19908 Roo.data.JsonStore = function(c){
19909     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19910         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19911         reader: new Roo.data.JsonReader(c, c.fields)
19912     }));
19913 };
19914 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19915  * Based on:
19916  * Ext JS Library 1.1.1
19917  * Copyright(c) 2006-2007, Ext JS, LLC.
19918  *
19919  * Originally Released Under LGPL - original licence link has changed is not relivant.
19920  *
19921  * Fork - LGPL
19922  * <script type="text/javascript">
19923  */
19924
19925  
19926 Roo.data.Field = function(config){
19927     if(typeof config == "string"){
19928         config = {name: config};
19929     }
19930     Roo.apply(this, config);
19931     
19932     if(!this.type){
19933         this.type = "auto";
19934     }
19935     
19936     var st = Roo.data.SortTypes;
19937     // named sortTypes are supported, here we look them up
19938     if(typeof this.sortType == "string"){
19939         this.sortType = st[this.sortType];
19940     }
19941     
19942     // set default sortType for strings and dates
19943     if(!this.sortType){
19944         switch(this.type){
19945             case "string":
19946                 this.sortType = st.asUCString;
19947                 break;
19948             case "date":
19949                 this.sortType = st.asDate;
19950                 break;
19951             default:
19952                 this.sortType = st.none;
19953         }
19954     }
19955
19956     // define once
19957     var stripRe = /[\$,%]/g;
19958
19959     // prebuilt conversion function for this field, instead of
19960     // switching every time we're reading a value
19961     if(!this.convert){
19962         var cv, dateFormat = this.dateFormat;
19963         switch(this.type){
19964             case "":
19965             case "auto":
19966             case undefined:
19967                 cv = function(v){ return v; };
19968                 break;
19969             case "string":
19970                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19971                 break;
19972             case "int":
19973                 cv = function(v){
19974                     return v !== undefined && v !== null && v !== '' ?
19975                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19976                     };
19977                 break;
19978             case "float":
19979                 cv = function(v){
19980                     return v !== undefined && v !== null && v !== '' ?
19981                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19982                     };
19983                 break;
19984             case "bool":
19985             case "boolean":
19986                 cv = function(v){ return v === true || v === "true" || v == 1; };
19987                 break;
19988             case "date":
19989                 cv = function(v){
19990                     if(!v){
19991                         return '';
19992                     }
19993                     if(v instanceof Date){
19994                         return v;
19995                     }
19996                     if(dateFormat){
19997                         if(dateFormat == "timestamp"){
19998                             return new Date(v*1000);
19999                         }
20000                         return Date.parseDate(v, dateFormat);
20001                     }
20002                     var parsed = Date.parse(v);
20003                     return parsed ? new Date(parsed) : null;
20004                 };
20005              break;
20006             
20007         }
20008         this.convert = cv;
20009     }
20010 };
20011
20012 Roo.data.Field.prototype = {
20013     dateFormat: null,
20014     defaultValue: "",
20015     mapping: null,
20016     sortType : null,
20017     sortDir : "ASC"
20018 };/*
20019  * Based on:
20020  * Ext JS Library 1.1.1
20021  * Copyright(c) 2006-2007, Ext JS, LLC.
20022  *
20023  * Originally Released Under LGPL - original licence link has changed is not relivant.
20024  *
20025  * Fork - LGPL
20026  * <script type="text/javascript">
20027  */
20028  
20029 // Base class for reading structured data from a data source.  This class is intended to be
20030 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20031
20032 /**
20033  * @class Roo.data.DataReader
20034  * Base class for reading structured data from a data source.  This class is intended to be
20035  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20036  */
20037
20038 Roo.data.DataReader = function(meta, recordType){
20039     
20040     this.meta = meta;
20041     
20042     this.recordType = recordType instanceof Array ? 
20043         Roo.data.Record.create(recordType) : recordType;
20044 };
20045
20046 Roo.data.DataReader.prototype = {
20047      /**
20048      * Create an empty record
20049      * @param {Object} data (optional) - overlay some values
20050      * @return {Roo.data.Record} record created.
20051      */
20052     newRow :  function(d) {
20053         var da =  {};
20054         this.recordType.prototype.fields.each(function(c) {
20055             switch( c.type) {
20056                 case 'int' : da[c.name] = 0; break;
20057                 case 'date' : da[c.name] = new Date(); break;
20058                 case 'float' : da[c.name] = 0.0; break;
20059                 case 'boolean' : da[c.name] = false; break;
20060                 default : da[c.name] = ""; break;
20061             }
20062             
20063         });
20064         return new this.recordType(Roo.apply(da, d));
20065     }
20066     
20067 };/*
20068  * Based on:
20069  * Ext JS Library 1.1.1
20070  * Copyright(c) 2006-2007, Ext JS, LLC.
20071  *
20072  * Originally Released Under LGPL - original licence link has changed is not relivant.
20073  *
20074  * Fork - LGPL
20075  * <script type="text/javascript">
20076  */
20077
20078 /**
20079  * @class Roo.data.DataProxy
20080  * @extends Roo.data.Observable
20081  * This class is an abstract base class for implementations which provide retrieval of
20082  * unformatted data objects.<br>
20083  * <p>
20084  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20085  * (of the appropriate type which knows how to parse the data object) to provide a block of
20086  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20087  * <p>
20088  * Custom implementations must implement the load method as described in
20089  * {@link Roo.data.HttpProxy#load}.
20090  */
20091 Roo.data.DataProxy = function(){
20092     this.addEvents({
20093         /**
20094          * @event beforeload
20095          * Fires before a network request is made to retrieve a data object.
20096          * @param {Object} This DataProxy object.
20097          * @param {Object} params The params parameter to the load function.
20098          */
20099         beforeload : true,
20100         /**
20101          * @event load
20102          * Fires before the load method's callback is called.
20103          * @param {Object} This DataProxy object.
20104          * @param {Object} o The data object.
20105          * @param {Object} arg The callback argument object passed to the load function.
20106          */
20107         load : true,
20108         /**
20109          * @event loadexception
20110          * Fires if an Exception occurs during data retrieval.
20111          * @param {Object} This DataProxy object.
20112          * @param {Object} o The data object.
20113          * @param {Object} arg The callback argument object passed to the load function.
20114          * @param {Object} e The Exception.
20115          */
20116         loadexception : true
20117     });
20118     Roo.data.DataProxy.superclass.constructor.call(this);
20119 };
20120
20121 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20122
20123     /**
20124      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20125      */
20126 /*
20127  * Based on:
20128  * Ext JS Library 1.1.1
20129  * Copyright(c) 2006-2007, Ext JS, LLC.
20130  *
20131  * Originally Released Under LGPL - original licence link has changed is not relivant.
20132  *
20133  * Fork - LGPL
20134  * <script type="text/javascript">
20135  */
20136 /**
20137  * @class Roo.data.MemoryProxy
20138  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20139  * to the Reader when its load method is called.
20140  * @constructor
20141  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20142  */
20143 Roo.data.MemoryProxy = function(data){
20144     if (data.data) {
20145         data = data.data;
20146     }
20147     Roo.data.MemoryProxy.superclass.constructor.call(this);
20148     this.data = data;
20149 };
20150
20151 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20152     /**
20153      * Load data from the requested source (in this case an in-memory
20154      * data object passed to the constructor), read the data object into
20155      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20156      * process that block using the passed callback.
20157      * @param {Object} params This parameter is not used by the MemoryProxy class.
20158      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20159      * object into a block of Roo.data.Records.
20160      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20161      * The function must be passed <ul>
20162      * <li>The Record block object</li>
20163      * <li>The "arg" argument from the load function</li>
20164      * <li>A boolean success indicator</li>
20165      * </ul>
20166      * @param {Object} scope The scope in which to call the callback
20167      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20168      */
20169     load : function(params, reader, callback, scope, arg){
20170         params = params || {};
20171         var result;
20172         try {
20173             result = reader.readRecords(this.data);
20174         }catch(e){
20175             this.fireEvent("loadexception", this, arg, null, e);
20176             callback.call(scope, null, arg, false);
20177             return;
20178         }
20179         callback.call(scope, result, arg, true);
20180     },
20181     
20182     // private
20183     update : function(params, records){
20184         
20185     }
20186 });/*
20187  * Based on:
20188  * Ext JS Library 1.1.1
20189  * Copyright(c) 2006-2007, Ext JS, LLC.
20190  *
20191  * Originally Released Under LGPL - original licence link has changed is not relivant.
20192  *
20193  * Fork - LGPL
20194  * <script type="text/javascript">
20195  */
20196 /**
20197  * @class Roo.data.HttpProxy
20198  * @extends Roo.data.DataProxy
20199  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20200  * configured to reference a certain URL.<br><br>
20201  * <p>
20202  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20203  * from which the running page was served.<br><br>
20204  * <p>
20205  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20206  * <p>
20207  * Be aware that to enable the browser to parse an XML document, the server must set
20208  * the Content-Type header in the HTTP response to "text/xml".
20209  * @constructor
20210  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20211  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20212  * will be used to make the request.
20213  */
20214 Roo.data.HttpProxy = function(conn){
20215     Roo.data.HttpProxy.superclass.constructor.call(this);
20216     // is conn a conn config or a real conn?
20217     this.conn = conn;
20218     this.useAjax = !conn || !conn.events;
20219   
20220 };
20221
20222 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20223     // thse are take from connection...
20224     
20225     /**
20226      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20227      */
20228     /**
20229      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20230      * extra parameters to each request made by this object. (defaults to undefined)
20231      */
20232     /**
20233      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20234      *  to each request made by this object. (defaults to undefined)
20235      */
20236     /**
20237      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20238      */
20239     /**
20240      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20241      */
20242      /**
20243      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20244      * @type Boolean
20245      */
20246   
20247
20248     /**
20249      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20250      * @type Boolean
20251      */
20252     /**
20253      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20254      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20255      * a finer-grained basis than the DataProxy events.
20256      */
20257     getConnection : function(){
20258         return this.useAjax ? Roo.Ajax : this.conn;
20259     },
20260
20261     /**
20262      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20263      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20264      * process that block using the passed callback.
20265      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20266      * for the request to the remote server.
20267      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20268      * object into a block of Roo.data.Records.
20269      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20270      * The function must be passed <ul>
20271      * <li>The Record block object</li>
20272      * <li>The "arg" argument from the load function</li>
20273      * <li>A boolean success indicator</li>
20274      * </ul>
20275      * @param {Object} scope The scope in which to call the callback
20276      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20277      */
20278     load : function(params, reader, callback, scope, arg){
20279         if(this.fireEvent("beforeload", this, params) !== false){
20280             var  o = {
20281                 params : params || {},
20282                 request: {
20283                     callback : callback,
20284                     scope : scope,
20285                     arg : arg
20286                 },
20287                 reader: reader,
20288                 callback : this.loadResponse,
20289                 scope: this
20290             };
20291             if(this.useAjax){
20292                 Roo.applyIf(o, this.conn);
20293                 if(this.activeRequest){
20294                     Roo.Ajax.abort(this.activeRequest);
20295                 }
20296                 this.activeRequest = Roo.Ajax.request(o);
20297             }else{
20298                 this.conn.request(o);
20299             }
20300         }else{
20301             callback.call(scope||this, null, arg, false);
20302         }
20303     },
20304
20305     // private
20306     loadResponse : function(o, success, response){
20307         delete this.activeRequest;
20308         if(!success){
20309             this.fireEvent("loadexception", this, o, response);
20310             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20311             return;
20312         }
20313         var result;
20314         try {
20315             result = o.reader.read(response);
20316         }catch(e){
20317             this.fireEvent("loadexception", this, o, response, e);
20318             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20319             return;
20320         }
20321         
20322         this.fireEvent("load", this, o, o.request.arg);
20323         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20324     },
20325
20326     // private
20327     update : function(dataSet){
20328
20329     },
20330
20331     // private
20332     updateResponse : function(dataSet){
20333
20334     }
20335 });/*
20336  * Based on:
20337  * Ext JS Library 1.1.1
20338  * Copyright(c) 2006-2007, Ext JS, LLC.
20339  *
20340  * Originally Released Under LGPL - original licence link has changed is not relivant.
20341  *
20342  * Fork - LGPL
20343  * <script type="text/javascript">
20344  */
20345
20346 /**
20347  * @class Roo.data.ScriptTagProxy
20348  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20349  * other than the originating domain of the running page.<br><br>
20350  * <p>
20351  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20352  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20353  * <p>
20354  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20355  * source code that is used as the source inside a &lt;script> tag.<br><br>
20356  * <p>
20357  * In order for the browser to process the returned data, the server must wrap the data object
20358  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20359  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20360  * depending on whether the callback name was passed:
20361  * <p>
20362  * <pre><code>
20363 boolean scriptTag = false;
20364 String cb = request.getParameter("callback");
20365 if (cb != null) {
20366     scriptTag = true;
20367     response.setContentType("text/javascript");
20368 } else {
20369     response.setContentType("application/x-json");
20370 }
20371 Writer out = response.getWriter();
20372 if (scriptTag) {
20373     out.write(cb + "(");
20374 }
20375 out.print(dataBlock.toJsonString());
20376 if (scriptTag) {
20377     out.write(");");
20378 }
20379 </pre></code>
20380  *
20381  * @constructor
20382  * @param {Object} config A configuration object.
20383  */
20384 Roo.data.ScriptTagProxy = function(config){
20385     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20386     Roo.apply(this, config);
20387     this.head = document.getElementsByTagName("head")[0];
20388 };
20389
20390 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20391
20392 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20393     /**
20394      * @cfg {String} url The URL from which to request the data object.
20395      */
20396     /**
20397      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20398      */
20399     timeout : 30000,
20400     /**
20401      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20402      * the server the name of the callback function set up by the load call to process the returned data object.
20403      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20404      * javascript output which calls this named function passing the data object as its only parameter.
20405      */
20406     callbackParam : "callback",
20407     /**
20408      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20409      * name to the request.
20410      */
20411     nocache : true,
20412
20413     /**
20414      * Load data from the configured URL, read the data object into
20415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20416      * process that block using the passed callback.
20417      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20418      * for the request to the remote server.
20419      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20420      * object into a block of Roo.data.Records.
20421      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20422      * The function must be passed <ul>
20423      * <li>The Record block object</li>
20424      * <li>The "arg" argument from the load function</li>
20425      * <li>A boolean success indicator</li>
20426      * </ul>
20427      * @param {Object} scope The scope in which to call the callback
20428      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20429      */
20430     load : function(params, reader, callback, scope, arg){
20431         if(this.fireEvent("beforeload", this, params) !== false){
20432
20433             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20434
20435             var url = this.url;
20436             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20437             if(this.nocache){
20438                 url += "&_dc=" + (new Date().getTime());
20439             }
20440             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20441             var trans = {
20442                 id : transId,
20443                 cb : "stcCallback"+transId,
20444                 scriptId : "stcScript"+transId,
20445                 params : params,
20446                 arg : arg,
20447                 url : url,
20448                 callback : callback,
20449                 scope : scope,
20450                 reader : reader
20451             };
20452             var conn = this;
20453
20454             window[trans.cb] = function(o){
20455                 conn.handleResponse(o, trans);
20456             };
20457
20458             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20459
20460             if(this.autoAbort !== false){
20461                 this.abort();
20462             }
20463
20464             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20465
20466             var script = document.createElement("script");
20467             script.setAttribute("src", url);
20468             script.setAttribute("type", "text/javascript");
20469             script.setAttribute("id", trans.scriptId);
20470             this.head.appendChild(script);
20471
20472             this.trans = trans;
20473         }else{
20474             callback.call(scope||this, null, arg, false);
20475         }
20476     },
20477
20478     // private
20479     isLoading : function(){
20480         return this.trans ? true : false;
20481     },
20482
20483     /**
20484      * Abort the current server request.
20485      */
20486     abort : function(){
20487         if(this.isLoading()){
20488             this.destroyTrans(this.trans);
20489         }
20490     },
20491
20492     // private
20493     destroyTrans : function(trans, isLoaded){
20494         this.head.removeChild(document.getElementById(trans.scriptId));
20495         clearTimeout(trans.timeoutId);
20496         if(isLoaded){
20497             window[trans.cb] = undefined;
20498             try{
20499                 delete window[trans.cb];
20500             }catch(e){}
20501         }else{
20502             // if hasn't been loaded, wait for load to remove it to prevent script error
20503             window[trans.cb] = function(){
20504                 window[trans.cb] = undefined;
20505                 try{
20506                     delete window[trans.cb];
20507                 }catch(e){}
20508             };
20509         }
20510     },
20511
20512     // private
20513     handleResponse : function(o, trans){
20514         this.trans = false;
20515         this.destroyTrans(trans, true);
20516         var result;
20517         try {
20518             result = trans.reader.readRecords(o);
20519         }catch(e){
20520             this.fireEvent("loadexception", this, o, trans.arg, e);
20521             trans.callback.call(trans.scope||window, null, trans.arg, false);
20522             return;
20523         }
20524         this.fireEvent("load", this, o, trans.arg);
20525         trans.callback.call(trans.scope||window, result, trans.arg, true);
20526     },
20527
20528     // private
20529     handleFailure : function(trans){
20530         this.trans = false;
20531         this.destroyTrans(trans, false);
20532         this.fireEvent("loadexception", this, null, trans.arg);
20533         trans.callback.call(trans.scope||window, null, trans.arg, false);
20534     }
20535 });/*
20536  * Based on:
20537  * Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  *
20540  * Originally Released Under LGPL - original licence link has changed is not relivant.
20541  *
20542  * Fork - LGPL
20543  * <script type="text/javascript">
20544  */
20545
20546 /**
20547  * @class Roo.data.JsonReader
20548  * @extends Roo.data.DataReader
20549  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20550  * based on mappings in a provided Roo.data.Record constructor.
20551  * 
20552  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20553  * in the reply previously. 
20554  * 
20555  * <p>
20556  * Example code:
20557  * <pre><code>
20558 var RecordDef = Roo.data.Record.create([
20559     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20560     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20561 ]);
20562 var myReader = new Roo.data.JsonReader({
20563     totalProperty: "results",    // The property which contains the total dataset size (optional)
20564     root: "rows",                // The property which contains an Array of row objects
20565     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20566 }, RecordDef);
20567 </code></pre>
20568  * <p>
20569  * This would consume a JSON file like this:
20570  * <pre><code>
20571 { 'results': 2, 'rows': [
20572     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20573     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20574 }
20575 </code></pre>
20576  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20577  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20578  * paged from the remote server.
20579  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20580  * @cfg {String} root name of the property which contains the Array of row objects.
20581  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20582  * @constructor
20583  * Create a new JsonReader
20584  * @param {Object} meta Metadata configuration options
20585  * @param {Object} recordType Either an Array of field definition objects,
20586  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20587  */
20588 Roo.data.JsonReader = function(meta, recordType){
20589     
20590     meta = meta || {};
20591     // set some defaults:
20592     Roo.applyIf(meta, {
20593         totalProperty: 'total',
20594         successProperty : 'success',
20595         root : 'data',
20596         id : 'id'
20597     });
20598     
20599     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20600 };
20601 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20602     
20603     /**
20604      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20605      * Used by Store query builder to append _requestMeta to params.
20606      * 
20607      */
20608     metaFromRemote : false,
20609     /**
20610      * This method is only used by a DataProxy which has retrieved data from a remote server.
20611      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20612      * @return {Object} data A data block which is used by an Roo.data.Store object as
20613      * a cache of Roo.data.Records.
20614      */
20615     read : function(response){
20616         var json = response.responseText;
20617        
20618         var o = /* eval:var:o */ eval("("+json+")");
20619         if(!o) {
20620             throw {message: "JsonReader.read: Json object not found"};
20621         }
20622         
20623         if(o.metaData){
20624             
20625             delete this.ef;
20626             this.metaFromRemote = true;
20627             this.meta = o.metaData;
20628             this.recordType = Roo.data.Record.create(o.metaData.fields);
20629             this.onMetaChange(this.meta, this.recordType, o);
20630         }
20631         return this.readRecords(o);
20632     },
20633
20634     // private function a store will implement
20635     onMetaChange : function(meta, recordType, o){
20636
20637     },
20638
20639     /**
20640          * @ignore
20641          */
20642     simpleAccess: function(obj, subsc) {
20643         return obj[subsc];
20644     },
20645
20646         /**
20647          * @ignore
20648          */
20649     getJsonAccessor: function(){
20650         var re = /[\[\.]/;
20651         return function(expr) {
20652             try {
20653                 return(re.test(expr))
20654                     ? new Function("obj", "return obj." + expr)
20655                     : function(obj){
20656                         return obj[expr];
20657                     };
20658             } catch(e){}
20659             return Roo.emptyFn;
20660         };
20661     }(),
20662
20663     /**
20664      * Create a data block containing Roo.data.Records from an XML document.
20665      * @param {Object} o An object which contains an Array of row objects in the property specified
20666      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20667      * which contains the total size of the dataset.
20668      * @return {Object} data A data block which is used by an Roo.data.Store object as
20669      * a cache of Roo.data.Records.
20670      */
20671     readRecords : function(o){
20672         /**
20673          * After any data loads, the raw JSON data is available for further custom processing.
20674          * @type Object
20675          */
20676         this.jsonData = o;
20677         var s = this.meta, Record = this.recordType,
20678             f = Record.prototype.fields, fi = f.items, fl = f.length;
20679
20680 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20681         if (!this.ef) {
20682             if(s.totalProperty) {
20683                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20684                 }
20685                 if(s.successProperty) {
20686                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20687                 }
20688                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20689                 if (s.id) {
20690                         var g = this.getJsonAccessor(s.id);
20691                         this.getId = function(rec) {
20692                                 var r = g(rec);
20693                                 return (r === undefined || r === "") ? null : r;
20694                         };
20695                 } else {
20696                         this.getId = function(){return null;};
20697                 }
20698             this.ef = [];
20699             for(var jj = 0; jj < fl; jj++){
20700                 f = fi[jj];
20701                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20702                 this.ef[jj] = this.getJsonAccessor(map);
20703             }
20704         }
20705
20706         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20707         if(s.totalProperty){
20708             var vt = parseInt(this.getTotal(o), 10);
20709             if(!isNaN(vt)){
20710                 totalRecords = vt;
20711             }
20712         }
20713         if(s.successProperty){
20714             var vs = this.getSuccess(o);
20715             if(vs === false || vs === 'false'){
20716                 success = false;
20717             }
20718         }
20719         var records = [];
20720             for(var i = 0; i < c; i++){
20721                     var n = root[i];
20722                 var values = {};
20723                 var id = this.getId(n);
20724                 for(var j = 0; j < fl; j++){
20725                     f = fi[j];
20726                 var v = this.ef[j](n);
20727                 if (!f.convert) {
20728                     Roo.log('missing convert for ' + f.name);
20729                     Roo.log(f);
20730                     continue;
20731                 }
20732                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20733                 }
20734                 var record = new Record(values, id);
20735                 record.json = n;
20736                 records[i] = record;
20737             }
20738             return {
20739                 success : success,
20740                 records : records,
20741                 totalRecords : totalRecords
20742             };
20743     }
20744 });/*
20745  * Based on:
20746  * Ext JS Library 1.1.1
20747  * Copyright(c) 2006-2007, Ext JS, LLC.
20748  *
20749  * Originally Released Under LGPL - original licence link has changed is not relivant.
20750  *
20751  * Fork - LGPL
20752  * <script type="text/javascript">
20753  */
20754
20755 /**
20756  * @class Roo.data.XmlReader
20757  * @extends Roo.data.DataReader
20758  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20759  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20760  * <p>
20761  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20762  * header in the HTTP response must be set to "text/xml".</em>
20763  * <p>
20764  * Example code:
20765  * <pre><code>
20766 var RecordDef = Roo.data.Record.create([
20767    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20768    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20769 ]);
20770 var myReader = new Roo.data.XmlReader({
20771    totalRecords: "results", // The element which contains the total dataset size (optional)
20772    record: "row",           // The repeated element which contains row information
20773    id: "id"                 // The element within the row that provides an ID for the record (optional)
20774 }, RecordDef);
20775 </code></pre>
20776  * <p>
20777  * This would consume an XML file like this:
20778  * <pre><code>
20779 &lt;?xml?>
20780 &lt;dataset>
20781  &lt;results>2&lt;/results>
20782  &lt;row>
20783    &lt;id>1&lt;/id>
20784    &lt;name>Bill&lt;/name>
20785    &lt;occupation>Gardener&lt;/occupation>
20786  &lt;/row>
20787  &lt;row>
20788    &lt;id>2&lt;/id>
20789    &lt;name>Ben&lt;/name>
20790    &lt;occupation>Horticulturalist&lt;/occupation>
20791  &lt;/row>
20792 &lt;/dataset>
20793 </code></pre>
20794  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20795  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20796  * paged from the remote server.
20797  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20798  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20799  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20800  * a record identifier value.
20801  * @constructor
20802  * Create a new XmlReader
20803  * @param {Object} meta Metadata configuration options
20804  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20805  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20806  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20807  */
20808 Roo.data.XmlReader = function(meta, recordType){
20809     meta = meta || {};
20810     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20811 };
20812 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20813     /**
20814      * This method is only used by a DataProxy which has retrieved data from a remote server.
20815          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20816          * to contain a method called 'responseXML' that returns an XML document object.
20817      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20818      * a cache of Roo.data.Records.
20819      */
20820     read : function(response){
20821         var doc = response.responseXML;
20822         if(!doc) {
20823             throw {message: "XmlReader.read: XML Document not available"};
20824         }
20825         return this.readRecords(doc);
20826     },
20827
20828     /**
20829      * Create a data block containing Roo.data.Records from an XML document.
20830          * @param {Object} doc A parsed XML document.
20831      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20832      * a cache of Roo.data.Records.
20833      */
20834     readRecords : function(doc){
20835         /**
20836          * After any data loads/reads, the raw XML Document is available for further custom processing.
20837          * @type XMLDocument
20838          */
20839         this.xmlData = doc;
20840         var root = doc.documentElement || doc;
20841         var q = Roo.DomQuery;
20842         var recordType = this.recordType, fields = recordType.prototype.fields;
20843         var sid = this.meta.id;
20844         var totalRecords = 0, success = true;
20845         if(this.meta.totalRecords){
20846             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20847         }
20848         
20849         if(this.meta.success){
20850             var sv = q.selectValue(this.meta.success, root, true);
20851             success = sv !== false && sv !== 'false';
20852         }
20853         var records = [];
20854         var ns = q.select(this.meta.record, root);
20855         for(var i = 0, len = ns.length; i < len; i++) {
20856                 var n = ns[i];
20857                 var values = {};
20858                 var id = sid ? q.selectValue(sid, n) : undefined;
20859                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20860                     var f = fields.items[j];
20861                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20862                     v = f.convert(v);
20863                     values[f.name] = v;
20864                 }
20865                 var record = new recordType(values, id);
20866                 record.node = n;
20867                 records[records.length] = record;
20868             }
20869
20870             return {
20871                 success : success,
20872                 records : records,
20873                 totalRecords : totalRecords || records.length
20874             };
20875     }
20876 });/*
20877  * Based on:
20878  * Ext JS Library 1.1.1
20879  * Copyright(c) 2006-2007, Ext JS, LLC.
20880  *
20881  * Originally Released Under LGPL - original licence link has changed is not relivant.
20882  *
20883  * Fork - LGPL
20884  * <script type="text/javascript">
20885  */
20886
20887 /**
20888  * @class Roo.data.ArrayReader
20889  * @extends Roo.data.DataReader
20890  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20891  * Each element of that Array represents a row of data fields. The
20892  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20893  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20894  * <p>
20895  * Example code:.
20896  * <pre><code>
20897 var RecordDef = Roo.data.Record.create([
20898     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20899     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20900 ]);
20901 var myReader = new Roo.data.ArrayReader({
20902     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20903 }, RecordDef);
20904 </code></pre>
20905  * <p>
20906  * This would consume an Array like this:
20907  * <pre><code>
20908 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20909   </code></pre>
20910  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20911  * @constructor
20912  * Create a new JsonReader
20913  * @param {Object} meta Metadata configuration options.
20914  * @param {Object} recordType Either an Array of field definition objects
20915  * as specified to {@link Roo.data.Record#create},
20916  * or an {@link Roo.data.Record} object
20917  * created using {@link Roo.data.Record#create}.
20918  */
20919 Roo.data.ArrayReader = function(meta, recordType){
20920     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20921 };
20922
20923 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20924     /**
20925      * Create a data block containing Roo.data.Records from an XML document.
20926      * @param {Object} o An Array of row objects which represents the dataset.
20927      * @return {Object} data A data block which is used by an Roo.data.Store object as
20928      * a cache of Roo.data.Records.
20929      */
20930     readRecords : function(o){
20931         var sid = this.meta ? this.meta.id : null;
20932         var recordType = this.recordType, fields = recordType.prototype.fields;
20933         var records = [];
20934         var root = o;
20935             for(var i = 0; i < root.length; i++){
20936                     var n = root[i];
20937                 var values = {};
20938                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20939                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20940                 var f = fields.items[j];
20941                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20942                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20943                 v = f.convert(v);
20944                 values[f.name] = v;
20945             }
20946                 var record = new recordType(values, id);
20947                 record.json = n;
20948                 records[records.length] = record;
20949             }
20950             return {
20951                 records : records,
20952                 totalRecords : records.length
20953             };
20954     }
20955 });/*
20956  * Based on:
20957  * Ext JS Library 1.1.1
20958  * Copyright(c) 2006-2007, Ext JS, LLC.
20959  *
20960  * Originally Released Under LGPL - original licence link has changed is not relivant.
20961  *
20962  * Fork - LGPL
20963  * <script type="text/javascript">
20964  */
20965
20966
20967 /**
20968  * @class Roo.data.Tree
20969  * @extends Roo.util.Observable
20970  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20971  * in the tree have most standard DOM functionality.
20972  * @constructor
20973  * @param {Node} root (optional) The root node
20974  */
20975 Roo.data.Tree = function(root){
20976    this.nodeHash = {};
20977    /**
20978     * The root node for this tree
20979     * @type Node
20980     */
20981    this.root = null;
20982    if(root){
20983        this.setRootNode(root);
20984    }
20985    this.addEvents({
20986        /**
20987         * @event append
20988         * Fires when a new child node is appended to a node in this tree.
20989         * @param {Tree} tree The owner tree
20990         * @param {Node} parent The parent node
20991         * @param {Node} node The newly appended node
20992         * @param {Number} index The index of the newly appended node
20993         */
20994        "append" : true,
20995        /**
20996         * @event remove
20997         * Fires when a child node is removed from a node in this tree.
20998         * @param {Tree} tree The owner tree
20999         * @param {Node} parent The parent node
21000         * @param {Node} node The child node removed
21001         */
21002        "remove" : true,
21003        /**
21004         * @event move
21005         * Fires when a node is moved to a new location in the tree
21006         * @param {Tree} tree The owner tree
21007         * @param {Node} node The node moved
21008         * @param {Node} oldParent The old parent of this node
21009         * @param {Node} newParent The new parent of this node
21010         * @param {Number} index The index it was moved to
21011         */
21012        "move" : true,
21013        /**
21014         * @event insert
21015         * Fires when a new child node is inserted in a node in this tree.
21016         * @param {Tree} tree The owner tree
21017         * @param {Node} parent The parent node
21018         * @param {Node} node The child node inserted
21019         * @param {Node} refNode The child node the node was inserted before
21020         */
21021        "insert" : true,
21022        /**
21023         * @event beforeappend
21024         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21025         * @param {Tree} tree The owner tree
21026         * @param {Node} parent The parent node
21027         * @param {Node} node The child node to be appended
21028         */
21029        "beforeappend" : true,
21030        /**
21031         * @event beforeremove
21032         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21033         * @param {Tree} tree The owner tree
21034         * @param {Node} parent The parent node
21035         * @param {Node} node The child node to be removed
21036         */
21037        "beforeremove" : true,
21038        /**
21039         * @event beforemove
21040         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21041         * @param {Tree} tree The owner tree
21042         * @param {Node} node The node being moved
21043         * @param {Node} oldParent The parent of the node
21044         * @param {Node} newParent The new parent the node is moving to
21045         * @param {Number} index The index it is being moved to
21046         */
21047        "beforemove" : true,
21048        /**
21049         * @event beforeinsert
21050         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node to be inserted
21054         * @param {Node} refNode The child node the node is being inserted before
21055         */
21056        "beforeinsert" : true
21057    });
21058
21059     Roo.data.Tree.superclass.constructor.call(this);
21060 };
21061
21062 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21063     pathSeparator: "/",
21064
21065     proxyNodeEvent : function(){
21066         return this.fireEvent.apply(this, arguments);
21067     },
21068
21069     /**
21070      * Returns the root node for this tree.
21071      * @return {Node}
21072      */
21073     getRootNode : function(){
21074         return this.root;
21075     },
21076
21077     /**
21078      * Sets the root node for this tree.
21079      * @param {Node} node
21080      * @return {Node}
21081      */
21082     setRootNode : function(node){
21083         this.root = node;
21084         node.ownerTree = this;
21085         node.isRoot = true;
21086         this.registerNode(node);
21087         return node;
21088     },
21089
21090     /**
21091      * Gets a node in this tree by its id.
21092      * @param {String} id
21093      * @return {Node}
21094      */
21095     getNodeById : function(id){
21096         return this.nodeHash[id];
21097     },
21098
21099     registerNode : function(node){
21100         this.nodeHash[node.id] = node;
21101     },
21102
21103     unregisterNode : function(node){
21104         delete this.nodeHash[node.id];
21105     },
21106
21107     toString : function(){
21108         return "[Tree"+(this.id?" "+this.id:"")+"]";
21109     }
21110 });
21111
21112 /**
21113  * @class Roo.data.Node
21114  * @extends Roo.util.Observable
21115  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21116  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21117  * @constructor
21118  * @param {Object} attributes The attributes/config for the node
21119  */
21120 Roo.data.Node = function(attributes){
21121     /**
21122      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21123      * @type {Object}
21124      */
21125     this.attributes = attributes || {};
21126     this.leaf = this.attributes.leaf;
21127     /**
21128      * The node id. @type String
21129      */
21130     this.id = this.attributes.id;
21131     if(!this.id){
21132         this.id = Roo.id(null, "ynode-");
21133         this.attributes.id = this.id;
21134     }
21135      
21136     
21137     /**
21138      * All child nodes of this node. @type Array
21139      */
21140     this.childNodes = [];
21141     if(!this.childNodes.indexOf){ // indexOf is a must
21142         this.childNodes.indexOf = function(o){
21143             for(var i = 0, len = this.length; i < len; i++){
21144                 if(this[i] == o) {
21145                     return i;
21146                 }
21147             }
21148             return -1;
21149         };
21150     }
21151     /**
21152      * The parent node for this node. @type Node
21153      */
21154     this.parentNode = null;
21155     /**
21156      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21157      */
21158     this.firstChild = null;
21159     /**
21160      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21161      */
21162     this.lastChild = null;
21163     /**
21164      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21165      */
21166     this.previousSibling = null;
21167     /**
21168      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21169      */
21170     this.nextSibling = null;
21171
21172     this.addEvents({
21173        /**
21174         * @event append
21175         * Fires when a new child node is appended
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The newly appended node
21179         * @param {Number} index The index of the newly appended node
21180         */
21181        "append" : true,
21182        /**
21183         * @event remove
21184         * Fires when a child node is removed
21185         * @param {Tree} tree The owner tree
21186         * @param {Node} this This node
21187         * @param {Node} node The removed node
21188         */
21189        "remove" : true,
21190        /**
21191         * @event move
21192         * Fires when this node is moved to a new location in the tree
21193         * @param {Tree} tree The owner tree
21194         * @param {Node} this This node
21195         * @param {Node} oldParent The old parent of this node
21196         * @param {Node} newParent The new parent of this node
21197         * @param {Number} index The index it was moved to
21198         */
21199        "move" : true,
21200        /**
21201         * @event insert
21202         * Fires when a new child node is inserted.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node inserted
21206         * @param {Node} refNode The child node the node was inserted before
21207         */
21208        "insert" : true,
21209        /**
21210         * @event beforeappend
21211         * Fires before a new child is appended, return false to cancel the append.
21212         * @param {Tree} tree The owner tree
21213         * @param {Node} this This node
21214         * @param {Node} node The child node to be appended
21215         */
21216        "beforeappend" : true,
21217        /**
21218         * @event beforeremove
21219         * Fires before a child is removed, return false to cancel the remove.
21220         * @param {Tree} tree The owner tree
21221         * @param {Node} this This node
21222         * @param {Node} node The child node to be removed
21223         */
21224        "beforeremove" : true,
21225        /**
21226         * @event beforemove
21227         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21228         * @param {Tree} tree The owner tree
21229         * @param {Node} this This node
21230         * @param {Node} oldParent The parent of this node
21231         * @param {Node} newParent The new parent this node is moving to
21232         * @param {Number} index The index it is being moved to
21233         */
21234        "beforemove" : true,
21235        /**
21236         * @event beforeinsert
21237         * Fires before a new child is inserted, return false to cancel the insert.
21238         * @param {Tree} tree The owner tree
21239         * @param {Node} this This node
21240         * @param {Node} node The child node to be inserted
21241         * @param {Node} refNode The child node the node is being inserted before
21242         */
21243        "beforeinsert" : true
21244    });
21245     this.listeners = this.attributes.listeners;
21246     Roo.data.Node.superclass.constructor.call(this);
21247 };
21248
21249 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21250     fireEvent : function(evtName){
21251         // first do standard event for this node
21252         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21253             return false;
21254         }
21255         // then bubble it up to the tree if the event wasn't cancelled
21256         var ot = this.getOwnerTree();
21257         if(ot){
21258             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21259                 return false;
21260             }
21261         }
21262         return true;
21263     },
21264
21265     /**
21266      * Returns true if this node is a leaf
21267      * @return {Boolean}
21268      */
21269     isLeaf : function(){
21270         return this.leaf === true;
21271     },
21272
21273     // private
21274     setFirstChild : function(node){
21275         this.firstChild = node;
21276     },
21277
21278     //private
21279     setLastChild : function(node){
21280         this.lastChild = node;
21281     },
21282
21283
21284     /**
21285      * Returns true if this node is the last child of its parent
21286      * @return {Boolean}
21287      */
21288     isLast : function(){
21289        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21290     },
21291
21292     /**
21293      * Returns true if this node is the first child of its parent
21294      * @return {Boolean}
21295      */
21296     isFirst : function(){
21297        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21298     },
21299
21300     hasChildNodes : function(){
21301         return !this.isLeaf() && this.childNodes.length > 0;
21302     },
21303
21304     /**
21305      * Insert node(s) as the last child node of this node.
21306      * @param {Node/Array} node The node or Array of nodes to append
21307      * @return {Node} The appended node if single append, or null if an array was passed
21308      */
21309     appendChild : function(node){
21310         var multi = false;
21311         if(node instanceof Array){
21312             multi = node;
21313         }else if(arguments.length > 1){
21314             multi = arguments;
21315         }
21316         // if passed an array or multiple args do them one by one
21317         if(multi){
21318             for(var i = 0, len = multi.length; i < len; i++) {
21319                 this.appendChild(multi[i]);
21320             }
21321         }else{
21322             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21323                 return false;
21324             }
21325             var index = this.childNodes.length;
21326             var oldParent = node.parentNode;
21327             // it's a move, make sure we move it cleanly
21328             if(oldParent){
21329                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21330                     return false;
21331                 }
21332                 oldParent.removeChild(node);
21333             }
21334             index = this.childNodes.length;
21335             if(index == 0){
21336                 this.setFirstChild(node);
21337             }
21338             this.childNodes.push(node);
21339             node.parentNode = this;
21340             var ps = this.childNodes[index-1];
21341             if(ps){
21342                 node.previousSibling = ps;
21343                 ps.nextSibling = node;
21344             }else{
21345                 node.previousSibling = null;
21346             }
21347             node.nextSibling = null;
21348             this.setLastChild(node);
21349             node.setOwnerTree(this.getOwnerTree());
21350             this.fireEvent("append", this.ownerTree, this, node, index);
21351             if(oldParent){
21352                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21353             }
21354             return node;
21355         }
21356     },
21357
21358     /**
21359      * Removes a child node from this node.
21360      * @param {Node} node The node to remove
21361      * @return {Node} The removed node
21362      */
21363     removeChild : function(node){
21364         var index = this.childNodes.indexOf(node);
21365         if(index == -1){
21366             return false;
21367         }
21368         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21369             return false;
21370         }
21371
21372         // remove it from childNodes collection
21373         this.childNodes.splice(index, 1);
21374
21375         // update siblings
21376         if(node.previousSibling){
21377             node.previousSibling.nextSibling = node.nextSibling;
21378         }
21379         if(node.nextSibling){
21380             node.nextSibling.previousSibling = node.previousSibling;
21381         }
21382
21383         // update child refs
21384         if(this.firstChild == node){
21385             this.setFirstChild(node.nextSibling);
21386         }
21387         if(this.lastChild == node){
21388             this.setLastChild(node.previousSibling);
21389         }
21390
21391         node.setOwnerTree(null);
21392         // clear any references from the node
21393         node.parentNode = null;
21394         node.previousSibling = null;
21395         node.nextSibling = null;
21396         this.fireEvent("remove", this.ownerTree, this, node);
21397         return node;
21398     },
21399
21400     /**
21401      * Inserts the first node before the second node in this nodes childNodes collection.
21402      * @param {Node} node The node to insert
21403      * @param {Node} refNode The node to insert before (if null the node is appended)
21404      * @return {Node} The inserted node
21405      */
21406     insertBefore : function(node, refNode){
21407         if(!refNode){ // like standard Dom, refNode can be null for append
21408             return this.appendChild(node);
21409         }
21410         // nothing to do
21411         if(node == refNode){
21412             return false;
21413         }
21414
21415         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21416             return false;
21417         }
21418         var index = this.childNodes.indexOf(refNode);
21419         var oldParent = node.parentNode;
21420         var refIndex = index;
21421
21422         // when moving internally, indexes will change after remove
21423         if(oldParent == this && this.childNodes.indexOf(node) < index){
21424             refIndex--;
21425         }
21426
21427         // it's a move, make sure we move it cleanly
21428         if(oldParent){
21429             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21430                 return false;
21431             }
21432             oldParent.removeChild(node);
21433         }
21434         if(refIndex == 0){
21435             this.setFirstChild(node);
21436         }
21437         this.childNodes.splice(refIndex, 0, node);
21438         node.parentNode = this;
21439         var ps = this.childNodes[refIndex-1];
21440         if(ps){
21441             node.previousSibling = ps;
21442             ps.nextSibling = node;
21443         }else{
21444             node.previousSibling = null;
21445         }
21446         node.nextSibling = refNode;
21447         refNode.previousSibling = node;
21448         node.setOwnerTree(this.getOwnerTree());
21449         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21450         if(oldParent){
21451             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21452         }
21453         return node;
21454     },
21455
21456     /**
21457      * Returns the child node at the specified index.
21458      * @param {Number} index
21459      * @return {Node}
21460      */
21461     item : function(index){
21462         return this.childNodes[index];
21463     },
21464
21465     /**
21466      * Replaces one child node in this node with another.
21467      * @param {Node} newChild The replacement node
21468      * @param {Node} oldChild The node to replace
21469      * @return {Node} The replaced node
21470      */
21471     replaceChild : function(newChild, oldChild){
21472         this.insertBefore(newChild, oldChild);
21473         this.removeChild(oldChild);
21474         return oldChild;
21475     },
21476
21477     /**
21478      * Returns the index of a child node
21479      * @param {Node} node
21480      * @return {Number} The index of the node or -1 if it was not found
21481      */
21482     indexOf : function(child){
21483         return this.childNodes.indexOf(child);
21484     },
21485
21486     /**
21487      * Returns the tree this node is in.
21488      * @return {Tree}
21489      */
21490     getOwnerTree : function(){
21491         // if it doesn't have one, look for one
21492         if(!this.ownerTree){
21493             var p = this;
21494             while(p){
21495                 if(p.ownerTree){
21496                     this.ownerTree = p.ownerTree;
21497                     break;
21498                 }
21499                 p = p.parentNode;
21500             }
21501         }
21502         return this.ownerTree;
21503     },
21504
21505     /**
21506      * Returns depth of this node (the root node has a depth of 0)
21507      * @return {Number}
21508      */
21509     getDepth : function(){
21510         var depth = 0;
21511         var p = this;
21512         while(p.parentNode){
21513             ++depth;
21514             p = p.parentNode;
21515         }
21516         return depth;
21517     },
21518
21519     // private
21520     setOwnerTree : function(tree){
21521         // if it's move, we need to update everyone
21522         if(tree != this.ownerTree){
21523             if(this.ownerTree){
21524                 this.ownerTree.unregisterNode(this);
21525             }
21526             this.ownerTree = tree;
21527             var cs = this.childNodes;
21528             for(var i = 0, len = cs.length; i < len; i++) {
21529                 cs[i].setOwnerTree(tree);
21530             }
21531             if(tree){
21532                 tree.registerNode(this);
21533             }
21534         }
21535     },
21536
21537     /**
21538      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21539      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21540      * @return {String} The path
21541      */
21542     getPath : function(attr){
21543         attr = attr || "id";
21544         var p = this.parentNode;
21545         var b = [this.attributes[attr]];
21546         while(p){
21547             b.unshift(p.attributes[attr]);
21548             p = p.parentNode;
21549         }
21550         var sep = this.getOwnerTree().pathSeparator;
21551         return sep + b.join(sep);
21552     },
21553
21554     /**
21555      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21556      * function call will be the scope provided or the current node. The arguments to the function
21557      * will be the args provided or the current node. If the function returns false at any point,
21558      * the bubble is stopped.
21559      * @param {Function} fn The function to call
21560      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21561      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21562      */
21563     bubble : function(fn, scope, args){
21564         var p = this;
21565         while(p){
21566             if(fn.call(scope || p, args || p) === false){
21567                 break;
21568             }
21569             p = p.parentNode;
21570         }
21571     },
21572
21573     /**
21574      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21575      * function call will be the scope provided or the current node. The arguments to the function
21576      * will be the args provided or the current node. If the function returns false at any point,
21577      * the cascade is stopped on that branch.
21578      * @param {Function} fn The function to call
21579      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21580      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21581      */
21582     cascade : function(fn, scope, args){
21583         if(fn.call(scope || this, args || this) !== false){
21584             var cs = this.childNodes;
21585             for(var i = 0, len = cs.length; i < len; i++) {
21586                 cs[i].cascade(fn, scope, args);
21587             }
21588         }
21589     },
21590
21591     /**
21592      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21593      * function call will be the scope provided or the current node. The arguments to the function
21594      * will be the args provided or the current node. If the function returns false at any point,
21595      * the iteration stops.
21596      * @param {Function} fn The function to call
21597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21599      */
21600     eachChild : function(fn, scope, args){
21601         var cs = this.childNodes;
21602         for(var i = 0, len = cs.length; i < len; i++) {
21603                 if(fn.call(scope || this, args || cs[i]) === false){
21604                     break;
21605                 }
21606         }
21607     },
21608
21609     /**
21610      * Finds the first child that has the attribute with the specified value.
21611      * @param {String} attribute The attribute name
21612      * @param {Mixed} value The value to search for
21613      * @return {Node} The found child or null if none was found
21614      */
21615     findChild : function(attribute, value){
21616         var cs = this.childNodes;
21617         for(var i = 0, len = cs.length; i < len; i++) {
21618                 if(cs[i].attributes[attribute] == value){
21619                     return cs[i];
21620                 }
21621         }
21622         return null;
21623     },
21624
21625     /**
21626      * Finds the first child by a custom function. The child matches if the function passed
21627      * returns true.
21628      * @param {Function} fn
21629      * @param {Object} scope (optional)
21630      * @return {Node} The found child or null if none was found
21631      */
21632     findChildBy : function(fn, scope){
21633         var cs = this.childNodes;
21634         for(var i = 0, len = cs.length; i < len; i++) {
21635                 if(fn.call(scope||cs[i], cs[i]) === true){
21636                     return cs[i];
21637                 }
21638         }
21639         return null;
21640     },
21641
21642     /**
21643      * Sorts this nodes children using the supplied sort function
21644      * @param {Function} fn
21645      * @param {Object} scope (optional)
21646      */
21647     sort : function(fn, scope){
21648         var cs = this.childNodes;
21649         var len = cs.length;
21650         if(len > 0){
21651             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21652             cs.sort(sortFn);
21653             for(var i = 0; i < len; i++){
21654                 var n = cs[i];
21655                 n.previousSibling = cs[i-1];
21656                 n.nextSibling = cs[i+1];
21657                 if(i == 0){
21658                     this.setFirstChild(n);
21659                 }
21660                 if(i == len-1){
21661                     this.setLastChild(n);
21662                 }
21663             }
21664         }
21665     },
21666
21667     /**
21668      * Returns true if this node is an ancestor (at any point) of the passed node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     contains : function(node){
21673         return node.isAncestor(this);
21674     },
21675
21676     /**
21677      * Returns true if the passed node is an ancestor (at any point) of this node.
21678      * @param {Node} node
21679      * @return {Boolean}
21680      */
21681     isAncestor : function(node){
21682         var p = this.parentNode;
21683         while(p){
21684             if(p == node){
21685                 return true;
21686             }
21687             p = p.parentNode;
21688         }
21689         return false;
21690     },
21691
21692     toString : function(){
21693         return "[Node"+(this.id?" "+this.id:"")+"]";
21694     }
21695 });/*
21696  * Based on:
21697  * Ext JS Library 1.1.1
21698  * Copyright(c) 2006-2007, Ext JS, LLC.
21699  *
21700  * Originally Released Under LGPL - original licence link has changed is not relivant.
21701  *
21702  * Fork - LGPL
21703  * <script type="text/javascript">
21704  */
21705  
21706
21707 /**
21708  * @class Roo.ComponentMgr
21709  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21710  * @singleton
21711  */
21712 Roo.ComponentMgr = function(){
21713     var all = new Roo.util.MixedCollection();
21714
21715     return {
21716         /**
21717          * Registers a component.
21718          * @param {Roo.Component} c The component
21719          */
21720         register : function(c){
21721             all.add(c);
21722         },
21723
21724         /**
21725          * Unregisters a component.
21726          * @param {Roo.Component} c The component
21727          */
21728         unregister : function(c){
21729             all.remove(c);
21730         },
21731
21732         /**
21733          * Returns a component by id
21734          * @param {String} id The component id
21735          */
21736         get : function(id){
21737             return all.get(id);
21738         },
21739
21740         /**
21741          * Registers a function that will be called when a specified component is added to ComponentMgr
21742          * @param {String} id The component id
21743          * @param {Funtction} fn The callback function
21744          * @param {Object} scope The scope of the callback
21745          */
21746         onAvailable : function(id, fn, scope){
21747             all.on("add", function(index, o){
21748                 if(o.id == id){
21749                     fn.call(scope || o, o);
21750                     all.un("add", fn, scope);
21751                 }
21752             });
21753         }
21754     };
21755 }();/*
21756  * Based on:
21757  * Ext JS Library 1.1.1
21758  * Copyright(c) 2006-2007, Ext JS, LLC.
21759  *
21760  * Originally Released Under LGPL - original licence link has changed is not relivant.
21761  *
21762  * Fork - LGPL
21763  * <script type="text/javascript">
21764  */
21765  
21766 /**
21767  * @class Roo.Component
21768  * @extends Roo.util.Observable
21769  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21770  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21771  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21772  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21773  * All visual components (widgets) that require rendering into a layout should subclass Component.
21774  * @constructor
21775  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21776  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21777  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21778  */
21779 Roo.Component = function(config){
21780     config = config || {};
21781     if(config.tagName || config.dom || typeof config == "string"){ // element object
21782         config = {el: config, id: config.id || config};
21783     }
21784     this.initialConfig = config;
21785
21786     Roo.apply(this, config);
21787     this.addEvents({
21788         /**
21789          * @event disable
21790          * Fires after the component is disabled.
21791              * @param {Roo.Component} this
21792              */
21793         disable : true,
21794         /**
21795          * @event enable
21796          * Fires after the component is enabled.
21797              * @param {Roo.Component} this
21798              */
21799         enable : true,
21800         /**
21801          * @event beforeshow
21802          * Fires before the component is shown.  Return false to stop the show.
21803              * @param {Roo.Component} this
21804              */
21805         beforeshow : true,
21806         /**
21807          * @event show
21808          * Fires after the component is shown.
21809              * @param {Roo.Component} this
21810              */
21811         show : true,
21812         /**
21813          * @event beforehide
21814          * Fires before the component is hidden. Return false to stop the hide.
21815              * @param {Roo.Component} this
21816              */
21817         beforehide : true,
21818         /**
21819          * @event hide
21820          * Fires after the component is hidden.
21821              * @param {Roo.Component} this
21822              */
21823         hide : true,
21824         /**
21825          * @event beforerender
21826          * Fires before the component is rendered. Return false to stop the render.
21827              * @param {Roo.Component} this
21828              */
21829         beforerender : true,
21830         /**
21831          * @event render
21832          * Fires after the component is rendered.
21833              * @param {Roo.Component} this
21834              */
21835         render : true,
21836         /**
21837          * @event beforedestroy
21838          * Fires before the component is destroyed. Return false to stop the destroy.
21839              * @param {Roo.Component} this
21840              */
21841         beforedestroy : true,
21842         /**
21843          * @event destroy
21844          * Fires after the component is destroyed.
21845              * @param {Roo.Component} this
21846              */
21847         destroy : true
21848     });
21849     if(!this.id){
21850         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21851     }
21852     Roo.ComponentMgr.register(this);
21853     Roo.Component.superclass.constructor.call(this);
21854     this.initComponent();
21855     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21856         this.render(this.renderTo);
21857         delete this.renderTo;
21858     }
21859 };
21860
21861 /** @private */
21862 Roo.Component.AUTO_ID = 1000;
21863
21864 Roo.extend(Roo.Component, Roo.util.Observable, {
21865     /**
21866      * @scope Roo.Component.prototype
21867      * @type {Boolean}
21868      * true if this component is hidden. Read-only.
21869      */
21870     hidden : false,
21871     /**
21872      * @type {Boolean}
21873      * true if this component is disabled. Read-only.
21874      */
21875     disabled : false,
21876     /**
21877      * @type {Boolean}
21878      * true if this component has been rendered. Read-only.
21879      */
21880     rendered : false,
21881     
21882     /** @cfg {String} disableClass
21883      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21884      */
21885     disabledClass : "x-item-disabled",
21886         /** @cfg {Boolean} allowDomMove
21887          * Whether the component can move the Dom node when rendering (defaults to true).
21888          */
21889     allowDomMove : true,
21890     /** @cfg {String} hideMode
21891      * How this component should hidden. Supported values are
21892      * "visibility" (css visibility), "offsets" (negative offset position) and
21893      * "display" (css display) - defaults to "display".
21894      */
21895     hideMode: 'display',
21896
21897     /** @private */
21898     ctype : "Roo.Component",
21899
21900     /**
21901      * @cfg {String} actionMode 
21902      * which property holds the element that used for  hide() / show() / disable() / enable()
21903      * default is 'el' 
21904      */
21905     actionMode : "el",
21906
21907     /** @private */
21908     getActionEl : function(){
21909         return this[this.actionMode];
21910     },
21911
21912     initComponent : Roo.emptyFn,
21913     /**
21914      * If this is a lazy rendering component, render it to its container element.
21915      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21916      */
21917     render : function(container, position){
21918         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21919             if(!container && this.el){
21920                 this.el = Roo.get(this.el);
21921                 container = this.el.dom.parentNode;
21922                 this.allowDomMove = false;
21923             }
21924             this.container = Roo.get(container);
21925             this.rendered = true;
21926             if(position !== undefined){
21927                 if(typeof position == 'number'){
21928                     position = this.container.dom.childNodes[position];
21929                 }else{
21930                     position = Roo.getDom(position);
21931                 }
21932             }
21933             this.onRender(this.container, position || null);
21934             if(this.cls){
21935                 this.el.addClass(this.cls);
21936                 delete this.cls;
21937             }
21938             if(this.style){
21939                 this.el.applyStyles(this.style);
21940                 delete this.style;
21941             }
21942             this.fireEvent("render", this);
21943             this.afterRender(this.container);
21944             if(this.hidden){
21945                 this.hide();
21946             }
21947             if(this.disabled){
21948                 this.disable();
21949             }
21950         }
21951         return this;
21952     },
21953
21954     /** @private */
21955     // default function is not really useful
21956     onRender : function(ct, position){
21957         if(this.el){
21958             this.el = Roo.get(this.el);
21959             if(this.allowDomMove !== false){
21960                 ct.dom.insertBefore(this.el.dom, position);
21961             }
21962         }
21963     },
21964
21965     /** @private */
21966     getAutoCreate : function(){
21967         var cfg = typeof this.autoCreate == "object" ?
21968                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21969         if(this.id && !cfg.id){
21970             cfg.id = this.id;
21971         }
21972         return cfg;
21973     },
21974
21975     /** @private */
21976     afterRender : Roo.emptyFn,
21977
21978     /**
21979      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21980      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21981      */
21982     destroy : function(){
21983         if(this.fireEvent("beforedestroy", this) !== false){
21984             this.purgeListeners();
21985             this.beforeDestroy();
21986             if(this.rendered){
21987                 this.el.removeAllListeners();
21988                 this.el.remove();
21989                 if(this.actionMode == "container"){
21990                     this.container.remove();
21991                 }
21992             }
21993             this.onDestroy();
21994             Roo.ComponentMgr.unregister(this);
21995             this.fireEvent("destroy", this);
21996         }
21997     },
21998
21999         /** @private */
22000     beforeDestroy : function(){
22001
22002     },
22003
22004         /** @private */
22005         onDestroy : function(){
22006
22007     },
22008
22009     /**
22010      * Returns the underlying {@link Roo.Element}.
22011      * @return {Roo.Element} The element
22012      */
22013     getEl : function(){
22014         return this.el;
22015     },
22016
22017     /**
22018      * Returns the id of this component.
22019      * @return {String}
22020      */
22021     getId : function(){
22022         return this.id;
22023     },
22024
22025     /**
22026      * Try to focus this component.
22027      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22028      * @return {Roo.Component} this
22029      */
22030     focus : function(selectText){
22031         if(this.rendered){
22032             this.el.focus();
22033             if(selectText === true){
22034                 this.el.dom.select();
22035             }
22036         }
22037         return this;
22038     },
22039
22040     /** @private */
22041     blur : function(){
22042         if(this.rendered){
22043             this.el.blur();
22044         }
22045         return this;
22046     },
22047
22048     /**
22049      * Disable this component.
22050      * @return {Roo.Component} this
22051      */
22052     disable : function(){
22053         if(this.rendered){
22054             this.onDisable();
22055         }
22056         this.disabled = true;
22057         this.fireEvent("disable", this);
22058         return this;
22059     },
22060
22061         // private
22062     onDisable : function(){
22063         this.getActionEl().addClass(this.disabledClass);
22064         this.el.dom.disabled = true;
22065     },
22066
22067     /**
22068      * Enable this component.
22069      * @return {Roo.Component} this
22070      */
22071     enable : function(){
22072         if(this.rendered){
22073             this.onEnable();
22074         }
22075         this.disabled = false;
22076         this.fireEvent("enable", this);
22077         return this;
22078     },
22079
22080         // private
22081     onEnable : function(){
22082         this.getActionEl().removeClass(this.disabledClass);
22083         this.el.dom.disabled = false;
22084     },
22085
22086     /**
22087      * Convenience function for setting disabled/enabled by boolean.
22088      * @param {Boolean} disabled
22089      */
22090     setDisabled : function(disabled){
22091         this[disabled ? "disable" : "enable"]();
22092     },
22093
22094     /**
22095      * Show this component.
22096      * @return {Roo.Component} this
22097      */
22098     show: function(){
22099         if(this.fireEvent("beforeshow", this) !== false){
22100             this.hidden = false;
22101             if(this.rendered){
22102                 this.onShow();
22103             }
22104             this.fireEvent("show", this);
22105         }
22106         return this;
22107     },
22108
22109     // private
22110     onShow : function(){
22111         var ae = this.getActionEl();
22112         if(this.hideMode == 'visibility'){
22113             ae.dom.style.visibility = "visible";
22114         }else if(this.hideMode == 'offsets'){
22115             ae.removeClass('x-hidden');
22116         }else{
22117             ae.dom.style.display = "";
22118         }
22119     },
22120
22121     /**
22122      * Hide this component.
22123      * @return {Roo.Component} this
22124      */
22125     hide: function(){
22126         if(this.fireEvent("beforehide", this) !== false){
22127             this.hidden = true;
22128             if(this.rendered){
22129                 this.onHide();
22130             }
22131             this.fireEvent("hide", this);
22132         }
22133         return this;
22134     },
22135
22136     // private
22137     onHide : function(){
22138         var ae = this.getActionEl();
22139         if(this.hideMode == 'visibility'){
22140             ae.dom.style.visibility = "hidden";
22141         }else if(this.hideMode == 'offsets'){
22142             ae.addClass('x-hidden');
22143         }else{
22144             ae.dom.style.display = "none";
22145         }
22146     },
22147
22148     /**
22149      * Convenience function to hide or show this component by boolean.
22150      * @param {Boolean} visible True to show, false to hide
22151      * @return {Roo.Component} this
22152      */
22153     setVisible: function(visible){
22154         if(visible) {
22155             this.show();
22156         }else{
22157             this.hide();
22158         }
22159         return this;
22160     },
22161
22162     /**
22163      * Returns true if this component is visible.
22164      */
22165     isVisible : function(){
22166         return this.getActionEl().isVisible();
22167     },
22168
22169     cloneConfig : function(overrides){
22170         overrides = overrides || {};
22171         var id = overrides.id || Roo.id();
22172         var cfg = Roo.applyIf(overrides, this.initialConfig);
22173         cfg.id = id; // prevent dup id
22174         return new this.constructor(cfg);
22175     }
22176 });/*
22177  * Based on:
22178  * Ext JS Library 1.1.1
22179  * Copyright(c) 2006-2007, Ext JS, LLC.
22180  *
22181  * Originally Released Under LGPL - original licence link has changed is not relivant.
22182  *
22183  * Fork - LGPL
22184  * <script type="text/javascript">
22185  */
22186  (function(){ 
22187 /**
22188  * @class Roo.Layer
22189  * @extends Roo.Element
22190  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22191  * automatic maintaining of shadow/shim positions.
22192  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22193  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22194  * you can pass a string with a CSS class name. False turns off the shadow.
22195  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22196  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22197  * @cfg {String} cls CSS class to add to the element
22198  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22199  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22200  * @constructor
22201  * @param {Object} config An object with config options.
22202  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22203  */
22204
22205 Roo.Layer = function(config, existingEl){
22206     config = config || {};
22207     var dh = Roo.DomHelper;
22208     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22209     if(existingEl){
22210         this.dom = Roo.getDom(existingEl);
22211     }
22212     if(!this.dom){
22213         var o = config.dh || {tag: "div", cls: "x-layer"};
22214         this.dom = dh.append(pel, o);
22215     }
22216     if(config.cls){
22217         this.addClass(config.cls);
22218     }
22219     this.constrain = config.constrain !== false;
22220     this.visibilityMode = Roo.Element.VISIBILITY;
22221     if(config.id){
22222         this.id = this.dom.id = config.id;
22223     }else{
22224         this.id = Roo.id(this.dom);
22225     }
22226     this.zindex = config.zindex || this.getZIndex();
22227     this.position("absolute", this.zindex);
22228     if(config.shadow){
22229         this.shadowOffset = config.shadowOffset || 4;
22230         this.shadow = new Roo.Shadow({
22231             offset : this.shadowOffset,
22232             mode : config.shadow
22233         });
22234     }else{
22235         this.shadowOffset = 0;
22236     }
22237     this.useShim = config.shim !== false && Roo.useShims;
22238     this.useDisplay = config.useDisplay;
22239     this.hide();
22240 };
22241
22242 var supr = Roo.Element.prototype;
22243
22244 // shims are shared among layer to keep from having 100 iframes
22245 var shims = [];
22246
22247 Roo.extend(Roo.Layer, Roo.Element, {
22248
22249     getZIndex : function(){
22250         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22251     },
22252
22253     getShim : function(){
22254         if(!this.useShim){
22255             return null;
22256         }
22257         if(this.shim){
22258             return this.shim;
22259         }
22260         var shim = shims.shift();
22261         if(!shim){
22262             shim = this.createShim();
22263             shim.enableDisplayMode('block');
22264             shim.dom.style.display = 'none';
22265             shim.dom.style.visibility = 'visible';
22266         }
22267         var pn = this.dom.parentNode;
22268         if(shim.dom.parentNode != pn){
22269             pn.insertBefore(shim.dom, this.dom);
22270         }
22271         shim.setStyle('z-index', this.getZIndex()-2);
22272         this.shim = shim;
22273         return shim;
22274     },
22275
22276     hideShim : function(){
22277         if(this.shim){
22278             this.shim.setDisplayed(false);
22279             shims.push(this.shim);
22280             delete this.shim;
22281         }
22282     },
22283
22284     disableShadow : function(){
22285         if(this.shadow){
22286             this.shadowDisabled = true;
22287             this.shadow.hide();
22288             this.lastShadowOffset = this.shadowOffset;
22289             this.shadowOffset = 0;
22290         }
22291     },
22292
22293     enableShadow : function(show){
22294         if(this.shadow){
22295             this.shadowDisabled = false;
22296             this.shadowOffset = this.lastShadowOffset;
22297             delete this.lastShadowOffset;
22298             if(show){
22299                 this.sync(true);
22300             }
22301         }
22302     },
22303
22304     // private
22305     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22306     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22307     sync : function(doShow){
22308         var sw = this.shadow;
22309         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22310             var sh = this.getShim();
22311
22312             var w = this.getWidth(),
22313                 h = this.getHeight();
22314
22315             var l = this.getLeft(true),
22316                 t = this.getTop(true);
22317
22318             if(sw && !this.shadowDisabled){
22319                 if(doShow && !sw.isVisible()){
22320                     sw.show(this);
22321                 }else{
22322                     sw.realign(l, t, w, h);
22323                 }
22324                 if(sh){
22325                     if(doShow){
22326                        sh.show();
22327                     }
22328                     // fit the shim behind the shadow, so it is shimmed too
22329                     var a = sw.adjusts, s = sh.dom.style;
22330                     s.left = (Math.min(l, l+a.l))+"px";
22331                     s.top = (Math.min(t, t+a.t))+"px";
22332                     s.width = (w+a.w)+"px";
22333                     s.height = (h+a.h)+"px";
22334                 }
22335             }else if(sh){
22336                 if(doShow){
22337                    sh.show();
22338                 }
22339                 sh.setSize(w, h);
22340                 sh.setLeftTop(l, t);
22341             }
22342             
22343         }
22344     },
22345
22346     // private
22347     destroy : function(){
22348         this.hideShim();
22349         if(this.shadow){
22350             this.shadow.hide();
22351         }
22352         this.removeAllListeners();
22353         var pn = this.dom.parentNode;
22354         if(pn){
22355             pn.removeChild(this.dom);
22356         }
22357         Roo.Element.uncache(this.id);
22358     },
22359
22360     remove : function(){
22361         this.destroy();
22362     },
22363
22364     // private
22365     beginUpdate : function(){
22366         this.updating = true;
22367     },
22368
22369     // private
22370     endUpdate : function(){
22371         this.updating = false;
22372         this.sync(true);
22373     },
22374
22375     // private
22376     hideUnders : function(negOffset){
22377         if(this.shadow){
22378             this.shadow.hide();
22379         }
22380         this.hideShim();
22381     },
22382
22383     // private
22384     constrainXY : function(){
22385         if(this.constrain){
22386             var vw = Roo.lib.Dom.getViewWidth(),
22387                 vh = Roo.lib.Dom.getViewHeight();
22388             var s = Roo.get(document).getScroll();
22389
22390             var xy = this.getXY();
22391             var x = xy[0], y = xy[1];   
22392             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22393             // only move it if it needs it
22394             var moved = false;
22395             // first validate right/bottom
22396             if((x + w) > vw+s.left){
22397                 x = vw - w - this.shadowOffset;
22398                 moved = true;
22399             }
22400             if((y + h) > vh+s.top){
22401                 y = vh - h - this.shadowOffset;
22402                 moved = true;
22403             }
22404             // then make sure top/left isn't negative
22405             if(x < s.left){
22406                 x = s.left;
22407                 moved = true;
22408             }
22409             if(y < s.top){
22410                 y = s.top;
22411                 moved = true;
22412             }
22413             if(moved){
22414                 if(this.avoidY){
22415                     var ay = this.avoidY;
22416                     if(y <= ay && (y+h) >= ay){
22417                         y = ay-h-5;   
22418                     }
22419                 }
22420                 xy = [x, y];
22421                 this.storeXY(xy);
22422                 supr.setXY.call(this, xy);
22423                 this.sync();
22424             }
22425         }
22426     },
22427
22428     isVisible : function(){
22429         return this.visible;    
22430     },
22431
22432     // private
22433     showAction : function(){
22434         this.visible = true; // track visibility to prevent getStyle calls
22435         if(this.useDisplay === true){
22436             this.setDisplayed("");
22437         }else if(this.lastXY){
22438             supr.setXY.call(this, this.lastXY);
22439         }else if(this.lastLT){
22440             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22441         }
22442     },
22443
22444     // private
22445     hideAction : function(){
22446         this.visible = false;
22447         if(this.useDisplay === true){
22448             this.setDisplayed(false);
22449         }else{
22450             this.setLeftTop(-10000,-10000);
22451         }
22452     },
22453
22454     // overridden Element method
22455     setVisible : function(v, a, d, c, e){
22456         if(v){
22457             this.showAction();
22458         }
22459         if(a && v){
22460             var cb = function(){
22461                 this.sync(true);
22462                 if(c){
22463                     c();
22464                 }
22465             }.createDelegate(this);
22466             supr.setVisible.call(this, true, true, d, cb, e);
22467         }else{
22468             if(!v){
22469                 this.hideUnders(true);
22470             }
22471             var cb = c;
22472             if(a){
22473                 cb = function(){
22474                     this.hideAction();
22475                     if(c){
22476                         c();
22477                     }
22478                 }.createDelegate(this);
22479             }
22480             supr.setVisible.call(this, v, a, d, cb, e);
22481             if(v){
22482                 this.sync(true);
22483             }else if(!a){
22484                 this.hideAction();
22485             }
22486         }
22487     },
22488
22489     storeXY : function(xy){
22490         delete this.lastLT;
22491         this.lastXY = xy;
22492     },
22493
22494     storeLeftTop : function(left, top){
22495         delete this.lastXY;
22496         this.lastLT = [left, top];
22497     },
22498
22499     // private
22500     beforeFx : function(){
22501         this.beforeAction();
22502         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22503     },
22504
22505     // private
22506     afterFx : function(){
22507         Roo.Layer.superclass.afterFx.apply(this, arguments);
22508         this.sync(this.isVisible());
22509     },
22510
22511     // private
22512     beforeAction : function(){
22513         if(!this.updating && this.shadow){
22514             this.shadow.hide();
22515         }
22516     },
22517
22518     // overridden Element method
22519     setLeft : function(left){
22520         this.storeLeftTop(left, this.getTop(true));
22521         supr.setLeft.apply(this, arguments);
22522         this.sync();
22523     },
22524
22525     setTop : function(top){
22526         this.storeLeftTop(this.getLeft(true), top);
22527         supr.setTop.apply(this, arguments);
22528         this.sync();
22529     },
22530
22531     setLeftTop : function(left, top){
22532         this.storeLeftTop(left, top);
22533         supr.setLeftTop.apply(this, arguments);
22534         this.sync();
22535     },
22536
22537     setXY : function(xy, a, d, c, e){
22538         this.fixDisplay();
22539         this.beforeAction();
22540         this.storeXY(xy);
22541         var cb = this.createCB(c);
22542         supr.setXY.call(this, xy, a, d, cb, e);
22543         if(!a){
22544             cb();
22545         }
22546     },
22547
22548     // private
22549     createCB : function(c){
22550         var el = this;
22551         return function(){
22552             el.constrainXY();
22553             el.sync(true);
22554             if(c){
22555                 c();
22556             }
22557         };
22558     },
22559
22560     // overridden Element method
22561     setX : function(x, a, d, c, e){
22562         this.setXY([x, this.getY()], a, d, c, e);
22563     },
22564
22565     // overridden Element method
22566     setY : function(y, a, d, c, e){
22567         this.setXY([this.getX(), y], a, d, c, e);
22568     },
22569
22570     // overridden Element method
22571     setSize : function(w, h, a, d, c, e){
22572         this.beforeAction();
22573         var cb = this.createCB(c);
22574         supr.setSize.call(this, w, h, a, d, cb, e);
22575         if(!a){
22576             cb();
22577         }
22578     },
22579
22580     // overridden Element method
22581     setWidth : function(w, a, d, c, e){
22582         this.beforeAction();
22583         var cb = this.createCB(c);
22584         supr.setWidth.call(this, w, a, d, cb, e);
22585         if(!a){
22586             cb();
22587         }
22588     },
22589
22590     // overridden Element method
22591     setHeight : function(h, a, d, c, e){
22592         this.beforeAction();
22593         var cb = this.createCB(c);
22594         supr.setHeight.call(this, h, a, d, cb, e);
22595         if(!a){
22596             cb();
22597         }
22598     },
22599
22600     // overridden Element method
22601     setBounds : function(x, y, w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         if(!a){
22605             this.storeXY([x, y]);
22606             supr.setXY.call(this, [x, y]);
22607             supr.setSize.call(this, w, h, a, d, cb, e);
22608             cb();
22609         }else{
22610             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22611         }
22612         return this;
22613     },
22614     
22615     /**
22616      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22617      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22618      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22619      * @param {Number} zindex The new z-index to set
22620      * @return {this} The Layer
22621      */
22622     setZIndex : function(zindex){
22623         this.zindex = zindex;
22624         this.setStyle("z-index", zindex + 2);
22625         if(this.shadow){
22626             this.shadow.setZIndex(zindex + 1);
22627         }
22628         if(this.shim){
22629             this.shim.setStyle("z-index", zindex);
22630         }
22631     }
22632 });
22633 })();/*
22634  * Based on:
22635  * Ext JS Library 1.1.1
22636  * Copyright(c) 2006-2007, Ext JS, LLC.
22637  *
22638  * Originally Released Under LGPL - original licence link has changed is not relivant.
22639  *
22640  * Fork - LGPL
22641  * <script type="text/javascript">
22642  */
22643
22644
22645 /**
22646  * @class Roo.Shadow
22647  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22648  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22649  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22650  * @constructor
22651  * Create a new Shadow
22652  * @param {Object} config The config object
22653  */
22654 Roo.Shadow = function(config){
22655     Roo.apply(this, config);
22656     if(typeof this.mode != "string"){
22657         this.mode = this.defaultMode;
22658     }
22659     var o = this.offset, a = {h: 0};
22660     var rad = Math.floor(this.offset/2);
22661     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22662         case "drop":
22663             a.w = 0;
22664             a.l = a.t = o;
22665             a.t -= 1;
22666             if(Roo.isIE){
22667                 a.l -= this.offset + rad;
22668                 a.t -= this.offset + rad;
22669                 a.w -= rad;
22670                 a.h -= rad;
22671                 a.t += 1;
22672             }
22673         break;
22674         case "sides":
22675             a.w = (o*2);
22676             a.l = -o;
22677             a.t = o-1;
22678             if(Roo.isIE){
22679                 a.l -= (this.offset - rad);
22680                 a.t -= this.offset + rad;
22681                 a.l += 1;
22682                 a.w -= (this.offset - rad)*2;
22683                 a.w -= rad + 1;
22684                 a.h -= 1;
22685             }
22686         break;
22687         case "frame":
22688             a.w = a.h = (o*2);
22689             a.l = a.t = -o;
22690             a.t += 1;
22691             a.h -= 2;
22692             if(Roo.isIE){
22693                 a.l -= (this.offset - rad);
22694                 a.t -= (this.offset - rad);
22695                 a.l += 1;
22696                 a.w -= (this.offset + rad + 1);
22697                 a.h -= (this.offset + rad);
22698                 a.h += 1;
22699             }
22700         break;
22701     };
22702
22703     this.adjusts = a;
22704 };
22705
22706 Roo.Shadow.prototype = {
22707     /**
22708      * @cfg {String} mode
22709      * The shadow display mode.  Supports the following options:<br />
22710      * sides: Shadow displays on both sides and bottom only<br />
22711      * frame: Shadow displays equally on all four sides<br />
22712      * drop: Traditional bottom-right drop shadow (default)
22713      */
22714     /**
22715      * @cfg {String} offset
22716      * The number of pixels to offset the shadow from the element (defaults to 4)
22717      */
22718     offset: 4,
22719
22720     // private
22721     defaultMode: "drop",
22722
22723     /**
22724      * Displays the shadow under the target element
22725      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22726      */
22727     show : function(target){
22728         target = Roo.get(target);
22729         if(!this.el){
22730             this.el = Roo.Shadow.Pool.pull();
22731             if(this.el.dom.nextSibling != target.dom){
22732                 this.el.insertBefore(target);
22733             }
22734         }
22735         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22736         if(Roo.isIE){
22737             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22738         }
22739         this.realign(
22740             target.getLeft(true),
22741             target.getTop(true),
22742             target.getWidth(),
22743             target.getHeight()
22744         );
22745         this.el.dom.style.display = "block";
22746     },
22747
22748     /**
22749      * Returns true if the shadow is visible, else false
22750      */
22751     isVisible : function(){
22752         return this.el ? true : false;  
22753     },
22754
22755     /**
22756      * Direct alignment when values are already available. Show must be called at least once before
22757      * calling this method to ensure it is initialized.
22758      * @param {Number} left The target element left position
22759      * @param {Number} top The target element top position
22760      * @param {Number} width The target element width
22761      * @param {Number} height The target element height
22762      */
22763     realign : function(l, t, w, h){
22764         if(!this.el){
22765             return;
22766         }
22767         var a = this.adjusts, d = this.el.dom, s = d.style;
22768         var iea = 0;
22769         s.left = (l+a.l)+"px";
22770         s.top = (t+a.t)+"px";
22771         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22772  
22773         if(s.width != sws || s.height != shs){
22774             s.width = sws;
22775             s.height = shs;
22776             if(!Roo.isIE){
22777                 var cn = d.childNodes;
22778                 var sww = Math.max(0, (sw-12))+"px";
22779                 cn[0].childNodes[1].style.width = sww;
22780                 cn[1].childNodes[1].style.width = sww;
22781                 cn[2].childNodes[1].style.width = sww;
22782                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22783             }
22784         }
22785     },
22786
22787     /**
22788      * Hides this shadow
22789      */
22790     hide : function(){
22791         if(this.el){
22792             this.el.dom.style.display = "none";
22793             Roo.Shadow.Pool.push(this.el);
22794             delete this.el;
22795         }
22796     },
22797
22798     /**
22799      * Adjust the z-index of this shadow
22800      * @param {Number} zindex The new z-index
22801      */
22802     setZIndex : function(z){
22803         this.zIndex = z;
22804         if(this.el){
22805             this.el.setStyle("z-index", z);
22806         }
22807     }
22808 };
22809
22810 // Private utility class that manages the internal Shadow cache
22811 Roo.Shadow.Pool = function(){
22812     var p = [];
22813     var markup = Roo.isIE ?
22814                  '<div class="x-ie-shadow"></div>' :
22815                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22816     return {
22817         pull : function(){
22818             var sh = p.shift();
22819             if(!sh){
22820                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22821                 sh.autoBoxAdjust = false;
22822             }
22823             return sh;
22824         },
22825
22826         push : function(sh){
22827             p.push(sh);
22828         }
22829     };
22830 }();/*
22831  * Based on:
22832  * Ext JS Library 1.1.1
22833  * Copyright(c) 2006-2007, Ext JS, LLC.
22834  *
22835  * Originally Released Under LGPL - original licence link has changed is not relivant.
22836  *
22837  * Fork - LGPL
22838  * <script type="text/javascript">
22839  */
22840
22841 /**
22842  * @class Roo.BoxComponent
22843  * @extends Roo.Component
22844  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22845  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22846  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22847  * layout containers.
22848  * @constructor
22849  * @param {Roo.Element/String/Object} config The configuration options.
22850  */
22851 Roo.BoxComponent = function(config){
22852     Roo.Component.call(this, config);
22853     this.addEvents({
22854         /**
22855          * @event resize
22856          * Fires after the component is resized.
22857              * @param {Roo.Component} this
22858              * @param {Number} adjWidth The box-adjusted width that was set
22859              * @param {Number} adjHeight The box-adjusted height that was set
22860              * @param {Number} rawWidth The width that was originally specified
22861              * @param {Number} rawHeight The height that was originally specified
22862              */
22863         resize : true,
22864         /**
22865          * @event move
22866          * Fires after the component is moved.
22867              * @param {Roo.Component} this
22868              * @param {Number} x The new x position
22869              * @param {Number} y The new y position
22870              */
22871         move : true
22872     });
22873 };
22874
22875 Roo.extend(Roo.BoxComponent, Roo.Component, {
22876     // private, set in afterRender to signify that the component has been rendered
22877     boxReady : false,
22878     // private, used to defer height settings to subclasses
22879     deferHeight: false,
22880     /** @cfg {Number} width
22881      * width (optional) size of component
22882      */
22883      /** @cfg {Number} height
22884      * height (optional) size of component
22885      */
22886      
22887     /**
22888      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22889      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22890      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22891      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22892      * @return {Roo.BoxComponent} this
22893      */
22894     setSize : function(w, h){
22895         // support for standard size objects
22896         if(typeof w == 'object'){
22897             h = w.height;
22898             w = w.width;
22899         }
22900         // not rendered
22901         if(!this.boxReady){
22902             this.width = w;
22903             this.height = h;
22904             return this;
22905         }
22906
22907         // prevent recalcs when not needed
22908         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22909             return this;
22910         }
22911         this.lastSize = {width: w, height: h};
22912
22913         var adj = this.adjustSize(w, h);
22914         var aw = adj.width, ah = adj.height;
22915         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22916             var rz = this.getResizeEl();
22917             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22918                 rz.setSize(aw, ah);
22919             }else if(!this.deferHeight && ah !== undefined){
22920                 rz.setHeight(ah);
22921             }else if(aw !== undefined){
22922                 rz.setWidth(aw);
22923             }
22924             this.onResize(aw, ah, w, h);
22925             this.fireEvent('resize', this, aw, ah, w, h);
22926         }
22927         return this;
22928     },
22929
22930     /**
22931      * Gets the current size of the component's underlying element.
22932      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22933      */
22934     getSize : function(){
22935         return this.el.getSize();
22936     },
22937
22938     /**
22939      * Gets the current XY position of the component's underlying element.
22940      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22941      * @return {Array} The XY position of the element (e.g., [100, 200])
22942      */
22943     getPosition : function(local){
22944         if(local === true){
22945             return [this.el.getLeft(true), this.el.getTop(true)];
22946         }
22947         return this.xy || this.el.getXY();
22948     },
22949
22950     /**
22951      * Gets the current box measurements of the component's underlying element.
22952      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22953      * @returns {Object} box An object in the format {x, y, width, height}
22954      */
22955     getBox : function(local){
22956         var s = this.el.getSize();
22957         if(local){
22958             s.x = this.el.getLeft(true);
22959             s.y = this.el.getTop(true);
22960         }else{
22961             var xy = this.xy || this.el.getXY();
22962             s.x = xy[0];
22963             s.y = xy[1];
22964         }
22965         return s;
22966     },
22967
22968     /**
22969      * Sets the current box measurements of the component's underlying element.
22970      * @param {Object} box An object in the format {x, y, width, height}
22971      * @returns {Roo.BoxComponent} this
22972      */
22973     updateBox : function(box){
22974         this.setSize(box.width, box.height);
22975         this.setPagePosition(box.x, box.y);
22976         return this;
22977     },
22978
22979     // protected
22980     getResizeEl : function(){
22981         return this.resizeEl || this.el;
22982     },
22983
22984     // protected
22985     getPositionEl : function(){
22986         return this.positionEl || this.el;
22987     },
22988
22989     /**
22990      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22991      * This method fires the move event.
22992      * @param {Number} left The new left
22993      * @param {Number} top The new top
22994      * @returns {Roo.BoxComponent} this
22995      */
22996     setPosition : function(x, y){
22997         this.x = x;
22998         this.y = y;
22999         if(!this.boxReady){
23000             return this;
23001         }
23002         var adj = this.adjustPosition(x, y);
23003         var ax = adj.x, ay = adj.y;
23004
23005         var el = this.getPositionEl();
23006         if(ax !== undefined || ay !== undefined){
23007             if(ax !== undefined && ay !== undefined){
23008                 el.setLeftTop(ax, ay);
23009             }else if(ax !== undefined){
23010                 el.setLeft(ax);
23011             }else if(ay !== undefined){
23012                 el.setTop(ay);
23013             }
23014             this.onPosition(ax, ay);
23015             this.fireEvent('move', this, ax, ay);
23016         }
23017         return this;
23018     },
23019
23020     /**
23021      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23022      * This method fires the move event.
23023      * @param {Number} x The new x position
23024      * @param {Number} y The new y position
23025      * @returns {Roo.BoxComponent} this
23026      */
23027     setPagePosition : function(x, y){
23028         this.pageX = x;
23029         this.pageY = y;
23030         if(!this.boxReady){
23031             return;
23032         }
23033         if(x === undefined || y === undefined){ // cannot translate undefined points
23034             return;
23035         }
23036         var p = this.el.translatePoints(x, y);
23037         this.setPosition(p.left, p.top);
23038         return this;
23039     },
23040
23041     // private
23042     onRender : function(ct, position){
23043         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23044         if(this.resizeEl){
23045             this.resizeEl = Roo.get(this.resizeEl);
23046         }
23047         if(this.positionEl){
23048             this.positionEl = Roo.get(this.positionEl);
23049         }
23050     },
23051
23052     // private
23053     afterRender : function(){
23054         Roo.BoxComponent.superclass.afterRender.call(this);
23055         this.boxReady = true;
23056         this.setSize(this.width, this.height);
23057         if(this.x || this.y){
23058             this.setPosition(this.x, this.y);
23059         }
23060         if(this.pageX || this.pageY){
23061             this.setPagePosition(this.pageX, this.pageY);
23062         }
23063     },
23064
23065     /**
23066      * Force the component's size to recalculate based on the underlying element's current height and width.
23067      * @returns {Roo.BoxComponent} this
23068      */
23069     syncSize : function(){
23070         delete this.lastSize;
23071         this.setSize(this.el.getWidth(), this.el.getHeight());
23072         return this;
23073     },
23074
23075     /**
23076      * Called after the component is resized, this method is empty by default but can be implemented by any
23077      * subclass that needs to perform custom logic after a resize occurs.
23078      * @param {Number} adjWidth The box-adjusted width that was set
23079      * @param {Number} adjHeight The box-adjusted height that was set
23080      * @param {Number} rawWidth The width that was originally specified
23081      * @param {Number} rawHeight The height that was originally specified
23082      */
23083     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23084
23085     },
23086
23087     /**
23088      * Called after the component is moved, this method is empty by default but can be implemented by any
23089      * subclass that needs to perform custom logic after a move occurs.
23090      * @param {Number} x The new x position
23091      * @param {Number} y The new y position
23092      */
23093     onPosition : function(x, y){
23094
23095     },
23096
23097     // private
23098     adjustSize : function(w, h){
23099         if(this.autoWidth){
23100             w = 'auto';
23101         }
23102         if(this.autoHeight){
23103             h = 'auto';
23104         }
23105         return {width : w, height: h};
23106     },
23107
23108     // private
23109     adjustPosition : function(x, y){
23110         return {x : x, y: y};
23111     }
23112 });/*
23113  * Based on:
23114  * Ext JS Library 1.1.1
23115  * Copyright(c) 2006-2007, Ext JS, LLC.
23116  *
23117  * Originally Released Under LGPL - original licence link has changed is not relivant.
23118  *
23119  * Fork - LGPL
23120  * <script type="text/javascript">
23121  */
23122
23123
23124 /**
23125  * @class Roo.SplitBar
23126  * @extends Roo.util.Observable
23127  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23128  * <br><br>
23129  * Usage:
23130  * <pre><code>
23131 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23132                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23133 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23134 split.minSize = 100;
23135 split.maxSize = 600;
23136 split.animate = true;
23137 split.on('moved', splitterMoved);
23138 </code></pre>
23139  * @constructor
23140  * Create a new SplitBar
23141  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23142  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23143  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23144  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23145                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23146                         position of the SplitBar).
23147  */
23148 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23149     
23150     /** @private */
23151     this.el = Roo.get(dragElement, true);
23152     this.el.dom.unselectable = "on";
23153     /** @private */
23154     this.resizingEl = Roo.get(resizingElement, true);
23155
23156     /**
23157      * @private
23158      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23159      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23160      * @type Number
23161      */
23162     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23163     
23164     /**
23165      * The minimum size of the resizing element. (Defaults to 0)
23166      * @type Number
23167      */
23168     this.minSize = 0;
23169     
23170     /**
23171      * The maximum size of the resizing element. (Defaults to 2000)
23172      * @type Number
23173      */
23174     this.maxSize = 2000;
23175     
23176     /**
23177      * Whether to animate the transition to the new size
23178      * @type Boolean
23179      */
23180     this.animate = false;
23181     
23182     /**
23183      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23184      * @type Boolean
23185      */
23186     this.useShim = false;
23187     
23188     /** @private */
23189     this.shim = null;
23190     
23191     if(!existingProxy){
23192         /** @private */
23193         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23194     }else{
23195         this.proxy = Roo.get(existingProxy).dom;
23196     }
23197     /** @private */
23198     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23199     
23200     /** @private */
23201     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23202     
23203     /** @private */
23204     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23205     
23206     /** @private */
23207     this.dragSpecs = {};
23208     
23209     /**
23210      * @private The adapter to use to positon and resize elements
23211      */
23212     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23213     this.adapter.init(this);
23214     
23215     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23216         /** @private */
23217         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23218         this.el.addClass("x-splitbar-h");
23219     }else{
23220         /** @private */
23221         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23222         this.el.addClass("x-splitbar-v");
23223     }
23224     
23225     this.addEvents({
23226         /**
23227          * @event resize
23228          * Fires when the splitter is moved (alias for {@link #event-moved})
23229          * @param {Roo.SplitBar} this
23230          * @param {Number} newSize the new width or height
23231          */
23232         "resize" : true,
23233         /**
23234          * @event moved
23235          * Fires when the splitter is moved
23236          * @param {Roo.SplitBar} this
23237          * @param {Number} newSize the new width or height
23238          */
23239         "moved" : true,
23240         /**
23241          * @event beforeresize
23242          * Fires before the splitter is dragged
23243          * @param {Roo.SplitBar} this
23244          */
23245         "beforeresize" : true,
23246
23247         "beforeapply" : true
23248     });
23249
23250     Roo.util.Observable.call(this);
23251 };
23252
23253 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23254     onStartProxyDrag : function(x, y){
23255         this.fireEvent("beforeresize", this);
23256         if(!this.overlay){
23257             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23258             o.unselectable();
23259             o.enableDisplayMode("block");
23260             // all splitbars share the same overlay
23261             Roo.SplitBar.prototype.overlay = o;
23262         }
23263         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23264         this.overlay.show();
23265         Roo.get(this.proxy).setDisplayed("block");
23266         var size = this.adapter.getElementSize(this);
23267         this.activeMinSize = this.getMinimumSize();;
23268         this.activeMaxSize = this.getMaximumSize();;
23269         var c1 = size - this.activeMinSize;
23270         var c2 = Math.max(this.activeMaxSize - size, 0);
23271         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23272             this.dd.resetConstraints();
23273             this.dd.setXConstraint(
23274                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23275                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23276             );
23277             this.dd.setYConstraint(0, 0);
23278         }else{
23279             this.dd.resetConstraints();
23280             this.dd.setXConstraint(0, 0);
23281             this.dd.setYConstraint(
23282                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23283                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23284             );
23285          }
23286         this.dragSpecs.startSize = size;
23287         this.dragSpecs.startPoint = [x, y];
23288         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23289     },
23290     
23291     /** 
23292      * @private Called after the drag operation by the DDProxy
23293      */
23294     onEndProxyDrag : function(e){
23295         Roo.get(this.proxy).setDisplayed(false);
23296         var endPoint = Roo.lib.Event.getXY(e);
23297         if(this.overlay){
23298             this.overlay.hide();
23299         }
23300         var newSize;
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             newSize = this.dragSpecs.startSize + 
23303                 (this.placement == Roo.SplitBar.LEFT ?
23304                     endPoint[0] - this.dragSpecs.startPoint[0] :
23305                     this.dragSpecs.startPoint[0] - endPoint[0]
23306                 );
23307         }else{
23308             newSize = this.dragSpecs.startSize + 
23309                 (this.placement == Roo.SplitBar.TOP ?
23310                     endPoint[1] - this.dragSpecs.startPoint[1] :
23311                     this.dragSpecs.startPoint[1] - endPoint[1]
23312                 );
23313         }
23314         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23315         if(newSize != this.dragSpecs.startSize){
23316             if(this.fireEvent('beforeapply', this, newSize) !== false){
23317                 this.adapter.setElementSize(this, newSize);
23318                 this.fireEvent("moved", this, newSize);
23319                 this.fireEvent("resize", this, newSize);
23320             }
23321         }
23322     },
23323     
23324     /**
23325      * Get the adapter this SplitBar uses
23326      * @return The adapter object
23327      */
23328     getAdapter : function(){
23329         return this.adapter;
23330     },
23331     
23332     /**
23333      * Set the adapter this SplitBar uses
23334      * @param {Object} adapter A SplitBar adapter object
23335      */
23336     setAdapter : function(adapter){
23337         this.adapter = adapter;
23338         this.adapter.init(this);
23339     },
23340     
23341     /**
23342      * Gets the minimum size for the resizing element
23343      * @return {Number} The minimum size
23344      */
23345     getMinimumSize : function(){
23346         return this.minSize;
23347     },
23348     
23349     /**
23350      * Sets the minimum size for the resizing element
23351      * @param {Number} minSize The minimum size
23352      */
23353     setMinimumSize : function(minSize){
23354         this.minSize = minSize;
23355     },
23356     
23357     /**
23358      * Gets the maximum size for the resizing element
23359      * @return {Number} The maximum size
23360      */
23361     getMaximumSize : function(){
23362         return this.maxSize;
23363     },
23364     
23365     /**
23366      * Sets the maximum size for the resizing element
23367      * @param {Number} maxSize The maximum size
23368      */
23369     setMaximumSize : function(maxSize){
23370         this.maxSize = maxSize;
23371     },
23372     
23373     /**
23374      * Sets the initialize size for the resizing element
23375      * @param {Number} size The initial size
23376      */
23377     setCurrentSize : function(size){
23378         var oldAnimate = this.animate;
23379         this.animate = false;
23380         this.adapter.setElementSize(this, size);
23381         this.animate = oldAnimate;
23382     },
23383     
23384     /**
23385      * Destroy this splitbar. 
23386      * @param {Boolean} removeEl True to remove the element
23387      */
23388     destroy : function(removeEl){
23389         if(this.shim){
23390             this.shim.remove();
23391         }
23392         this.dd.unreg();
23393         this.proxy.parentNode.removeChild(this.proxy);
23394         if(removeEl){
23395             this.el.remove();
23396         }
23397     }
23398 });
23399
23400 /**
23401  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23402  */
23403 Roo.SplitBar.createProxy = function(dir){
23404     var proxy = new Roo.Element(document.createElement("div"));
23405     proxy.unselectable();
23406     var cls = 'x-splitbar-proxy';
23407     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23408     document.body.appendChild(proxy.dom);
23409     return proxy.dom;
23410 };
23411
23412 /** 
23413  * @class Roo.SplitBar.BasicLayoutAdapter
23414  * Default Adapter. It assumes the splitter and resizing element are not positioned
23415  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23416  */
23417 Roo.SplitBar.BasicLayoutAdapter = function(){
23418 };
23419
23420 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23421     // do nothing for now
23422     init : function(s){
23423     
23424     },
23425     /**
23426      * Called before drag operations to get the current size of the resizing element. 
23427      * @param {Roo.SplitBar} s The SplitBar using this adapter
23428      */
23429      getElementSize : function(s){
23430         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23431             return s.resizingEl.getWidth();
23432         }else{
23433             return s.resizingEl.getHeight();
23434         }
23435     },
23436     
23437     /**
23438      * Called after drag operations to set the size of the resizing element.
23439      * @param {Roo.SplitBar} s The SplitBar using this adapter
23440      * @param {Number} newSize The new size to set
23441      * @param {Function} onComplete A function to be invoked when resizing is complete
23442      */
23443     setElementSize : function(s, newSize, onComplete){
23444         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23445             if(!s.animate){
23446                 s.resizingEl.setWidth(newSize);
23447                 if(onComplete){
23448                     onComplete(s, newSize);
23449                 }
23450             }else{
23451                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23452             }
23453         }else{
23454             
23455             if(!s.animate){
23456                 s.resizingEl.setHeight(newSize);
23457                 if(onComplete){
23458                     onComplete(s, newSize);
23459                 }
23460             }else{
23461                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23462             }
23463         }
23464     }
23465 };
23466
23467 /** 
23468  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23469  * @extends Roo.SplitBar.BasicLayoutAdapter
23470  * Adapter that  moves the splitter element to align with the resized sizing element. 
23471  * Used with an absolute positioned SplitBar.
23472  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23473  * document.body, make sure you assign an id to the body element.
23474  */
23475 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23476     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23477     this.container = Roo.get(container);
23478 };
23479
23480 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23481     init : function(s){
23482         this.basic.init(s);
23483     },
23484     
23485     getElementSize : function(s){
23486         return this.basic.getElementSize(s);
23487     },
23488     
23489     setElementSize : function(s, newSize, onComplete){
23490         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23491     },
23492     
23493     moveSplitter : function(s){
23494         var yes = Roo.SplitBar;
23495         switch(s.placement){
23496             case yes.LEFT:
23497                 s.el.setX(s.resizingEl.getRight());
23498                 break;
23499             case yes.RIGHT:
23500                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23501                 break;
23502             case yes.TOP:
23503                 s.el.setY(s.resizingEl.getBottom());
23504                 break;
23505             case yes.BOTTOM:
23506                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23507                 break;
23508         }
23509     }
23510 };
23511
23512 /**
23513  * Orientation constant - Create a vertical SplitBar
23514  * @static
23515  * @type Number
23516  */
23517 Roo.SplitBar.VERTICAL = 1;
23518
23519 /**
23520  * Orientation constant - Create a horizontal SplitBar
23521  * @static
23522  * @type Number
23523  */
23524 Roo.SplitBar.HORIZONTAL = 2;
23525
23526 /**
23527  * Placement constant - The resizing element is to the left of the splitter element
23528  * @static
23529  * @type Number
23530  */
23531 Roo.SplitBar.LEFT = 1;
23532
23533 /**
23534  * Placement constant - The resizing element is to the right of the splitter element
23535  * @static
23536  * @type Number
23537  */
23538 Roo.SplitBar.RIGHT = 2;
23539
23540 /**
23541  * Placement constant - The resizing element is positioned above the splitter element
23542  * @static
23543  * @type Number
23544  */
23545 Roo.SplitBar.TOP = 3;
23546
23547 /**
23548  * Placement constant - The resizing element is positioned under splitter element
23549  * @static
23550  * @type Number
23551  */
23552 Roo.SplitBar.BOTTOM = 4;
23553 /*
23554  * Based on:
23555  * Ext JS Library 1.1.1
23556  * Copyright(c) 2006-2007, Ext JS, LLC.
23557  *
23558  * Originally Released Under LGPL - original licence link has changed is not relivant.
23559  *
23560  * Fork - LGPL
23561  * <script type="text/javascript">
23562  */
23563
23564 /**
23565  * @class Roo.View
23566  * @extends Roo.util.Observable
23567  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23568  * This class also supports single and multi selection modes. <br>
23569  * Create a data model bound view:
23570  <pre><code>
23571  var store = new Roo.data.Store(...);
23572
23573  var view = new Roo.View({
23574     el : "my-element",
23575     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23576  
23577     singleSelect: true,
23578     selectedClass: "ydataview-selected",
23579     store: store
23580  });
23581
23582  // listen for node click?
23583  view.on("click", function(vw, index, node, e){
23584  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23585  });
23586
23587  // load XML data
23588  dataModel.load("foobar.xml");
23589  </code></pre>
23590  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23591  * <br><br>
23592  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23593  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23594  * 
23595  * Note: old style constructor is still suported (container, template, config)
23596  * 
23597  * @constructor
23598  * Create a new View
23599  * @param {Object} config The config object
23600  * 
23601  */
23602 Roo.View = function(config, depreciated_tpl, depreciated_config){
23603     
23604     if (typeof(depreciated_tpl) == 'undefined') {
23605         // new way.. - universal constructor.
23606         Roo.apply(this, config);
23607         this.el  = Roo.get(this.el);
23608     } else {
23609         // old format..
23610         this.el  = Roo.get(config);
23611         this.tpl = depreciated_tpl;
23612         Roo.apply(this, depreciated_config);
23613     }
23614      
23615     
23616     if(typeof(this.tpl) == "string"){
23617         this.tpl = new Roo.Template(this.tpl);
23618     } else {
23619         // support xtype ctors..
23620         this.tpl = new Roo.factory(this.tpl, Roo);
23621     }
23622     
23623     
23624     this.tpl.compile();
23625    
23626
23627      
23628     /** @private */
23629     this.addEvents({
23630         /**
23631          * @event beforeclick
23632          * Fires before a click is processed. Returns false to cancel the default action.
23633          * @param {Roo.View} this
23634          * @param {Number} index The index of the target node
23635          * @param {HTMLElement} node The target node
23636          * @param {Roo.EventObject} e The raw event object
23637          */
23638             "beforeclick" : true,
23639         /**
23640          * @event click
23641          * Fires when a template node is clicked.
23642          * @param {Roo.View} this
23643          * @param {Number} index The index of the target node
23644          * @param {HTMLElement} node The target node
23645          * @param {Roo.EventObject} e The raw event object
23646          */
23647             "click" : true,
23648         /**
23649          * @event dblclick
23650          * Fires when a template node is double clicked.
23651          * @param {Roo.View} this
23652          * @param {Number} index The index of the target node
23653          * @param {HTMLElement} node The target node
23654          * @param {Roo.EventObject} e The raw event object
23655          */
23656             "dblclick" : true,
23657         /**
23658          * @event contextmenu
23659          * Fires when a template node is right clicked.
23660          * @param {Roo.View} this
23661          * @param {Number} index The index of the target node
23662          * @param {HTMLElement} node The target node
23663          * @param {Roo.EventObject} e The raw event object
23664          */
23665             "contextmenu" : true,
23666         /**
23667          * @event selectionchange
23668          * Fires when the selected nodes change.
23669          * @param {Roo.View} this
23670          * @param {Array} selections Array of the selected nodes
23671          */
23672             "selectionchange" : true,
23673     
23674         /**
23675          * @event beforeselect
23676          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23677          * @param {Roo.View} this
23678          * @param {HTMLElement} node The node to be selected
23679          * @param {Array} selections Array of currently selected nodes
23680          */
23681             "beforeselect" : true,
23682         /**
23683          * @event preparedata
23684          * Fires on every row to render, to allow you to change the data.
23685          * @param {Roo.View} this
23686          * @param {Object} data to be rendered (change this)
23687          */
23688           "preparedata" : true
23689         });
23690
23691     this.el.on({
23692         "click": this.onClick,
23693         "dblclick": this.onDblClick,
23694         "contextmenu": this.onContextMenu,
23695         scope:this
23696     });
23697
23698     this.selections = [];
23699     this.nodes = [];
23700     this.cmp = new Roo.CompositeElementLite([]);
23701     if(this.store){
23702         this.store = Roo.factory(this.store, Roo.data);
23703         this.setStore(this.store, true);
23704     }
23705     Roo.View.superclass.constructor.call(this);
23706 };
23707
23708 Roo.extend(Roo.View, Roo.util.Observable, {
23709     
23710      /**
23711      * @cfg {Roo.data.Store} store Data store to load data from.
23712      */
23713     store : false,
23714     
23715     /**
23716      * @cfg {String|Roo.Element} el The container element.
23717      */
23718     el : '',
23719     
23720     /**
23721      * @cfg {String|Roo.Template} tpl The template used by this View 
23722      */
23723     tpl : false,
23724     
23725     /**
23726      * @cfg {String} selectedClass The css class to add to selected nodes
23727      */
23728     selectedClass : "x-view-selected",
23729      /**
23730      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23731      */
23732     emptyText : "",
23733     /**
23734      * @cfg {Boolean} multiSelect Allow multiple selection
23735      */
23736     multiSelect : false,
23737     /**
23738      * @cfg {Boolean} singleSelect Allow single selection
23739      */
23740     singleSelect:  false,
23741     
23742     /**
23743      * @cfg {Boolean} toggleSelect - selecting 
23744      */
23745     toggleSelect : false,
23746     
23747     /**
23748      * Returns the element this view is bound to.
23749      * @return {Roo.Element}
23750      */
23751     getEl : function(){
23752         return this.el;
23753     },
23754
23755     /**
23756      * Refreshes the view.
23757      */
23758     refresh : function(){
23759         var t = this.tpl;
23760         this.clearSelections();
23761         this.el.update("");
23762         var html = [];
23763         var records = this.store.getRange();
23764         if(records.length < 1){
23765             this.el.update(this.emptyText);
23766             return;
23767         }
23768         for(var i = 0, len = records.length; i < len; i++){
23769             var data = this.prepareData(records[i].data, i, records[i]);
23770             this.fireEvent("preparedata", this, data, i, records[i]);
23771             html[html.length] = t.apply(data);
23772         }
23773         this.el.update(html.join(""));
23774         this.nodes = this.el.dom.childNodes;
23775         this.updateIndexes(0);
23776     },
23777
23778     /**
23779      * Function to override to reformat the data that is sent to
23780      * the template for each node.
23781      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23782      * a JSON object for an UpdateManager bound view).
23783      */
23784     prepareData : function(data){
23785         return data;
23786     },
23787
23788     onUpdate : function(ds, record){
23789         this.clearSelections();
23790         var index = this.store.indexOf(record);
23791         var n = this.nodes[index];
23792         this.tpl.insertBefore(n, this.prepareData(record.data));
23793         n.parentNode.removeChild(n);
23794         this.updateIndexes(index, index);
23795     },
23796
23797     onAdd : function(ds, records, index){
23798         this.clearSelections();
23799         if(this.nodes.length == 0){
23800             this.refresh();
23801             return;
23802         }
23803         var n = this.nodes[index];
23804         for(var i = 0, len = records.length; i < len; i++){
23805             var d = this.prepareData(records[i].data);
23806             if(n){
23807                 this.tpl.insertBefore(n, d);
23808             }else{
23809                 this.tpl.append(this.el, d);
23810             }
23811         }
23812         this.updateIndexes(index);
23813     },
23814
23815     onRemove : function(ds, record, index){
23816         this.clearSelections();
23817         this.el.dom.removeChild(this.nodes[index]);
23818         this.updateIndexes(index);
23819     },
23820
23821     /**
23822      * Refresh an individual node.
23823      * @param {Number} index
23824      */
23825     refreshNode : function(index){
23826         this.onUpdate(this.store, this.store.getAt(index));
23827     },
23828
23829     updateIndexes : function(startIndex, endIndex){
23830         var ns = this.nodes;
23831         startIndex = startIndex || 0;
23832         endIndex = endIndex || ns.length - 1;
23833         for(var i = startIndex; i <= endIndex; i++){
23834             ns[i].nodeIndex = i;
23835         }
23836     },
23837
23838     /**
23839      * Changes the data store this view uses and refresh the view.
23840      * @param {Store} store
23841      */
23842     setStore : function(store, initial){
23843         if(!initial && this.store){
23844             this.store.un("datachanged", this.refresh);
23845             this.store.un("add", this.onAdd);
23846             this.store.un("remove", this.onRemove);
23847             this.store.un("update", this.onUpdate);
23848             this.store.un("clear", this.refresh);
23849         }
23850         if(store){
23851           
23852             store.on("datachanged", this.refresh, this);
23853             store.on("add", this.onAdd, this);
23854             store.on("remove", this.onRemove, this);
23855             store.on("update", this.onUpdate, this);
23856             store.on("clear", this.refresh, this);
23857         }
23858         
23859         if(store){
23860             this.refresh();
23861         }
23862     },
23863
23864     /**
23865      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23866      * @param {HTMLElement} node
23867      * @return {HTMLElement} The template node
23868      */
23869     findItemFromChild : function(node){
23870         var el = this.el.dom;
23871         if(!node || node.parentNode == el){
23872                     return node;
23873             }
23874             var p = node.parentNode;
23875             while(p && p != el){
23876             if(p.parentNode == el){
23877                 return p;
23878             }
23879             p = p.parentNode;
23880         }
23881             return null;
23882     },
23883
23884     /** @ignore */
23885     onClick : function(e){
23886         var item = this.findItemFromChild(e.getTarget());
23887         if(item){
23888             var index = this.indexOf(item);
23889             if(this.onItemClick(item, index, e) !== false){
23890                 this.fireEvent("click", this, index, item, e);
23891             }
23892         }else{
23893             this.clearSelections();
23894         }
23895     },
23896
23897     /** @ignore */
23898     onContextMenu : function(e){
23899         var item = this.findItemFromChild(e.getTarget());
23900         if(item){
23901             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23902         }
23903     },
23904
23905     /** @ignore */
23906     onDblClick : function(e){
23907         var item = this.findItemFromChild(e.getTarget());
23908         if(item){
23909             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23910         }
23911     },
23912
23913     onItemClick : function(item, index, e)
23914     {
23915         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23916             return false;
23917         }
23918         if (this.toggleSelect) {
23919             var m = this.isSelected(item) ? 'unselect' : 'select';
23920             Roo.log(m);
23921             var _t = this;
23922             _t[m](item, true, false);
23923             return true;
23924         }
23925         if(this.multiSelect || this.singleSelect){
23926             if(this.multiSelect && e.shiftKey && this.lastSelection){
23927                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23928             }else{
23929                 this.select(item, this.multiSelect && e.ctrlKey);
23930                 this.lastSelection = item;
23931             }
23932             e.preventDefault();
23933         }
23934         return true;
23935     },
23936
23937     /**
23938      * Get the number of selected nodes.
23939      * @return {Number}
23940      */
23941     getSelectionCount : function(){
23942         return this.selections.length;
23943     },
23944
23945     /**
23946      * Get the currently selected nodes.
23947      * @return {Array} An array of HTMLElements
23948      */
23949     getSelectedNodes : function(){
23950         return this.selections;
23951     },
23952
23953     /**
23954      * Get the indexes of the selected nodes.
23955      * @return {Array}
23956      */
23957     getSelectedIndexes : function(){
23958         var indexes = [], s = this.selections;
23959         for(var i = 0, len = s.length; i < len; i++){
23960             indexes.push(s[i].nodeIndex);
23961         }
23962         return indexes;
23963     },
23964
23965     /**
23966      * Clear all selections
23967      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23968      */
23969     clearSelections : function(suppressEvent){
23970         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23971             this.cmp.elements = this.selections;
23972             this.cmp.removeClass(this.selectedClass);
23973             this.selections = [];
23974             if(!suppressEvent){
23975                 this.fireEvent("selectionchange", this, this.selections);
23976             }
23977         }
23978     },
23979
23980     /**
23981      * Returns true if the passed node is selected
23982      * @param {HTMLElement/Number} node The node or node index
23983      * @return {Boolean}
23984      */
23985     isSelected : function(node){
23986         var s = this.selections;
23987         if(s.length < 1){
23988             return false;
23989         }
23990         node = this.getNode(node);
23991         return s.indexOf(node) !== -1;
23992     },
23993
23994     /**
23995      * Selects nodes.
23996      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
23997      * @param {Boolean} keepExisting (optional) true to keep existing selections
23998      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23999      */
24000     select : function(nodeInfo, keepExisting, suppressEvent){
24001         if(nodeInfo instanceof Array){
24002             if(!keepExisting){
24003                 this.clearSelections(true);
24004             }
24005             for(var i = 0, len = nodeInfo.length; i < len; i++){
24006                 this.select(nodeInfo[i], true, true);
24007             }
24008             return;
24009         } 
24010         var node = this.getNode(nodeInfo);
24011         if(!node || this.isSelected(node)){
24012             return; // already selected.
24013         }
24014         if(!keepExisting){
24015             this.clearSelections(true);
24016         }
24017         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24018             Roo.fly(node).addClass(this.selectedClass);
24019             this.selections.push(node);
24020             if(!suppressEvent){
24021                 this.fireEvent("selectionchange", this, this.selections);
24022             }
24023         }
24024         
24025         
24026     },
24027       /**
24028      * Unselects nodes.
24029      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24030      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24031      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24032      */
24033     unselect : function(nodeInfo, keepExisting, suppressEvent)
24034     {
24035         if(nodeInfo instanceof Array){
24036             Roo.each(this.selections, function(s) {
24037                 this.unselect(s, nodeInfo);
24038             }, this);
24039             return;
24040         }
24041         var node = this.getNode(nodeInfo);
24042         if(!node || !this.isSelected(node)){
24043             Roo.log("not selected");
24044             return; // not selected.
24045         }
24046         // fireevent???
24047         var ns = [];
24048         Roo.each(this.selections, function(s) {
24049             if (s == node ) {
24050                 Roo.fly(node).removeClass(this.selectedClass);
24051
24052                 return;
24053             }
24054             ns.push(s);
24055         },this);
24056         
24057         this.selections= ns;
24058         this.fireEvent("selectionchange", this, this.selections);
24059     },
24060
24061     /**
24062      * Gets a template node.
24063      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24064      * @return {HTMLElement} The node or null if it wasn't found
24065      */
24066     getNode : function(nodeInfo){
24067         if(typeof nodeInfo == "string"){
24068             return document.getElementById(nodeInfo);
24069         }else if(typeof nodeInfo == "number"){
24070             return this.nodes[nodeInfo];
24071         }
24072         return nodeInfo;
24073     },
24074
24075     /**
24076      * Gets a range template nodes.
24077      * @param {Number} startIndex
24078      * @param {Number} endIndex
24079      * @return {Array} An array of nodes
24080      */
24081     getNodes : function(start, end){
24082         var ns = this.nodes;
24083         start = start || 0;
24084         end = typeof end == "undefined" ? ns.length - 1 : end;
24085         var nodes = [];
24086         if(start <= end){
24087             for(var i = start; i <= end; i++){
24088                 nodes.push(ns[i]);
24089             }
24090         } else{
24091             for(var i = start; i >= end; i--){
24092                 nodes.push(ns[i]);
24093             }
24094         }
24095         return nodes;
24096     },
24097
24098     /**
24099      * Finds the index of the passed node
24100      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24101      * @return {Number} The index of the node or -1
24102      */
24103     indexOf : function(node){
24104         node = this.getNode(node);
24105         if(typeof node.nodeIndex == "number"){
24106             return node.nodeIndex;
24107         }
24108         var ns = this.nodes;
24109         for(var i = 0, len = ns.length; i < len; i++){
24110             if(ns[i] == node){
24111                 return i;
24112             }
24113         }
24114         return -1;
24115     }
24116 });
24117 /*
24118  * Based on:
24119  * Ext JS Library 1.1.1
24120  * Copyright(c) 2006-2007, Ext JS, LLC.
24121  *
24122  * Originally Released Under LGPL - original licence link has changed is not relivant.
24123  *
24124  * Fork - LGPL
24125  * <script type="text/javascript">
24126  */
24127
24128 /**
24129  * @class Roo.JsonView
24130  * @extends Roo.View
24131  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24132 <pre><code>
24133 var view = new Roo.JsonView({
24134     container: "my-element",
24135     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24136     multiSelect: true, 
24137     jsonRoot: "data" 
24138 });
24139
24140 // listen for node click?
24141 view.on("click", function(vw, index, node, e){
24142     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24143 });
24144
24145 // direct load of JSON data
24146 view.load("foobar.php");
24147
24148 // Example from my blog list
24149 var tpl = new Roo.Template(
24150     '&lt;div class="entry"&gt;' +
24151     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24152     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24153     "&lt;/div&gt;&lt;hr /&gt;"
24154 );
24155
24156 var moreView = new Roo.JsonView({
24157     container :  "entry-list", 
24158     template : tpl,
24159     jsonRoot: "posts"
24160 });
24161 moreView.on("beforerender", this.sortEntries, this);
24162 moreView.load({
24163     url: "/blog/get-posts.php",
24164     params: "allposts=true",
24165     text: "Loading Blog Entries..."
24166 });
24167 </code></pre>
24168
24169 * Note: old code is supported with arguments : (container, template, config)
24170
24171
24172  * @constructor
24173  * Create a new JsonView
24174  * 
24175  * @param {Object} config The config object
24176  * 
24177  */
24178 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24179     
24180     
24181     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24182
24183     var um = this.el.getUpdateManager();
24184     um.setRenderer(this);
24185     um.on("update", this.onLoad, this);
24186     um.on("failure", this.onLoadException, this);
24187
24188     /**
24189      * @event beforerender
24190      * Fires before rendering of the downloaded JSON data.
24191      * @param {Roo.JsonView} this
24192      * @param {Object} data The JSON data loaded
24193      */
24194     /**
24195      * @event load
24196      * Fires when data is loaded.
24197      * @param {Roo.JsonView} this
24198      * @param {Object} data The JSON data loaded
24199      * @param {Object} response The raw Connect response object
24200      */
24201     /**
24202      * @event loadexception
24203      * Fires when loading fails.
24204      * @param {Roo.JsonView} this
24205      * @param {Object} response The raw Connect response object
24206      */
24207     this.addEvents({
24208         'beforerender' : true,
24209         'load' : true,
24210         'loadexception' : true
24211     });
24212 };
24213 Roo.extend(Roo.JsonView, Roo.View, {
24214     /**
24215      * @type {String} The root property in the loaded JSON object that contains the data
24216      */
24217     jsonRoot : "",
24218
24219     /**
24220      * Refreshes the view.
24221      */
24222     refresh : function(){
24223         this.clearSelections();
24224         this.el.update("");
24225         var html = [];
24226         var o = this.jsonData;
24227         if(o && o.length > 0){
24228             for(var i = 0, len = o.length; i < len; i++){
24229                 var data = this.prepareData(o[i], i, o);
24230                 html[html.length] = this.tpl.apply(data);
24231             }
24232         }else{
24233             html.push(this.emptyText);
24234         }
24235         this.el.update(html.join(""));
24236         this.nodes = this.el.dom.childNodes;
24237         this.updateIndexes(0);
24238     },
24239
24240     /**
24241      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24242      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24243      <pre><code>
24244      view.load({
24245          url: "your-url.php",
24246          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24247          callback: yourFunction,
24248          scope: yourObject, //(optional scope)
24249          discardUrl: false,
24250          nocache: false,
24251          text: "Loading...",
24252          timeout: 30,
24253          scripts: false
24254      });
24255      </code></pre>
24256      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24257      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24258      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24259      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24260      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24261      */
24262     load : function(){
24263         var um = this.el.getUpdateManager();
24264         um.update.apply(um, arguments);
24265     },
24266
24267     render : function(el, response){
24268         this.clearSelections();
24269         this.el.update("");
24270         var o;
24271         try{
24272             o = Roo.util.JSON.decode(response.responseText);
24273             if(this.jsonRoot){
24274                 
24275                 o = o[this.jsonRoot];
24276             }
24277         } catch(e){
24278         }
24279         /**
24280          * The current JSON data or null
24281          */
24282         this.jsonData = o;
24283         this.beforeRender();
24284         this.refresh();
24285     },
24286
24287 /**
24288  * Get the number of records in the current JSON dataset
24289  * @return {Number}
24290  */
24291     getCount : function(){
24292         return this.jsonData ? this.jsonData.length : 0;
24293     },
24294
24295 /**
24296  * Returns the JSON object for the specified node(s)
24297  * @param {HTMLElement/Array} node The node or an array of nodes
24298  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24299  * you get the JSON object for the node
24300  */
24301     getNodeData : function(node){
24302         if(node instanceof Array){
24303             var data = [];
24304             for(var i = 0, len = node.length; i < len; i++){
24305                 data.push(this.getNodeData(node[i]));
24306             }
24307             return data;
24308         }
24309         return this.jsonData[this.indexOf(node)] || null;
24310     },
24311
24312     beforeRender : function(){
24313         this.snapshot = this.jsonData;
24314         if(this.sortInfo){
24315             this.sort.apply(this, this.sortInfo);
24316         }
24317         this.fireEvent("beforerender", this, this.jsonData);
24318     },
24319
24320     onLoad : function(el, o){
24321         this.fireEvent("load", this, this.jsonData, o);
24322     },
24323
24324     onLoadException : function(el, o){
24325         this.fireEvent("loadexception", this, o);
24326     },
24327
24328 /**
24329  * Filter the data by a specific property.
24330  * @param {String} property A property on your JSON objects
24331  * @param {String/RegExp} value Either string that the property values
24332  * should start with, or a RegExp to test against the property
24333  */
24334     filter : function(property, value){
24335         if(this.jsonData){
24336             var data = [];
24337             var ss = this.snapshot;
24338             if(typeof value == "string"){
24339                 var vlen = value.length;
24340                 if(vlen == 0){
24341                     this.clearFilter();
24342                     return;
24343                 }
24344                 value = value.toLowerCase();
24345                 for(var i = 0, len = ss.length; i < len; i++){
24346                     var o = ss[i];
24347                     if(o[property].substr(0, vlen).toLowerCase() == value){
24348                         data.push(o);
24349                     }
24350                 }
24351             } else if(value.exec){ // regex?
24352                 for(var i = 0, len = ss.length; i < len; i++){
24353                     var o = ss[i];
24354                     if(value.test(o[property])){
24355                         data.push(o);
24356                     }
24357                 }
24358             } else{
24359                 return;
24360             }
24361             this.jsonData = data;
24362             this.refresh();
24363         }
24364     },
24365
24366 /**
24367  * Filter by a function. The passed function will be called with each
24368  * object in the current dataset. If the function returns true the value is kept,
24369  * otherwise it is filtered.
24370  * @param {Function} fn
24371  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24372  */
24373     filterBy : function(fn, scope){
24374         if(this.jsonData){
24375             var data = [];
24376             var ss = this.snapshot;
24377             for(var i = 0, len = ss.length; i < len; i++){
24378                 var o = ss[i];
24379                 if(fn.call(scope || this, o)){
24380                     data.push(o);
24381                 }
24382             }
24383             this.jsonData = data;
24384             this.refresh();
24385         }
24386     },
24387
24388 /**
24389  * Clears the current filter.
24390  */
24391     clearFilter : function(){
24392         if(this.snapshot && this.jsonData != this.snapshot){
24393             this.jsonData = this.snapshot;
24394             this.refresh();
24395         }
24396     },
24397
24398
24399 /**
24400  * Sorts the data for this view and refreshes it.
24401  * @param {String} property A property on your JSON objects to sort on
24402  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24403  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24404  */
24405     sort : function(property, dir, sortType){
24406         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24407         if(this.jsonData){
24408             var p = property;
24409             var dsc = dir && dir.toLowerCase() == "desc";
24410             var f = function(o1, o2){
24411                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24412                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24413                 ;
24414                 if(v1 < v2){
24415                     return dsc ? +1 : -1;
24416                 } else if(v1 > v2){
24417                     return dsc ? -1 : +1;
24418                 } else{
24419                     return 0;
24420                 }
24421             };
24422             this.jsonData.sort(f);
24423             this.refresh();
24424             if(this.jsonData != this.snapshot){
24425                 this.snapshot.sort(f);
24426             }
24427         }
24428     }
24429 });/*
24430  * Based on:
24431  * Ext JS Library 1.1.1
24432  * Copyright(c) 2006-2007, Ext JS, LLC.
24433  *
24434  * Originally Released Under LGPL - original licence link has changed is not relivant.
24435  *
24436  * Fork - LGPL
24437  * <script type="text/javascript">
24438  */
24439  
24440
24441 /**
24442  * @class Roo.ColorPalette
24443  * @extends Roo.Component
24444  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24445  * Here's an example of typical usage:
24446  * <pre><code>
24447 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24448 cp.render('my-div');
24449
24450 cp.on('select', function(palette, selColor){
24451     // do something with selColor
24452 });
24453 </code></pre>
24454  * @constructor
24455  * Create a new ColorPalette
24456  * @param {Object} config The config object
24457  */
24458 Roo.ColorPalette = function(config){
24459     Roo.ColorPalette.superclass.constructor.call(this, config);
24460     this.addEvents({
24461         /**
24462              * @event select
24463              * Fires when a color is selected
24464              * @param {ColorPalette} this
24465              * @param {String} color The 6-digit color hex code (without the # symbol)
24466              */
24467         select: true
24468     });
24469
24470     if(this.handler){
24471         this.on("select", this.handler, this.scope, true);
24472     }
24473 };
24474 Roo.extend(Roo.ColorPalette, Roo.Component, {
24475     /**
24476      * @cfg {String} itemCls
24477      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24478      */
24479     itemCls : "x-color-palette",
24480     /**
24481      * @cfg {String} value
24482      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24483      * the hex codes are case-sensitive.
24484      */
24485     value : null,
24486     clickEvent:'click',
24487     // private
24488     ctype: "Roo.ColorPalette",
24489
24490     /**
24491      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24492      */
24493     allowReselect : false,
24494
24495     /**
24496      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24497      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24498      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24499      * of colors with the width setting until the box is symmetrical.</p>
24500      * <p>You can override individual colors if needed:</p>
24501      * <pre><code>
24502 var cp = new Roo.ColorPalette();
24503 cp.colors[0] = "FF0000";  // change the first box to red
24504 </code></pre>
24505
24506 Or you can provide a custom array of your own for complete control:
24507 <pre><code>
24508 var cp = new Roo.ColorPalette();
24509 cp.colors = ["000000", "993300", "333300"];
24510 </code></pre>
24511      * @type Array
24512      */
24513     colors : [
24514         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24515         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24516         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24517         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24518         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24519     ],
24520
24521     // private
24522     onRender : function(container, position){
24523         var t = new Roo.MasterTemplate(
24524             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24525         );
24526         var c = this.colors;
24527         for(var i = 0, len = c.length; i < len; i++){
24528             t.add([c[i]]);
24529         }
24530         var el = document.createElement("div");
24531         el.className = this.itemCls;
24532         t.overwrite(el);
24533         container.dom.insertBefore(el, position);
24534         this.el = Roo.get(el);
24535         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24536         if(this.clickEvent != 'click'){
24537             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24538         }
24539     },
24540
24541     // private
24542     afterRender : function(){
24543         Roo.ColorPalette.superclass.afterRender.call(this);
24544         if(this.value){
24545             var s = this.value;
24546             this.value = null;
24547             this.select(s);
24548         }
24549     },
24550
24551     // private
24552     handleClick : function(e, t){
24553         e.preventDefault();
24554         if(!this.disabled){
24555             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24556             this.select(c.toUpperCase());
24557         }
24558     },
24559
24560     /**
24561      * Selects the specified color in the palette (fires the select event)
24562      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24563      */
24564     select : function(color){
24565         color = color.replace("#", "");
24566         if(color != this.value || this.allowReselect){
24567             var el = this.el;
24568             if(this.value){
24569                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24570             }
24571             el.child("a.color-"+color).addClass("x-color-palette-sel");
24572             this.value = color;
24573             this.fireEvent("select", this, color);
24574         }
24575     }
24576 });/*
24577  * Based on:
24578  * Ext JS Library 1.1.1
24579  * Copyright(c) 2006-2007, Ext JS, LLC.
24580  *
24581  * Originally Released Under LGPL - original licence link has changed is not relivant.
24582  *
24583  * Fork - LGPL
24584  * <script type="text/javascript">
24585  */
24586  
24587 /**
24588  * @class Roo.DatePicker
24589  * @extends Roo.Component
24590  * Simple date picker class.
24591  * @constructor
24592  * Create a new DatePicker
24593  * @param {Object} config The config object
24594  */
24595 Roo.DatePicker = function(config){
24596     Roo.DatePicker.superclass.constructor.call(this, config);
24597
24598     this.value = config && config.value ?
24599                  config.value.clearTime() : new Date().clearTime();
24600
24601     this.addEvents({
24602         /**
24603              * @event select
24604              * Fires when a date is selected
24605              * @param {DatePicker} this
24606              * @param {Date} date The selected date
24607              */
24608         'select': true,
24609         /**
24610              * @event monthchange
24611              * Fires when the displayed month changes 
24612              * @param {DatePicker} this
24613              * @param {Date} date The selected month
24614              */
24615         'monthchange': true
24616     });
24617
24618     if(this.handler){
24619         this.on("select", this.handler,  this.scope || this);
24620     }
24621     // build the disabledDatesRE
24622     if(!this.disabledDatesRE && this.disabledDates){
24623         var dd = this.disabledDates;
24624         var re = "(?:";
24625         for(var i = 0; i < dd.length; i++){
24626             re += dd[i];
24627             if(i != dd.length-1) re += "|";
24628         }
24629         this.disabledDatesRE = new RegExp(re + ")");
24630     }
24631 };
24632
24633 Roo.extend(Roo.DatePicker, Roo.Component, {
24634     /**
24635      * @cfg {String} todayText
24636      * The text to display on the button that selects the current date (defaults to "Today")
24637      */
24638     todayText : "Today",
24639     /**
24640      * @cfg {String} okText
24641      * The text to display on the ok button
24642      */
24643     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24644     /**
24645      * @cfg {String} cancelText
24646      * The text to display on the cancel button
24647      */
24648     cancelText : "Cancel",
24649     /**
24650      * @cfg {String} todayTip
24651      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24652      */
24653     todayTip : "{0} (Spacebar)",
24654     /**
24655      * @cfg {Date} minDate
24656      * Minimum allowable date (JavaScript date object, defaults to null)
24657      */
24658     minDate : null,
24659     /**
24660      * @cfg {Date} maxDate
24661      * Maximum allowable date (JavaScript date object, defaults to null)
24662      */
24663     maxDate : null,
24664     /**
24665      * @cfg {String} minText
24666      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24667      */
24668     minText : "This date is before the minimum date",
24669     /**
24670      * @cfg {String} maxText
24671      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24672      */
24673     maxText : "This date is after the maximum date",
24674     /**
24675      * @cfg {String} format
24676      * The default date format string which can be overriden for localization support.  The format must be
24677      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24678      */
24679     format : "m/d/y",
24680     /**
24681      * @cfg {Array} disabledDays
24682      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24683      */
24684     disabledDays : null,
24685     /**
24686      * @cfg {String} disabledDaysText
24687      * The tooltip to display when the date falls on a disabled day (defaults to "")
24688      */
24689     disabledDaysText : "",
24690     /**
24691      * @cfg {RegExp} disabledDatesRE
24692      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24693      */
24694     disabledDatesRE : null,
24695     /**
24696      * @cfg {String} disabledDatesText
24697      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24698      */
24699     disabledDatesText : "",
24700     /**
24701      * @cfg {Boolean} constrainToViewport
24702      * True to constrain the date picker to the viewport (defaults to true)
24703      */
24704     constrainToViewport : true,
24705     /**
24706      * @cfg {Array} monthNames
24707      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24708      */
24709     monthNames : Date.monthNames,
24710     /**
24711      * @cfg {Array} dayNames
24712      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24713      */
24714     dayNames : Date.dayNames,
24715     /**
24716      * @cfg {String} nextText
24717      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24718      */
24719     nextText: 'Next Month (Control+Right)',
24720     /**
24721      * @cfg {String} prevText
24722      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24723      */
24724     prevText: 'Previous Month (Control+Left)',
24725     /**
24726      * @cfg {String} monthYearText
24727      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24728      */
24729     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24730     /**
24731      * @cfg {Number} startDay
24732      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24733      */
24734     startDay : 0,
24735     /**
24736      * @cfg {Bool} showClear
24737      * Show a clear button (usefull for date form elements that can be blank.)
24738      */
24739     
24740     showClear: false,
24741     
24742     /**
24743      * Sets the value of the date field
24744      * @param {Date} value The date to set
24745      */
24746     setValue : function(value){
24747         var old = this.value;
24748         this.value = value.clearTime(true);
24749         if(this.el){
24750             this.update(this.value);
24751         }
24752     },
24753
24754     /**
24755      * Gets the current selected value of the date field
24756      * @return {Date} The selected date
24757      */
24758     getValue : function(){
24759         return this.value;
24760     },
24761
24762     // private
24763     focus : function(){
24764         if(this.el){
24765             this.update(this.activeDate);
24766         }
24767     },
24768
24769     // private
24770     onRender : function(container, position){
24771         var m = [
24772              '<table cellspacing="0">',
24773                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24774                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24775         var dn = this.dayNames;
24776         for(var i = 0; i < 7; i++){
24777             var d = this.startDay+i;
24778             if(d > 6){
24779                 d = d-7;
24780             }
24781             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24782         }
24783         m[m.length] = "</tr></thead><tbody><tr>";
24784         for(var i = 0; i < 42; i++) {
24785             if(i % 7 == 0 && i != 0){
24786                 m[m.length] = "</tr><tr>";
24787             }
24788             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24789         }
24790         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24791             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24792
24793         var el = document.createElement("div");
24794         el.className = "x-date-picker";
24795         el.innerHTML = m.join("");
24796
24797         container.dom.insertBefore(el, position);
24798
24799         this.el = Roo.get(el);
24800         this.eventEl = Roo.get(el.firstChild);
24801
24802         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24803             handler: this.showPrevMonth,
24804             scope: this,
24805             preventDefault:true,
24806             stopDefault:true
24807         });
24808
24809         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24810             handler: this.showNextMonth,
24811             scope: this,
24812             preventDefault:true,
24813             stopDefault:true
24814         });
24815
24816         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24817
24818         this.monthPicker = this.el.down('div.x-date-mp');
24819         this.monthPicker.enableDisplayMode('block');
24820         
24821         var kn = new Roo.KeyNav(this.eventEl, {
24822             "left" : function(e){
24823                 e.ctrlKey ?
24824                     this.showPrevMonth() :
24825                     this.update(this.activeDate.add("d", -1));
24826             },
24827
24828             "right" : function(e){
24829                 e.ctrlKey ?
24830                     this.showNextMonth() :
24831                     this.update(this.activeDate.add("d", 1));
24832             },
24833
24834             "up" : function(e){
24835                 e.ctrlKey ?
24836                     this.showNextYear() :
24837                     this.update(this.activeDate.add("d", -7));
24838             },
24839
24840             "down" : function(e){
24841                 e.ctrlKey ?
24842                     this.showPrevYear() :
24843                     this.update(this.activeDate.add("d", 7));
24844             },
24845
24846             "pageUp" : function(e){
24847                 this.showNextMonth();
24848             },
24849
24850             "pageDown" : function(e){
24851                 this.showPrevMonth();
24852             },
24853
24854             "enter" : function(e){
24855                 e.stopPropagation();
24856                 return true;
24857             },
24858
24859             scope : this
24860         });
24861
24862         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24863
24864         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24865
24866         this.el.unselectable();
24867         
24868         this.cells = this.el.select("table.x-date-inner tbody td");
24869         this.textNodes = this.el.query("table.x-date-inner tbody span");
24870
24871         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24872             text: "&#160;",
24873             tooltip: this.monthYearText
24874         });
24875
24876         this.mbtn.on('click', this.showMonthPicker, this);
24877         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24878
24879
24880         var today = (new Date()).dateFormat(this.format);
24881         
24882         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24883         if (this.showClear) {
24884             baseTb.add( new Roo.Toolbar.Fill());
24885         }
24886         baseTb.add({
24887             text: String.format(this.todayText, today),
24888             tooltip: String.format(this.todayTip, today),
24889             handler: this.selectToday,
24890             scope: this
24891         });
24892         
24893         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24894             
24895         //});
24896         if (this.showClear) {
24897             
24898             baseTb.add( new Roo.Toolbar.Fill());
24899             baseTb.add({
24900                 text: '&#160;',
24901                 cls: 'x-btn-icon x-btn-clear',
24902                 handler: function() {
24903                     //this.value = '';
24904                     this.fireEvent("select", this, '');
24905                 },
24906                 scope: this
24907             });
24908         }
24909         
24910         
24911         if(Roo.isIE){
24912             this.el.repaint();
24913         }
24914         this.update(this.value);
24915     },
24916
24917     createMonthPicker : function(){
24918         if(!this.monthPicker.dom.firstChild){
24919             var buf = ['<table border="0" cellspacing="0">'];
24920             for(var i = 0; i < 6; i++){
24921                 buf.push(
24922                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24923                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24924                     i == 0 ?
24925                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24926                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24927                 );
24928             }
24929             buf.push(
24930                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24931                     this.okText,
24932                     '</button><button type="button" class="x-date-mp-cancel">',
24933                     this.cancelText,
24934                     '</button></td></tr>',
24935                 '</table>'
24936             );
24937             this.monthPicker.update(buf.join(''));
24938             this.monthPicker.on('click', this.onMonthClick, this);
24939             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24940
24941             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24942             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24943
24944             this.mpMonths.each(function(m, a, i){
24945                 i += 1;
24946                 if((i%2) == 0){
24947                     m.dom.xmonth = 5 + Math.round(i * .5);
24948                 }else{
24949                     m.dom.xmonth = Math.round((i-1) * .5);
24950                 }
24951             });
24952         }
24953     },
24954
24955     showMonthPicker : function(){
24956         this.createMonthPicker();
24957         var size = this.el.getSize();
24958         this.monthPicker.setSize(size);
24959         this.monthPicker.child('table').setSize(size);
24960
24961         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24962         this.updateMPMonth(this.mpSelMonth);
24963         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24964         this.updateMPYear(this.mpSelYear);
24965
24966         this.monthPicker.slideIn('t', {duration:.2});
24967     },
24968
24969     updateMPYear : function(y){
24970         this.mpyear = y;
24971         var ys = this.mpYears.elements;
24972         for(var i = 1; i <= 10; i++){
24973             var td = ys[i-1], y2;
24974             if((i%2) == 0){
24975                 y2 = y + Math.round(i * .5);
24976                 td.firstChild.innerHTML = y2;
24977                 td.xyear = y2;
24978             }else{
24979                 y2 = y - (5-Math.round(i * .5));
24980                 td.firstChild.innerHTML = y2;
24981                 td.xyear = y2;
24982             }
24983             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24984         }
24985     },
24986
24987     updateMPMonth : function(sm){
24988         this.mpMonths.each(function(m, a, i){
24989             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24990         });
24991     },
24992
24993     selectMPMonth: function(m){
24994         
24995     },
24996
24997     onMonthClick : function(e, t){
24998         e.stopEvent();
24999         var el = new Roo.Element(t), pn;
25000         if(el.is('button.x-date-mp-cancel')){
25001             this.hideMonthPicker();
25002         }
25003         else if(el.is('button.x-date-mp-ok')){
25004             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25005             this.hideMonthPicker();
25006         }
25007         else if(pn = el.up('td.x-date-mp-month', 2)){
25008             this.mpMonths.removeClass('x-date-mp-sel');
25009             pn.addClass('x-date-mp-sel');
25010             this.mpSelMonth = pn.dom.xmonth;
25011         }
25012         else if(pn = el.up('td.x-date-mp-year', 2)){
25013             this.mpYears.removeClass('x-date-mp-sel');
25014             pn.addClass('x-date-mp-sel');
25015             this.mpSelYear = pn.dom.xyear;
25016         }
25017         else if(el.is('a.x-date-mp-prev')){
25018             this.updateMPYear(this.mpyear-10);
25019         }
25020         else if(el.is('a.x-date-mp-next')){
25021             this.updateMPYear(this.mpyear+10);
25022         }
25023     },
25024
25025     onMonthDblClick : function(e, t){
25026         e.stopEvent();
25027         var el = new Roo.Element(t), pn;
25028         if(pn = el.up('td.x-date-mp-month', 2)){
25029             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25030             this.hideMonthPicker();
25031         }
25032         else if(pn = el.up('td.x-date-mp-year', 2)){
25033             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25034             this.hideMonthPicker();
25035         }
25036     },
25037
25038     hideMonthPicker : function(disableAnim){
25039         if(this.monthPicker){
25040             if(disableAnim === true){
25041                 this.monthPicker.hide();
25042             }else{
25043                 this.monthPicker.slideOut('t', {duration:.2});
25044             }
25045         }
25046     },
25047
25048     // private
25049     showPrevMonth : function(e){
25050         this.update(this.activeDate.add("mo", -1));
25051     },
25052
25053     // private
25054     showNextMonth : function(e){
25055         this.update(this.activeDate.add("mo", 1));
25056     },
25057
25058     // private
25059     showPrevYear : function(){
25060         this.update(this.activeDate.add("y", -1));
25061     },
25062
25063     // private
25064     showNextYear : function(){
25065         this.update(this.activeDate.add("y", 1));
25066     },
25067
25068     // private
25069     handleMouseWheel : function(e){
25070         var delta = e.getWheelDelta();
25071         if(delta > 0){
25072             this.showPrevMonth();
25073             e.stopEvent();
25074         } else if(delta < 0){
25075             this.showNextMonth();
25076             e.stopEvent();
25077         }
25078     },
25079
25080     // private
25081     handleDateClick : function(e, t){
25082         e.stopEvent();
25083         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25084             this.setValue(new Date(t.dateValue));
25085             this.fireEvent("select", this, this.value);
25086         }
25087     },
25088
25089     // private
25090     selectToday : function(){
25091         this.setValue(new Date().clearTime());
25092         this.fireEvent("select", this, this.value);
25093     },
25094
25095     // private
25096     update : function(date)
25097     {
25098         var vd = this.activeDate;
25099         this.activeDate = date;
25100         if(vd && this.el){
25101             var t = date.getTime();
25102             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25103                 this.cells.removeClass("x-date-selected");
25104                 this.cells.each(function(c){
25105                    if(c.dom.firstChild.dateValue == t){
25106                        c.addClass("x-date-selected");
25107                        setTimeout(function(){
25108                             try{c.dom.firstChild.focus();}catch(e){}
25109                        }, 50);
25110                        return false;
25111                    }
25112                 });
25113                 return;
25114             }
25115         }
25116         
25117         var days = date.getDaysInMonth();
25118         var firstOfMonth = date.getFirstDateOfMonth();
25119         var startingPos = firstOfMonth.getDay()-this.startDay;
25120
25121         if(startingPos <= this.startDay){
25122             startingPos += 7;
25123         }
25124
25125         var pm = date.add("mo", -1);
25126         var prevStart = pm.getDaysInMonth()-startingPos;
25127
25128         var cells = this.cells.elements;
25129         var textEls = this.textNodes;
25130         days += startingPos;
25131
25132         // convert everything to numbers so it's fast
25133         var day = 86400000;
25134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25135         var today = new Date().clearTime().getTime();
25136         var sel = date.clearTime().getTime();
25137         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25138         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25139         var ddMatch = this.disabledDatesRE;
25140         var ddText = this.disabledDatesText;
25141         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25142         var ddaysText = this.disabledDaysText;
25143         var format = this.format;
25144
25145         var setCellClass = function(cal, cell){
25146             cell.title = "";
25147             var t = d.getTime();
25148             cell.firstChild.dateValue = t;
25149             if(t == today){
25150                 cell.className += " x-date-today";
25151                 cell.title = cal.todayText;
25152             }
25153             if(t == sel){
25154                 cell.className += " x-date-selected";
25155                 setTimeout(function(){
25156                     try{cell.firstChild.focus();}catch(e){}
25157                 }, 50);
25158             }
25159             // disabling
25160             if(t < min) {
25161                 cell.className = " x-date-disabled";
25162                 cell.title = cal.minText;
25163                 return;
25164             }
25165             if(t > max) {
25166                 cell.className = " x-date-disabled";
25167                 cell.title = cal.maxText;
25168                 return;
25169             }
25170             if(ddays){
25171                 if(ddays.indexOf(d.getDay()) != -1){
25172                     cell.title = ddaysText;
25173                     cell.className = " x-date-disabled";
25174                 }
25175             }
25176             if(ddMatch && format){
25177                 var fvalue = d.dateFormat(format);
25178                 if(ddMatch.test(fvalue)){
25179                     cell.title = ddText.replace("%0", fvalue);
25180                     cell.className = " x-date-disabled";
25181                 }
25182             }
25183         };
25184
25185         var i = 0;
25186         for(; i < startingPos; i++) {
25187             textEls[i].innerHTML = (++prevStart);
25188             d.setDate(d.getDate()+1);
25189             cells[i].className = "x-date-prevday";
25190             setCellClass(this, cells[i]);
25191         }
25192         for(; i < days; i++){
25193             intDay = i - startingPos + 1;
25194             textEls[i].innerHTML = (intDay);
25195             d.setDate(d.getDate()+1);
25196             cells[i].className = "x-date-active";
25197             setCellClass(this, cells[i]);
25198         }
25199         var extraDays = 0;
25200         for(; i < 42; i++) {
25201              textEls[i].innerHTML = (++extraDays);
25202              d.setDate(d.getDate()+1);
25203              cells[i].className = "x-date-nextday";
25204              setCellClass(this, cells[i]);
25205         }
25206
25207         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25208         this.fireEvent('monthchange', this, date);
25209         
25210         if(!this.internalRender){
25211             var main = this.el.dom.firstChild;
25212             var w = main.offsetWidth;
25213             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25214             Roo.fly(main).setWidth(w);
25215             this.internalRender = true;
25216             // opera does not respect the auto grow header center column
25217             // then, after it gets a width opera refuses to recalculate
25218             // without a second pass
25219             if(Roo.isOpera && !this.secondPass){
25220                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25221                 this.secondPass = true;
25222                 this.update.defer(10, this, [date]);
25223             }
25224         }
25225         
25226         
25227     }
25228 });        /*
25229  * Based on:
25230  * Ext JS Library 1.1.1
25231  * Copyright(c) 2006-2007, Ext JS, LLC.
25232  *
25233  * Originally Released Under LGPL - original licence link has changed is not relivant.
25234  *
25235  * Fork - LGPL
25236  * <script type="text/javascript">
25237  */
25238 /**
25239  * @class Roo.TabPanel
25240  * @extends Roo.util.Observable
25241  * A lightweight tab container.
25242  * <br><br>
25243  * Usage:
25244  * <pre><code>
25245 // basic tabs 1, built from existing content
25246 var tabs = new Roo.TabPanel("tabs1");
25247 tabs.addTab("script", "View Script");
25248 tabs.addTab("markup", "View Markup");
25249 tabs.activate("script");
25250
25251 // more advanced tabs, built from javascript
25252 var jtabs = new Roo.TabPanel("jtabs");
25253 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25254
25255 // set up the UpdateManager
25256 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25257 var updater = tab2.getUpdateManager();
25258 updater.setDefaultUrl("ajax1.htm");
25259 tab2.on('activate', updater.refresh, updater, true);
25260
25261 // Use setUrl for Ajax loading
25262 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25263 tab3.setUrl("ajax2.htm", null, true);
25264
25265 // Disabled tab
25266 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25267 tab4.disable();
25268
25269 jtabs.activate("jtabs-1");
25270  * </code></pre>
25271  * @constructor
25272  * Create a new TabPanel.
25273  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25274  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25275  */
25276 Roo.TabPanel = function(container, config){
25277     /**
25278     * The container element for this TabPanel.
25279     * @type Roo.Element
25280     */
25281     this.el = Roo.get(container, true);
25282     if(config){
25283         if(typeof config == "boolean"){
25284             this.tabPosition = config ? "bottom" : "top";
25285         }else{
25286             Roo.apply(this, config);
25287         }
25288     }
25289     if(this.tabPosition == "bottom"){
25290         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25291         this.el.addClass("x-tabs-bottom");
25292     }
25293     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25294     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25295     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25296     if(Roo.isIE){
25297         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25298     }
25299     if(this.tabPosition != "bottom"){
25300         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25301          * @type Roo.Element
25302          */
25303         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25304         this.el.addClass("x-tabs-top");
25305     }
25306     this.items = [];
25307
25308     this.bodyEl.setStyle("position", "relative");
25309
25310     this.active = null;
25311     this.activateDelegate = this.activate.createDelegate(this);
25312
25313     this.addEvents({
25314         /**
25315          * @event tabchange
25316          * Fires when the active tab changes
25317          * @param {Roo.TabPanel} this
25318          * @param {Roo.TabPanelItem} activePanel The new active tab
25319          */
25320         "tabchange": true,
25321         /**
25322          * @event beforetabchange
25323          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25324          * @param {Roo.TabPanel} this
25325          * @param {Object} e Set cancel to true on this object to cancel the tab change
25326          * @param {Roo.TabPanelItem} tab The tab being changed to
25327          */
25328         "beforetabchange" : true
25329     });
25330
25331     Roo.EventManager.onWindowResize(this.onResize, this);
25332     this.cpad = this.el.getPadding("lr");
25333     this.hiddenCount = 0;
25334
25335
25336     // toolbar on the tabbar support...
25337     if (this.toolbar) {
25338         var tcfg = this.toolbar;
25339         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25340         this.toolbar = new Roo.Toolbar(tcfg);
25341         if (Roo.isSafari) {
25342             var tbl = tcfg.container.child('table', true);
25343             tbl.setAttribute('width', '100%');
25344         }
25345         
25346     }
25347    
25348
25349
25350     Roo.TabPanel.superclass.constructor.call(this);
25351 };
25352
25353 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25354     /*
25355      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25356      */
25357     tabPosition : "top",
25358     /*
25359      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25360      */
25361     currentTabWidth : 0,
25362     /*
25363      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25364      */
25365     minTabWidth : 40,
25366     /*
25367      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25368      */
25369     maxTabWidth : 250,
25370     /*
25371      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25372      */
25373     preferredTabWidth : 175,
25374     /*
25375      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25376      */
25377     resizeTabs : false,
25378     /*
25379      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25380      */
25381     monitorResize : true,
25382     /*
25383      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25384      */
25385     toolbar : false,
25386
25387     /**
25388      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25389      * @param {String} id The id of the div to use <b>or create</b>
25390      * @param {String} text The text for the tab
25391      * @param {String} content (optional) Content to put in the TabPanelItem body
25392      * @param {Boolean} closable (optional) True to create a close icon on the tab
25393      * @return {Roo.TabPanelItem} The created TabPanelItem
25394      */
25395     addTab : function(id, text, content, closable){
25396         var item = new Roo.TabPanelItem(this, id, text, closable);
25397         this.addTabItem(item);
25398         if(content){
25399             item.setContent(content);
25400         }
25401         return item;
25402     },
25403
25404     /**
25405      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25406      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25407      * @return {Roo.TabPanelItem}
25408      */
25409     getTab : function(id){
25410         return this.items[id];
25411     },
25412
25413     /**
25414      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25415      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25416      */
25417     hideTab : function(id){
25418         var t = this.items[id];
25419         if(!t.isHidden()){
25420            t.setHidden(true);
25421            this.hiddenCount++;
25422            this.autoSizeTabs();
25423         }
25424     },
25425
25426     /**
25427      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25428      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25429      */
25430     unhideTab : function(id){
25431         var t = this.items[id];
25432         if(t.isHidden()){
25433            t.setHidden(false);
25434            this.hiddenCount--;
25435            this.autoSizeTabs();
25436         }
25437     },
25438
25439     /**
25440      * Adds an existing {@link Roo.TabPanelItem}.
25441      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25442      */
25443     addTabItem : function(item){
25444         this.items[item.id] = item;
25445         this.items.push(item);
25446         if(this.resizeTabs){
25447            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25448            this.autoSizeTabs();
25449         }else{
25450             item.autoSize();
25451         }
25452     },
25453
25454     /**
25455      * Removes a {@link Roo.TabPanelItem}.
25456      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25457      */
25458     removeTab : function(id){
25459         var items = this.items;
25460         var tab = items[id];
25461         if(!tab) { return; }
25462         var index = items.indexOf(tab);
25463         if(this.active == tab && items.length > 1){
25464             var newTab = this.getNextAvailable(index);
25465             if(newTab) {
25466                 newTab.activate();
25467             }
25468         }
25469         this.stripEl.dom.removeChild(tab.pnode.dom);
25470         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25471             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25472         }
25473         items.splice(index, 1);
25474         delete this.items[tab.id];
25475         tab.fireEvent("close", tab);
25476         tab.purgeListeners();
25477         this.autoSizeTabs();
25478     },
25479
25480     getNextAvailable : function(start){
25481         var items = this.items;
25482         var index = start;
25483         // look for a next tab that will slide over to
25484         // replace the one being removed
25485         while(index < items.length){
25486             var item = items[++index];
25487             if(item && !item.isHidden()){
25488                 return item;
25489             }
25490         }
25491         // if one isn't found select the previous tab (on the left)
25492         index = start;
25493         while(index >= 0){
25494             var item = items[--index];
25495             if(item && !item.isHidden()){
25496                 return item;
25497             }
25498         }
25499         return null;
25500     },
25501
25502     /**
25503      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25504      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25505      */
25506     disableTab : function(id){
25507         var tab = this.items[id];
25508         if(tab && this.active != tab){
25509             tab.disable();
25510         }
25511     },
25512
25513     /**
25514      * Enables a {@link Roo.TabPanelItem} that is disabled.
25515      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25516      */
25517     enableTab : function(id){
25518         var tab = this.items[id];
25519         tab.enable();
25520     },
25521
25522     /**
25523      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25524      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25525      * @return {Roo.TabPanelItem} The TabPanelItem.
25526      */
25527     activate : function(id){
25528         var tab = this.items[id];
25529         if(!tab){
25530             return null;
25531         }
25532         if(tab == this.active || tab.disabled){
25533             return tab;
25534         }
25535         var e = {};
25536         this.fireEvent("beforetabchange", this, e, tab);
25537         if(e.cancel !== true && !tab.disabled){
25538             if(this.active){
25539                 this.active.hide();
25540             }
25541             this.active = this.items[id];
25542             this.active.show();
25543             this.fireEvent("tabchange", this, this.active);
25544         }
25545         return tab;
25546     },
25547
25548     /**
25549      * Gets the active {@link Roo.TabPanelItem}.
25550      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25551      */
25552     getActiveTab : function(){
25553         return this.active;
25554     },
25555
25556     /**
25557      * Updates the tab body element to fit the height of the container element
25558      * for overflow scrolling
25559      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25560      */
25561     syncHeight : function(targetHeight){
25562         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25563         var bm = this.bodyEl.getMargins();
25564         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25565         this.bodyEl.setHeight(newHeight);
25566         return newHeight;
25567     },
25568
25569     onResize : function(){
25570         if(this.monitorResize){
25571             this.autoSizeTabs();
25572         }
25573     },
25574
25575     /**
25576      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25577      */
25578     beginUpdate : function(){
25579         this.updating = true;
25580     },
25581
25582     /**
25583      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25584      */
25585     endUpdate : function(){
25586         this.updating = false;
25587         this.autoSizeTabs();
25588     },
25589
25590     /**
25591      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25592      */
25593     autoSizeTabs : function(){
25594         var count = this.items.length;
25595         var vcount = count - this.hiddenCount;
25596         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25597         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25598         var availWidth = Math.floor(w / vcount);
25599         var b = this.stripBody;
25600         if(b.getWidth() > w){
25601             var tabs = this.items;
25602             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25603             if(availWidth < this.minTabWidth){
25604                 /*if(!this.sleft){    // incomplete scrolling code
25605                     this.createScrollButtons();
25606                 }
25607                 this.showScroll();
25608                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25609             }
25610         }else{
25611             if(this.currentTabWidth < this.preferredTabWidth){
25612                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25613             }
25614         }
25615     },
25616
25617     /**
25618      * Returns the number of tabs in this TabPanel.
25619      * @return {Number}
25620      */
25621      getCount : function(){
25622          return this.items.length;
25623      },
25624
25625     /**
25626      * Resizes all the tabs to the passed width
25627      * @param {Number} The new width
25628      */
25629     setTabWidth : function(width){
25630         this.currentTabWidth = width;
25631         for(var i = 0, len = this.items.length; i < len; i++) {
25632                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25633         }
25634     },
25635
25636     /**
25637      * Destroys this TabPanel
25638      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25639      */
25640     destroy : function(removeEl){
25641         Roo.EventManager.removeResizeListener(this.onResize, this);
25642         for(var i = 0, len = this.items.length; i < len; i++){
25643             this.items[i].purgeListeners();
25644         }
25645         if(removeEl === true){
25646             this.el.update("");
25647             this.el.remove();
25648         }
25649     }
25650 });
25651
25652 /**
25653  * @class Roo.TabPanelItem
25654  * @extends Roo.util.Observable
25655  * Represents an individual item (tab plus body) in a TabPanel.
25656  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25657  * @param {String} id The id of this TabPanelItem
25658  * @param {String} text The text for the tab of this TabPanelItem
25659  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25660  */
25661 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25662     /**
25663      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25664      * @type Roo.TabPanel
25665      */
25666     this.tabPanel = tabPanel;
25667     /**
25668      * The id for this TabPanelItem
25669      * @type String
25670      */
25671     this.id = id;
25672     /** @private */
25673     this.disabled = false;
25674     /** @private */
25675     this.text = text;
25676     /** @private */
25677     this.loaded = false;
25678     this.closable = closable;
25679
25680     /**
25681      * The body element for this TabPanelItem.
25682      * @type Roo.Element
25683      */
25684     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25685     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25686     this.bodyEl.setStyle("display", "block");
25687     this.bodyEl.setStyle("zoom", "1");
25688     this.hideAction();
25689
25690     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25691     /** @private */
25692     this.el = Roo.get(els.el, true);
25693     this.inner = Roo.get(els.inner, true);
25694     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25695     this.pnode = Roo.get(els.el.parentNode, true);
25696     this.el.on("mousedown", this.onTabMouseDown, this);
25697     this.el.on("click", this.onTabClick, this);
25698     /** @private */
25699     if(closable){
25700         var c = Roo.get(els.close, true);
25701         c.dom.title = this.closeText;
25702         c.addClassOnOver("close-over");
25703         c.on("click", this.closeClick, this);
25704      }
25705
25706     this.addEvents({
25707          /**
25708          * @event activate
25709          * Fires when this tab becomes the active tab.
25710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25711          * @param {Roo.TabPanelItem} this
25712          */
25713         "activate": true,
25714         /**
25715          * @event beforeclose
25716          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25717          * @param {Roo.TabPanelItem} this
25718          * @param {Object} e Set cancel to true on this object to cancel the close.
25719          */
25720         "beforeclose": true,
25721         /**
25722          * @event close
25723          * Fires when this tab is closed.
25724          * @param {Roo.TabPanelItem} this
25725          */
25726          "close": true,
25727         /**
25728          * @event deactivate
25729          * Fires when this tab is no longer the active tab.
25730          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25731          * @param {Roo.TabPanelItem} this
25732          */
25733          "deactivate" : true
25734     });
25735     this.hidden = false;
25736
25737     Roo.TabPanelItem.superclass.constructor.call(this);
25738 };
25739
25740 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25741     purgeListeners : function(){
25742        Roo.util.Observable.prototype.purgeListeners.call(this);
25743        this.el.removeAllListeners();
25744     },
25745     /**
25746      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25747      */
25748     show : function(){
25749         this.pnode.addClass("on");
25750         this.showAction();
25751         if(Roo.isOpera){
25752             this.tabPanel.stripWrap.repaint();
25753         }
25754         this.fireEvent("activate", this.tabPanel, this);
25755     },
25756
25757     /**
25758      * Returns true if this tab is the active tab.
25759      * @return {Boolean}
25760      */
25761     isActive : function(){
25762         return this.tabPanel.getActiveTab() == this;
25763     },
25764
25765     /**
25766      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25767      */
25768     hide : function(){
25769         this.pnode.removeClass("on");
25770         this.hideAction();
25771         this.fireEvent("deactivate", this.tabPanel, this);
25772     },
25773
25774     hideAction : function(){
25775         this.bodyEl.hide();
25776         this.bodyEl.setStyle("position", "absolute");
25777         this.bodyEl.setLeft("-20000px");
25778         this.bodyEl.setTop("-20000px");
25779     },
25780
25781     showAction : function(){
25782         this.bodyEl.setStyle("position", "relative");
25783         this.bodyEl.setTop("");
25784         this.bodyEl.setLeft("");
25785         this.bodyEl.show();
25786     },
25787
25788     /**
25789      * Set the tooltip for the tab.
25790      * @param {String} tooltip The tab's tooltip
25791      */
25792     setTooltip : function(text){
25793         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25794             this.textEl.dom.qtip = text;
25795             this.textEl.dom.removeAttribute('title');
25796         }else{
25797             this.textEl.dom.title = text;
25798         }
25799     },
25800
25801     onTabClick : function(e){
25802         e.preventDefault();
25803         this.tabPanel.activate(this.id);
25804     },
25805
25806     onTabMouseDown : function(e){
25807         e.preventDefault();
25808         this.tabPanel.activate(this.id);
25809     },
25810
25811     getWidth : function(){
25812         return this.inner.getWidth();
25813     },
25814
25815     setWidth : function(width){
25816         var iwidth = width - this.pnode.getPadding("lr");
25817         this.inner.setWidth(iwidth);
25818         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25819         this.pnode.setWidth(width);
25820     },
25821
25822     /**
25823      * Show or hide the tab
25824      * @param {Boolean} hidden True to hide or false to show.
25825      */
25826     setHidden : function(hidden){
25827         this.hidden = hidden;
25828         this.pnode.setStyle("display", hidden ? "none" : "");
25829     },
25830
25831     /**
25832      * Returns true if this tab is "hidden"
25833      * @return {Boolean}
25834      */
25835     isHidden : function(){
25836         return this.hidden;
25837     },
25838
25839     /**
25840      * Returns the text for this tab
25841      * @return {String}
25842      */
25843     getText : function(){
25844         return this.text;
25845     },
25846
25847     autoSize : function(){
25848         //this.el.beginMeasure();
25849         this.textEl.setWidth(1);
25850         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25851         //this.el.endMeasure();
25852     },
25853
25854     /**
25855      * Sets the text for the tab (Note: this also sets the tooltip text)
25856      * @param {String} text The tab's text and tooltip
25857      */
25858     setText : function(text){
25859         this.text = text;
25860         this.textEl.update(text);
25861         this.setTooltip(text);
25862         if(!this.tabPanel.resizeTabs){
25863             this.autoSize();
25864         }
25865     },
25866     /**
25867      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25868      */
25869     activate : function(){
25870         this.tabPanel.activate(this.id);
25871     },
25872
25873     /**
25874      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25875      */
25876     disable : function(){
25877         if(this.tabPanel.active != this){
25878             this.disabled = true;
25879             this.pnode.addClass("disabled");
25880         }
25881     },
25882
25883     /**
25884      * Enables this TabPanelItem if it was previously disabled.
25885      */
25886     enable : function(){
25887         this.disabled = false;
25888         this.pnode.removeClass("disabled");
25889     },
25890
25891     /**
25892      * Sets the content for this TabPanelItem.
25893      * @param {String} content The content
25894      * @param {Boolean} loadScripts true to look for and load scripts
25895      */
25896     setContent : function(content, loadScripts){
25897         this.bodyEl.update(content, loadScripts);
25898     },
25899
25900     /**
25901      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25902      * @return {Roo.UpdateManager} The UpdateManager
25903      */
25904     getUpdateManager : function(){
25905         return this.bodyEl.getUpdateManager();
25906     },
25907
25908     /**
25909      * Set a URL to be used to load the content for this TabPanelItem.
25910      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25911      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25912      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25913      * @return {Roo.UpdateManager} The UpdateManager
25914      */
25915     setUrl : function(url, params, loadOnce){
25916         if(this.refreshDelegate){
25917             this.un('activate', this.refreshDelegate);
25918         }
25919         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25920         this.on("activate", this.refreshDelegate);
25921         return this.bodyEl.getUpdateManager();
25922     },
25923
25924     /** @private */
25925     _handleRefresh : function(url, params, loadOnce){
25926         if(!loadOnce || !this.loaded){
25927             var updater = this.bodyEl.getUpdateManager();
25928             updater.update(url, params, this._setLoaded.createDelegate(this));
25929         }
25930     },
25931
25932     /**
25933      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25934      *   Will fail silently if the setUrl method has not been called.
25935      *   This does not activate the panel, just updates its content.
25936      */
25937     refresh : function(){
25938         if(this.refreshDelegate){
25939            this.loaded = false;
25940            this.refreshDelegate();
25941         }
25942     },
25943
25944     /** @private */
25945     _setLoaded : function(){
25946         this.loaded = true;
25947     },
25948
25949     /** @private */
25950     closeClick : function(e){
25951         var o = {};
25952         e.stopEvent();
25953         this.fireEvent("beforeclose", this, o);
25954         if(o.cancel !== true){
25955             this.tabPanel.removeTab(this.id);
25956         }
25957     },
25958     /**
25959      * The text displayed in the tooltip for the close icon.
25960      * @type String
25961      */
25962     closeText : "Close this tab"
25963 });
25964
25965 /** @private */
25966 Roo.TabPanel.prototype.createStrip = function(container){
25967     var strip = document.createElement("div");
25968     strip.className = "x-tabs-wrap";
25969     container.appendChild(strip);
25970     return strip;
25971 };
25972 /** @private */
25973 Roo.TabPanel.prototype.createStripList = function(strip){
25974     // div wrapper for retard IE
25975     // returns the "tr" element.
25976     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25977         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25978         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25979     return strip.firstChild.firstChild.firstChild.firstChild;
25980 };
25981 /** @private */
25982 Roo.TabPanel.prototype.createBody = function(container){
25983     var body = document.createElement("div");
25984     Roo.id(body, "tab-body");
25985     Roo.fly(body).addClass("x-tabs-body");
25986     container.appendChild(body);
25987     return body;
25988 };
25989 /** @private */
25990 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25991     var body = Roo.getDom(id);
25992     if(!body){
25993         body = document.createElement("div");
25994         body.id = id;
25995     }
25996     Roo.fly(body).addClass("x-tabs-item-body");
25997     bodyEl.insertBefore(body, bodyEl.firstChild);
25998     return body;
25999 };
26000 /** @private */
26001 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26002     var td = document.createElement("td");
26003     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26004     //stripEl.appendChild(td);
26005     if(closable){
26006         td.className = "x-tabs-closable";
26007         if(!this.closeTpl){
26008             this.closeTpl = new Roo.Template(
26009                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26010                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26011                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26012             );
26013         }
26014         var el = this.closeTpl.overwrite(td, {"text": text});
26015         var close = el.getElementsByTagName("div")[0];
26016         var inner = el.getElementsByTagName("em")[0];
26017         return {"el": el, "close": close, "inner": inner};
26018     } else {
26019         if(!this.tabTpl){
26020             this.tabTpl = new Roo.Template(
26021                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26022                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26023             );
26024         }
26025         var el = this.tabTpl.overwrite(td, {"text": text});
26026         var inner = el.getElementsByTagName("em")[0];
26027         return {"el": el, "inner": inner};
26028     }
26029 };/*
26030  * Based on:
26031  * Ext JS Library 1.1.1
26032  * Copyright(c) 2006-2007, Ext JS, LLC.
26033  *
26034  * Originally Released Under LGPL - original licence link has changed is not relivant.
26035  *
26036  * Fork - LGPL
26037  * <script type="text/javascript">
26038  */
26039
26040 /**
26041  * @class Roo.Button
26042  * @extends Roo.util.Observable
26043  * Simple Button class
26044  * @cfg {String} text The button text
26045  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26046  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26047  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26048  * @cfg {Object} scope The scope of the handler
26049  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26050  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26051  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26052  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26053  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26054  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26055    applies if enableToggle = true)
26056  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26057  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26058   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26059  * @constructor
26060  * Create a new button
26061  * @param {Object} config The config object
26062  */
26063 Roo.Button = function(renderTo, config)
26064 {
26065     if (!config) {
26066         config = renderTo;
26067         renderTo = config.renderTo || false;
26068     }
26069     
26070     Roo.apply(this, config);
26071     this.addEvents({
26072         /**
26073              * @event click
26074              * Fires when this button is clicked
26075              * @param {Button} this
26076              * @param {EventObject} e The click event
26077              */
26078             "click" : true,
26079         /**
26080              * @event toggle
26081              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26082              * @param {Button} this
26083              * @param {Boolean} pressed
26084              */
26085             "toggle" : true,
26086         /**
26087              * @event mouseover
26088              * Fires when the mouse hovers over the button
26089              * @param {Button} this
26090              * @param {Event} e The event object
26091              */
26092         'mouseover' : true,
26093         /**
26094              * @event mouseout
26095              * Fires when the mouse exits the button
26096              * @param {Button} this
26097              * @param {Event} e The event object
26098              */
26099         'mouseout': true,
26100          /**
26101              * @event render
26102              * Fires when the button is rendered
26103              * @param {Button} this
26104              */
26105         'render': true
26106     });
26107     if(this.menu){
26108         this.menu = Roo.menu.MenuMgr.get(this.menu);
26109     }
26110     // register listeners first!!  - so render can be captured..
26111     Roo.util.Observable.call(this);
26112     if(renderTo){
26113         this.render(renderTo);
26114     }
26115     
26116   
26117 };
26118
26119 Roo.extend(Roo.Button, Roo.util.Observable, {
26120     /**
26121      * 
26122      */
26123     
26124     /**
26125      * Read-only. True if this button is hidden
26126      * @type Boolean
26127      */
26128     hidden : false,
26129     /**
26130      * Read-only. True if this button is disabled
26131      * @type Boolean
26132      */
26133     disabled : false,
26134     /**
26135      * Read-only. True if this button is pressed (only if enableToggle = true)
26136      * @type Boolean
26137      */
26138     pressed : false,
26139
26140     /**
26141      * @cfg {Number} tabIndex 
26142      * The DOM tabIndex for this button (defaults to undefined)
26143      */
26144     tabIndex : undefined,
26145
26146     /**
26147      * @cfg {Boolean} enableToggle
26148      * True to enable pressed/not pressed toggling (defaults to false)
26149      */
26150     enableToggle: false,
26151     /**
26152      * @cfg {Mixed} menu
26153      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26154      */
26155     menu : undefined,
26156     /**
26157      * @cfg {String} menuAlign
26158      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26159      */
26160     menuAlign : "tl-bl?",
26161
26162     /**
26163      * @cfg {String} iconCls
26164      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26165      */
26166     iconCls : undefined,
26167     /**
26168      * @cfg {String} type
26169      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26170      */
26171     type : 'button',
26172
26173     // private
26174     menuClassTarget: 'tr',
26175
26176     /**
26177      * @cfg {String} clickEvent
26178      * The type of event to map to the button's event handler (defaults to 'click')
26179      */
26180     clickEvent : 'click',
26181
26182     /**
26183      * @cfg {Boolean} handleMouseEvents
26184      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26185      */
26186     handleMouseEvents : true,
26187
26188     /**
26189      * @cfg {String} tooltipType
26190      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26191      */
26192     tooltipType : 'qtip',
26193
26194     /**
26195      * @cfg {String} cls
26196      * A CSS class to apply to the button's main element.
26197      */
26198     
26199     /**
26200      * @cfg {Roo.Template} template (Optional)
26201      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26202      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26203      * require code modifications if required elements (e.g. a button) aren't present.
26204      */
26205
26206     // private
26207     render : function(renderTo){
26208         var btn;
26209         if(this.hideParent){
26210             this.parentEl = Roo.get(renderTo);
26211         }
26212         if(!this.dhconfig){
26213             if(!this.template){
26214                 if(!Roo.Button.buttonTemplate){
26215                     // hideous table template
26216                     Roo.Button.buttonTemplate = new Roo.Template(
26217                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26218                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26219                         "</tr></tbody></table>");
26220                 }
26221                 this.template = Roo.Button.buttonTemplate;
26222             }
26223             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26224             var btnEl = btn.child("button:first");
26225             btnEl.on('focus', this.onFocus, this);
26226             btnEl.on('blur', this.onBlur, this);
26227             if(this.cls){
26228                 btn.addClass(this.cls);
26229             }
26230             if(this.icon){
26231                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26232             }
26233             if(this.iconCls){
26234                 btnEl.addClass(this.iconCls);
26235                 if(!this.cls){
26236                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26237                 }
26238             }
26239             if(this.tabIndex !== undefined){
26240                 btnEl.dom.tabIndex = this.tabIndex;
26241             }
26242             if(this.tooltip){
26243                 if(typeof this.tooltip == 'object'){
26244                     Roo.QuickTips.tips(Roo.apply({
26245                           target: btnEl.id
26246                     }, this.tooltip));
26247                 } else {
26248                     btnEl.dom[this.tooltipType] = this.tooltip;
26249                 }
26250             }
26251         }else{
26252             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26253         }
26254         this.el = btn;
26255         if(this.id){
26256             this.el.dom.id = this.el.id = this.id;
26257         }
26258         if(this.menu){
26259             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26260             this.menu.on("show", this.onMenuShow, this);
26261             this.menu.on("hide", this.onMenuHide, this);
26262         }
26263         btn.addClass("x-btn");
26264         if(Roo.isIE && !Roo.isIE7){
26265             this.autoWidth.defer(1, this);
26266         }else{
26267             this.autoWidth();
26268         }
26269         if(this.handleMouseEvents){
26270             btn.on("mouseover", this.onMouseOver, this);
26271             btn.on("mouseout", this.onMouseOut, this);
26272             btn.on("mousedown", this.onMouseDown, this);
26273         }
26274         btn.on(this.clickEvent, this.onClick, this);
26275         //btn.on("mouseup", this.onMouseUp, this);
26276         if(this.hidden){
26277             this.hide();
26278         }
26279         if(this.disabled){
26280             this.disable();
26281         }
26282         Roo.ButtonToggleMgr.register(this);
26283         if(this.pressed){
26284             this.el.addClass("x-btn-pressed");
26285         }
26286         if(this.repeat){
26287             var repeater = new Roo.util.ClickRepeater(btn,
26288                 typeof this.repeat == "object" ? this.repeat : {}
26289             );
26290             repeater.on("click", this.onClick,  this);
26291         }
26292         
26293         this.fireEvent('render', this);
26294         
26295     },
26296     /**
26297      * Returns the button's underlying element
26298      * @return {Roo.Element} The element
26299      */
26300     getEl : function(){
26301         return this.el;  
26302     },
26303     
26304     /**
26305      * Destroys this Button and removes any listeners.
26306      */
26307     destroy : function(){
26308         Roo.ButtonToggleMgr.unregister(this);
26309         this.el.removeAllListeners();
26310         this.purgeListeners();
26311         this.el.remove();
26312     },
26313
26314     // private
26315     autoWidth : function(){
26316         if(this.el){
26317             this.el.setWidth("auto");
26318             if(Roo.isIE7 && Roo.isStrict){
26319                 var ib = this.el.child('button');
26320                 if(ib && ib.getWidth() > 20){
26321                     ib.clip();
26322                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26323                 }
26324             }
26325             if(this.minWidth){
26326                 if(this.hidden){
26327                     this.el.beginMeasure();
26328                 }
26329                 if(this.el.getWidth() < this.minWidth){
26330                     this.el.setWidth(this.minWidth);
26331                 }
26332                 if(this.hidden){
26333                     this.el.endMeasure();
26334                 }
26335             }
26336         }
26337     },
26338
26339     /**
26340      * Assigns this button's click handler
26341      * @param {Function} handler The function to call when the button is clicked
26342      * @param {Object} scope (optional) Scope for the function passed in
26343      */
26344     setHandler : function(handler, scope){
26345         this.handler = handler;
26346         this.scope = scope;  
26347     },
26348     
26349     /**
26350      * Sets this button's text
26351      * @param {String} text The button text
26352      */
26353     setText : function(text){
26354         this.text = text;
26355         if(this.el){
26356             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26357         }
26358         this.autoWidth();
26359     },
26360     
26361     /**
26362      * Gets the text for this button
26363      * @return {String} The button text
26364      */
26365     getText : function(){
26366         return this.text;  
26367     },
26368     
26369     /**
26370      * Show this button
26371      */
26372     show: function(){
26373         this.hidden = false;
26374         if(this.el){
26375             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26376         }
26377     },
26378     
26379     /**
26380      * Hide this button
26381      */
26382     hide: function(){
26383         this.hidden = true;
26384         if(this.el){
26385             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26386         }
26387     },
26388     
26389     /**
26390      * Convenience function for boolean show/hide
26391      * @param {Boolean} visible True to show, false to hide
26392      */
26393     setVisible: function(visible){
26394         if(visible) {
26395             this.show();
26396         }else{
26397             this.hide();
26398         }
26399     },
26400     
26401     /**
26402      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26403      * @param {Boolean} state (optional) Force a particular state
26404      */
26405     toggle : function(state){
26406         state = state === undefined ? !this.pressed : state;
26407         if(state != this.pressed){
26408             if(state){
26409                 this.el.addClass("x-btn-pressed");
26410                 this.pressed = true;
26411                 this.fireEvent("toggle", this, true);
26412             }else{
26413                 this.el.removeClass("x-btn-pressed");
26414                 this.pressed = false;
26415                 this.fireEvent("toggle", this, false);
26416             }
26417             if(this.toggleHandler){
26418                 this.toggleHandler.call(this.scope || this, this, state);
26419             }
26420         }
26421     },
26422     
26423     /**
26424      * Focus the button
26425      */
26426     focus : function(){
26427         this.el.child('button:first').focus();
26428     },
26429     
26430     /**
26431      * Disable this button
26432      */
26433     disable : function(){
26434         if(this.el){
26435             this.el.addClass("x-btn-disabled");
26436         }
26437         this.disabled = true;
26438     },
26439     
26440     /**
26441      * Enable this button
26442      */
26443     enable : function(){
26444         if(this.el){
26445             this.el.removeClass("x-btn-disabled");
26446         }
26447         this.disabled = false;
26448     },
26449
26450     /**
26451      * Convenience function for boolean enable/disable
26452      * @param {Boolean} enabled True to enable, false to disable
26453      */
26454     setDisabled : function(v){
26455         this[v !== true ? "enable" : "disable"]();
26456     },
26457
26458     // private
26459     onClick : function(e){
26460         if(e){
26461             e.preventDefault();
26462         }
26463         if(e.button != 0){
26464             return;
26465         }
26466         if(!this.disabled){
26467             if(this.enableToggle){
26468                 this.toggle();
26469             }
26470             if(this.menu && !this.menu.isVisible()){
26471                 this.menu.show(this.el, this.menuAlign);
26472             }
26473             this.fireEvent("click", this, e);
26474             if(this.handler){
26475                 this.el.removeClass("x-btn-over");
26476                 this.handler.call(this.scope || this, this, e);
26477             }
26478         }
26479     },
26480     // private
26481     onMouseOver : function(e){
26482         if(!this.disabled){
26483             this.el.addClass("x-btn-over");
26484             this.fireEvent('mouseover', this, e);
26485         }
26486     },
26487     // private
26488     onMouseOut : function(e){
26489         if(!e.within(this.el,  true)){
26490             this.el.removeClass("x-btn-over");
26491             this.fireEvent('mouseout', this, e);
26492         }
26493     },
26494     // private
26495     onFocus : function(e){
26496         if(!this.disabled){
26497             this.el.addClass("x-btn-focus");
26498         }
26499     },
26500     // private
26501     onBlur : function(e){
26502         this.el.removeClass("x-btn-focus");
26503     },
26504     // private
26505     onMouseDown : function(e){
26506         if(!this.disabled && e.button == 0){
26507             this.el.addClass("x-btn-click");
26508             Roo.get(document).on('mouseup', this.onMouseUp, this);
26509         }
26510     },
26511     // private
26512     onMouseUp : function(e){
26513         if(e.button == 0){
26514             this.el.removeClass("x-btn-click");
26515             Roo.get(document).un('mouseup', this.onMouseUp, this);
26516         }
26517     },
26518     // private
26519     onMenuShow : function(e){
26520         this.el.addClass("x-btn-menu-active");
26521     },
26522     // private
26523     onMenuHide : function(e){
26524         this.el.removeClass("x-btn-menu-active");
26525     }   
26526 });
26527
26528 // Private utility class used by Button
26529 Roo.ButtonToggleMgr = function(){
26530    var groups = {};
26531    
26532    function toggleGroup(btn, state){
26533        if(state){
26534            var g = groups[btn.toggleGroup];
26535            for(var i = 0, l = g.length; i < l; i++){
26536                if(g[i] != btn){
26537                    g[i].toggle(false);
26538                }
26539            }
26540        }
26541    }
26542    
26543    return {
26544        register : function(btn){
26545            if(!btn.toggleGroup){
26546                return;
26547            }
26548            var g = groups[btn.toggleGroup];
26549            if(!g){
26550                g = groups[btn.toggleGroup] = [];
26551            }
26552            g.push(btn);
26553            btn.on("toggle", toggleGroup);
26554        },
26555        
26556        unregister : function(btn){
26557            if(!btn.toggleGroup){
26558                return;
26559            }
26560            var g = groups[btn.toggleGroup];
26561            if(g){
26562                g.remove(btn);
26563                btn.un("toggle", toggleGroup);
26564            }
26565        }
26566    };
26567 }();/*
26568  * Based on:
26569  * Ext JS Library 1.1.1
26570  * Copyright(c) 2006-2007, Ext JS, LLC.
26571  *
26572  * Originally Released Under LGPL - original licence link has changed is not relivant.
26573  *
26574  * Fork - LGPL
26575  * <script type="text/javascript">
26576  */
26577  
26578 /**
26579  * @class Roo.SplitButton
26580  * @extends Roo.Button
26581  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26582  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26583  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26584  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26585  * @cfg {String} arrowTooltip The title attribute of the arrow
26586  * @constructor
26587  * Create a new menu button
26588  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26589  * @param {Object} config The config object
26590  */
26591 Roo.SplitButton = function(renderTo, config){
26592     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26593     /**
26594      * @event arrowclick
26595      * Fires when this button's arrow is clicked
26596      * @param {SplitButton} this
26597      * @param {EventObject} e The click event
26598      */
26599     this.addEvents({"arrowclick":true});
26600 };
26601
26602 Roo.extend(Roo.SplitButton, Roo.Button, {
26603     render : function(renderTo){
26604         // this is one sweet looking template!
26605         var tpl = new Roo.Template(
26606             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26607             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26608             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26609             "</tbody></table></td><td>",
26610             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26611             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26612             "</tbody></table></td></tr></table>"
26613         );
26614         var btn = tpl.append(renderTo, [this.text, this.type], true);
26615         var btnEl = btn.child("button");
26616         if(this.cls){
26617             btn.addClass(this.cls);
26618         }
26619         if(this.icon){
26620             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26621         }
26622         if(this.iconCls){
26623             btnEl.addClass(this.iconCls);
26624             if(!this.cls){
26625                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26626             }
26627         }
26628         this.el = btn;
26629         if(this.handleMouseEvents){
26630             btn.on("mouseover", this.onMouseOver, this);
26631             btn.on("mouseout", this.onMouseOut, this);
26632             btn.on("mousedown", this.onMouseDown, this);
26633             btn.on("mouseup", this.onMouseUp, this);
26634         }
26635         btn.on(this.clickEvent, this.onClick, this);
26636         if(this.tooltip){
26637             if(typeof this.tooltip == 'object'){
26638                 Roo.QuickTips.tips(Roo.apply({
26639                       target: btnEl.id
26640                 }, this.tooltip));
26641             } else {
26642                 btnEl.dom[this.tooltipType] = this.tooltip;
26643             }
26644         }
26645         if(this.arrowTooltip){
26646             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26647         }
26648         if(this.hidden){
26649             this.hide();
26650         }
26651         if(this.disabled){
26652             this.disable();
26653         }
26654         if(this.pressed){
26655             this.el.addClass("x-btn-pressed");
26656         }
26657         if(Roo.isIE && !Roo.isIE7){
26658             this.autoWidth.defer(1, this);
26659         }else{
26660             this.autoWidth();
26661         }
26662         if(this.menu){
26663             this.menu.on("show", this.onMenuShow, this);
26664             this.menu.on("hide", this.onMenuHide, this);
26665         }
26666         this.fireEvent('render', this);
26667     },
26668
26669     // private
26670     autoWidth : function(){
26671         if(this.el){
26672             var tbl = this.el.child("table:first");
26673             var tbl2 = this.el.child("table:last");
26674             this.el.setWidth("auto");
26675             tbl.setWidth("auto");
26676             if(Roo.isIE7 && Roo.isStrict){
26677                 var ib = this.el.child('button:first');
26678                 if(ib && ib.getWidth() > 20){
26679                     ib.clip();
26680                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26681                 }
26682             }
26683             if(this.minWidth){
26684                 if(this.hidden){
26685                     this.el.beginMeasure();
26686                 }
26687                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26688                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26689                 }
26690                 if(this.hidden){
26691                     this.el.endMeasure();
26692                 }
26693             }
26694             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26695         } 
26696     },
26697     /**
26698      * Sets this button's click handler
26699      * @param {Function} handler The function to call when the button is clicked
26700      * @param {Object} scope (optional) Scope for the function passed above
26701      */
26702     setHandler : function(handler, scope){
26703         this.handler = handler;
26704         this.scope = scope;  
26705     },
26706     
26707     /**
26708      * Sets this button's arrow click handler
26709      * @param {Function} handler The function to call when the arrow is clicked
26710      * @param {Object} scope (optional) Scope for the function passed above
26711      */
26712     setArrowHandler : function(handler, scope){
26713         this.arrowHandler = handler;
26714         this.scope = scope;  
26715     },
26716     
26717     /**
26718      * Focus the button
26719      */
26720     focus : function(){
26721         if(this.el){
26722             this.el.child("button:first").focus();
26723         }
26724     },
26725
26726     // private
26727     onClick : function(e){
26728         e.preventDefault();
26729         if(!this.disabled){
26730             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26731                 if(this.menu && !this.menu.isVisible()){
26732                     this.menu.show(this.el, this.menuAlign);
26733                 }
26734                 this.fireEvent("arrowclick", this, e);
26735                 if(this.arrowHandler){
26736                     this.arrowHandler.call(this.scope || this, this, e);
26737                 }
26738             }else{
26739                 this.fireEvent("click", this, e);
26740                 if(this.handler){
26741                     this.handler.call(this.scope || this, this, e);
26742                 }
26743             }
26744         }
26745     },
26746     // private
26747     onMouseDown : function(e){
26748         if(!this.disabled){
26749             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26750         }
26751     },
26752     // private
26753     onMouseUp : function(e){
26754         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26755     }   
26756 });
26757
26758
26759 // backwards compat
26760 Roo.MenuButton = Roo.SplitButton;/*
26761  * Based on:
26762  * Ext JS Library 1.1.1
26763  * Copyright(c) 2006-2007, Ext JS, LLC.
26764  *
26765  * Originally Released Under LGPL - original licence link has changed is not relivant.
26766  *
26767  * Fork - LGPL
26768  * <script type="text/javascript">
26769  */
26770
26771 /**
26772  * @class Roo.Toolbar
26773  * Basic Toolbar class.
26774  * @constructor
26775  * Creates a new Toolbar
26776  * @param {Object} container The config object
26777  */ 
26778 Roo.Toolbar = function(container, buttons, config)
26779 {
26780     /// old consturctor format still supported..
26781     if(container instanceof Array){ // omit the container for later rendering
26782         buttons = container;
26783         config = buttons;
26784         container = null;
26785     }
26786     if (typeof(container) == 'object' && container.xtype) {
26787         config = container;
26788         container = config.container;
26789         buttons = config.buttons || []; // not really - use items!!
26790     }
26791     var xitems = [];
26792     if (config && config.items) {
26793         xitems = config.items;
26794         delete config.items;
26795     }
26796     Roo.apply(this, config);
26797     this.buttons = buttons;
26798     
26799     if(container){
26800         this.render(container);
26801     }
26802     this.xitems = xitems;
26803     Roo.each(xitems, function(b) {
26804         this.add(b);
26805     }, this);
26806     
26807 };
26808
26809 Roo.Toolbar.prototype = {
26810     /**
26811      * @cfg {Array} items
26812      * array of button configs or elements to add (will be converted to a MixedCollection)
26813      */
26814     
26815     /**
26816      * @cfg {String/HTMLElement/Element} container
26817      * The id or element that will contain the toolbar
26818      */
26819     // private
26820     render : function(ct){
26821         this.el = Roo.get(ct);
26822         if(this.cls){
26823             this.el.addClass(this.cls);
26824         }
26825         // using a table allows for vertical alignment
26826         // 100% width is needed by Safari...
26827         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26828         this.tr = this.el.child("tr", true);
26829         var autoId = 0;
26830         this.items = new Roo.util.MixedCollection(false, function(o){
26831             return o.id || ("item" + (++autoId));
26832         });
26833         if(this.buttons){
26834             this.add.apply(this, this.buttons);
26835             delete this.buttons;
26836         }
26837     },
26838
26839     /**
26840      * Adds element(s) to the toolbar -- this function takes a variable number of 
26841      * arguments of mixed type and adds them to the toolbar.
26842      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26843      * <ul>
26844      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26845      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26846      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26847      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26848      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26849      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26850      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26851      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26852      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26853      * </ul>
26854      * @param {Mixed} arg2
26855      * @param {Mixed} etc.
26856      */
26857     add : function(){
26858         var a = arguments, l = a.length;
26859         for(var i = 0; i < l; i++){
26860             this._add(a[i]);
26861         }
26862     },
26863     // private..
26864     _add : function(el) {
26865         
26866         if (el.xtype) {
26867             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26868         }
26869         
26870         if (el.applyTo){ // some kind of form field
26871             return this.addField(el);
26872         } 
26873         if (el.render){ // some kind of Toolbar.Item
26874             return this.addItem(el);
26875         }
26876         if (typeof el == "string"){ // string
26877             if(el == "separator" || el == "-"){
26878                 return this.addSeparator();
26879             }
26880             if (el == " "){
26881                 return this.addSpacer();
26882             }
26883             if(el == "->"){
26884                 return this.addFill();
26885             }
26886             return this.addText(el);
26887             
26888         }
26889         if(el.tagName){ // element
26890             return this.addElement(el);
26891         }
26892         if(typeof el == "object"){ // must be button config?
26893             return this.addButton(el);
26894         }
26895         // and now what?!?!
26896         return false;
26897         
26898     },
26899     
26900     /**
26901      * Add an Xtype element
26902      * @param {Object} xtype Xtype Object
26903      * @return {Object} created Object
26904      */
26905     addxtype : function(e){
26906         return this.add(e);  
26907     },
26908     
26909     /**
26910      * Returns the Element for this toolbar.
26911      * @return {Roo.Element}
26912      */
26913     getEl : function(){
26914         return this.el;  
26915     },
26916     
26917     /**
26918      * Adds a separator
26919      * @return {Roo.Toolbar.Item} The separator item
26920      */
26921     addSeparator : function(){
26922         return this.addItem(new Roo.Toolbar.Separator());
26923     },
26924
26925     /**
26926      * Adds a spacer element
26927      * @return {Roo.Toolbar.Spacer} The spacer item
26928      */
26929     addSpacer : function(){
26930         return this.addItem(new Roo.Toolbar.Spacer());
26931     },
26932
26933     /**
26934      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26935      * @return {Roo.Toolbar.Fill} The fill item
26936      */
26937     addFill : function(){
26938         return this.addItem(new Roo.Toolbar.Fill());
26939     },
26940
26941     /**
26942      * Adds any standard HTML element to the toolbar
26943      * @param {String/HTMLElement/Element} el The element or id of the element to add
26944      * @return {Roo.Toolbar.Item} The element's item
26945      */
26946     addElement : function(el){
26947         return this.addItem(new Roo.Toolbar.Item(el));
26948     },
26949     /**
26950      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26951      * @type Roo.util.MixedCollection  
26952      */
26953     items : false,
26954      
26955     /**
26956      * Adds any Toolbar.Item or subclass
26957      * @param {Roo.Toolbar.Item} item
26958      * @return {Roo.Toolbar.Item} The item
26959      */
26960     addItem : function(item){
26961         var td = this.nextBlock();
26962         item.render(td);
26963         this.items.add(item);
26964         return item;
26965     },
26966     
26967     /**
26968      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26969      * @param {Object/Array} config A button config or array of configs
26970      * @return {Roo.Toolbar.Button/Array}
26971      */
26972     addButton : function(config){
26973         if(config instanceof Array){
26974             var buttons = [];
26975             for(var i = 0, len = config.length; i < len; i++) {
26976                 buttons.push(this.addButton(config[i]));
26977             }
26978             return buttons;
26979         }
26980         var b = config;
26981         if(!(config instanceof Roo.Toolbar.Button)){
26982             b = config.split ?
26983                 new Roo.Toolbar.SplitButton(config) :
26984                 new Roo.Toolbar.Button(config);
26985         }
26986         var td = this.nextBlock();
26987         b.render(td);
26988         this.items.add(b);
26989         return b;
26990     },
26991     
26992     /**
26993      * Adds text to the toolbar
26994      * @param {String} text The text to add
26995      * @return {Roo.Toolbar.Item} The element's item
26996      */
26997     addText : function(text){
26998         return this.addItem(new Roo.Toolbar.TextItem(text));
26999     },
27000     
27001     /**
27002      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27003      * @param {Number} index The index where the item is to be inserted
27004      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27005      * @return {Roo.Toolbar.Button/Item}
27006      */
27007     insertButton : function(index, item){
27008         if(item instanceof Array){
27009             var buttons = [];
27010             for(var i = 0, len = item.length; i < len; i++) {
27011                buttons.push(this.insertButton(index + i, item[i]));
27012             }
27013             return buttons;
27014         }
27015         if (!(item instanceof Roo.Toolbar.Button)){
27016            item = new Roo.Toolbar.Button(item);
27017         }
27018         var td = document.createElement("td");
27019         this.tr.insertBefore(td, this.tr.childNodes[index]);
27020         item.render(td);
27021         this.items.insert(index, item);
27022         return item;
27023     },
27024     
27025     /**
27026      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27027      * @param {Object} config
27028      * @return {Roo.Toolbar.Item} The element's item
27029      */
27030     addDom : function(config, returnEl){
27031         var td = this.nextBlock();
27032         Roo.DomHelper.overwrite(td, config);
27033         var ti = new Roo.Toolbar.Item(td.firstChild);
27034         ti.render(td);
27035         this.items.add(ti);
27036         return ti;
27037     },
27038
27039     /**
27040      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27041      * @type Roo.util.MixedCollection  
27042      */
27043     fields : false,
27044     
27045     /**
27046      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27047      * Note: the field should not have been rendered yet. For a field that has already been
27048      * rendered, use {@link #addElement}.
27049      * @param {Roo.form.Field} field
27050      * @return {Roo.ToolbarItem}
27051      */
27052      
27053       
27054     addField : function(field) {
27055         if (!this.fields) {
27056             var autoId = 0;
27057             this.fields = new Roo.util.MixedCollection(false, function(o){
27058                 return o.id || ("item" + (++autoId));
27059             });
27060
27061         }
27062         
27063         var td = this.nextBlock();
27064         field.render(td);
27065         var ti = new Roo.Toolbar.Item(td.firstChild);
27066         ti.render(td);
27067         this.items.add(ti);
27068         this.fields.add(field);
27069         return ti;
27070     },
27071     /**
27072      * Hide the toolbar
27073      * @method hide
27074      */
27075      
27076       
27077     hide : function()
27078     {
27079         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27080         this.el.child('div').hide();
27081     },
27082     /**
27083      * Show the toolbar
27084      * @method show
27085      */
27086     show : function()
27087     {
27088         this.el.child('div').show();
27089     },
27090       
27091     // private
27092     nextBlock : function(){
27093         var td = document.createElement("td");
27094         this.tr.appendChild(td);
27095         return td;
27096     },
27097
27098     // private
27099     destroy : function(){
27100         if(this.items){ // rendered?
27101             Roo.destroy.apply(Roo, this.items.items);
27102         }
27103         if(this.fields){ // rendered?
27104             Roo.destroy.apply(Roo, this.fields.items);
27105         }
27106         Roo.Element.uncache(this.el, this.tr);
27107     }
27108 };
27109
27110 /**
27111  * @class Roo.Toolbar.Item
27112  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27113  * @constructor
27114  * Creates a new Item
27115  * @param {HTMLElement} el 
27116  */
27117 Roo.Toolbar.Item = function(el){
27118     this.el = Roo.getDom(el);
27119     this.id = Roo.id(this.el);
27120     this.hidden = false;
27121 };
27122
27123 Roo.Toolbar.Item.prototype = {
27124     
27125     /**
27126      * Get this item's HTML Element
27127      * @return {HTMLElement}
27128      */
27129     getEl : function(){
27130        return this.el;  
27131     },
27132
27133     // private
27134     render : function(td){
27135         this.td = td;
27136         td.appendChild(this.el);
27137     },
27138     
27139     /**
27140      * Removes and destroys this item.
27141      */
27142     destroy : function(){
27143         this.td.parentNode.removeChild(this.td);
27144     },
27145     
27146     /**
27147      * Shows this item.
27148      */
27149     show: function(){
27150         this.hidden = false;
27151         this.td.style.display = "";
27152     },
27153     
27154     /**
27155      * Hides this item.
27156      */
27157     hide: function(){
27158         this.hidden = true;
27159         this.td.style.display = "none";
27160     },
27161     
27162     /**
27163      * Convenience function for boolean show/hide.
27164      * @param {Boolean} visible true to show/false to hide
27165      */
27166     setVisible: function(visible){
27167         if(visible) {
27168             this.show();
27169         }else{
27170             this.hide();
27171         }
27172     },
27173     
27174     /**
27175      * Try to focus this item.
27176      */
27177     focus : function(){
27178         Roo.fly(this.el).focus();
27179     },
27180     
27181     /**
27182      * Disables this item.
27183      */
27184     disable : function(){
27185         Roo.fly(this.td).addClass("x-item-disabled");
27186         this.disabled = true;
27187         this.el.disabled = true;
27188     },
27189     
27190     /**
27191      * Enables this item.
27192      */
27193     enable : function(){
27194         Roo.fly(this.td).removeClass("x-item-disabled");
27195         this.disabled = false;
27196         this.el.disabled = false;
27197     }
27198 };
27199
27200
27201 /**
27202  * @class Roo.Toolbar.Separator
27203  * @extends Roo.Toolbar.Item
27204  * A simple toolbar separator class
27205  * @constructor
27206  * Creates a new Separator
27207  */
27208 Roo.Toolbar.Separator = function(){
27209     var s = document.createElement("span");
27210     s.className = "ytb-sep";
27211     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27212 };
27213 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27214     enable:Roo.emptyFn,
27215     disable:Roo.emptyFn,
27216     focus:Roo.emptyFn
27217 });
27218
27219 /**
27220  * @class Roo.Toolbar.Spacer
27221  * @extends Roo.Toolbar.Item
27222  * A simple element that adds extra horizontal space to a toolbar.
27223  * @constructor
27224  * Creates a new Spacer
27225  */
27226 Roo.Toolbar.Spacer = function(){
27227     var s = document.createElement("div");
27228     s.className = "ytb-spacer";
27229     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27230 };
27231 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27232     enable:Roo.emptyFn,
27233     disable:Roo.emptyFn,
27234     focus:Roo.emptyFn
27235 });
27236
27237 /**
27238  * @class Roo.Toolbar.Fill
27239  * @extends Roo.Toolbar.Spacer
27240  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27241  * @constructor
27242  * Creates a new Spacer
27243  */
27244 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27245     // private
27246     render : function(td){
27247         td.style.width = '100%';
27248         Roo.Toolbar.Fill.superclass.render.call(this, td);
27249     }
27250 });
27251
27252 /**
27253  * @class Roo.Toolbar.TextItem
27254  * @extends Roo.Toolbar.Item
27255  * A simple class that renders text directly into a toolbar.
27256  * @constructor
27257  * Creates a new TextItem
27258  * @param {String} text
27259  */
27260 Roo.Toolbar.TextItem = function(text){
27261     if (typeof(text) == 'object') {
27262         text = text.text;
27263     }
27264     var s = document.createElement("span");
27265     s.className = "ytb-text";
27266     s.innerHTML = text;
27267     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27268 };
27269 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27270     enable:Roo.emptyFn,
27271     disable:Roo.emptyFn,
27272     focus:Roo.emptyFn
27273 });
27274
27275 /**
27276  * @class Roo.Toolbar.Button
27277  * @extends Roo.Button
27278  * A button that renders into a toolbar.
27279  * @constructor
27280  * Creates a new Button
27281  * @param {Object} config A standard {@link Roo.Button} config object
27282  */
27283 Roo.Toolbar.Button = function(config){
27284     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27285 };
27286 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27287     render : function(td){
27288         this.td = td;
27289         Roo.Toolbar.Button.superclass.render.call(this, td);
27290     },
27291     
27292     /**
27293      * Removes and destroys this button
27294      */
27295     destroy : function(){
27296         Roo.Toolbar.Button.superclass.destroy.call(this);
27297         this.td.parentNode.removeChild(this.td);
27298     },
27299     
27300     /**
27301      * Shows this button
27302      */
27303     show: function(){
27304         this.hidden = false;
27305         this.td.style.display = "";
27306     },
27307     
27308     /**
27309      * Hides this button
27310      */
27311     hide: function(){
27312         this.hidden = true;
27313         this.td.style.display = "none";
27314     },
27315
27316     /**
27317      * Disables this item
27318      */
27319     disable : function(){
27320         Roo.fly(this.td).addClass("x-item-disabled");
27321         this.disabled = true;
27322     },
27323
27324     /**
27325      * Enables this item
27326      */
27327     enable : function(){
27328         Roo.fly(this.td).removeClass("x-item-disabled");
27329         this.disabled = false;
27330     }
27331 });
27332 // backwards compat
27333 Roo.ToolbarButton = Roo.Toolbar.Button;
27334
27335 /**
27336  * @class Roo.Toolbar.SplitButton
27337  * @extends Roo.SplitButton
27338  * A menu button that renders into a toolbar.
27339  * @constructor
27340  * Creates a new SplitButton
27341  * @param {Object} config A standard {@link Roo.SplitButton} config object
27342  */
27343 Roo.Toolbar.SplitButton = function(config){
27344     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27345 };
27346 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27347     render : function(td){
27348         this.td = td;
27349         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27350     },
27351     
27352     /**
27353      * Removes and destroys this button
27354      */
27355     destroy : function(){
27356         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27357         this.td.parentNode.removeChild(this.td);
27358     },
27359     
27360     /**
27361      * Shows this button
27362      */
27363     show: function(){
27364         this.hidden = false;
27365         this.td.style.display = "";
27366     },
27367     
27368     /**
27369      * Hides this button
27370      */
27371     hide: function(){
27372         this.hidden = true;
27373         this.td.style.display = "none";
27374     }
27375 });
27376
27377 // backwards compat
27378 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27379  * Based on:
27380  * Ext JS Library 1.1.1
27381  * Copyright(c) 2006-2007, Ext JS, LLC.
27382  *
27383  * Originally Released Under LGPL - original licence link has changed is not relivant.
27384  *
27385  * Fork - LGPL
27386  * <script type="text/javascript">
27387  */
27388  
27389 /**
27390  * @class Roo.PagingToolbar
27391  * @extends Roo.Toolbar
27392  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27393  * @constructor
27394  * Create a new PagingToolbar
27395  * @param {Object} config The config object
27396  */
27397 Roo.PagingToolbar = function(el, ds, config)
27398 {
27399     // old args format still supported... - xtype is prefered..
27400     if (typeof(el) == 'object' && el.xtype) {
27401         // created from xtype...
27402         config = el;
27403         ds = el.dataSource;
27404         el = config.container;
27405     }
27406     var items = [];
27407     if (config.items) {
27408         items = config.items;
27409         config.items = [];
27410     }
27411     
27412     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27413     this.ds = ds;
27414     this.cursor = 0;
27415     this.renderButtons(this.el);
27416     this.bind(ds);
27417     
27418     // supprot items array.
27419    
27420     Roo.each(items, function(e) {
27421         this.add(Roo.factory(e));
27422     },this);
27423     
27424 };
27425
27426 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27427     /**
27428      * @cfg {Roo.data.Store} dataSource
27429      * The underlying data store providing the paged data
27430      */
27431     /**
27432      * @cfg {String/HTMLElement/Element} container
27433      * container The id or element that will contain the toolbar
27434      */
27435     /**
27436      * @cfg {Boolean} displayInfo
27437      * True to display the displayMsg (defaults to false)
27438      */
27439     /**
27440      * @cfg {Number} pageSize
27441      * The number of records to display per page (defaults to 20)
27442      */
27443     pageSize: 20,
27444     /**
27445      * @cfg {String} displayMsg
27446      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27447      */
27448     displayMsg : 'Displaying {0} - {1} of {2}',
27449     /**
27450      * @cfg {String} emptyMsg
27451      * The message to display when no records are found (defaults to "No data to display")
27452      */
27453     emptyMsg : 'No data to display',
27454     /**
27455      * Customizable piece of the default paging text (defaults to "Page")
27456      * @type String
27457      */
27458     beforePageText : "Page",
27459     /**
27460      * Customizable piece of the default paging text (defaults to "of %0")
27461      * @type String
27462      */
27463     afterPageText : "of {0}",
27464     /**
27465      * Customizable piece of the default paging text (defaults to "First Page")
27466      * @type String
27467      */
27468     firstText : "First Page",
27469     /**
27470      * Customizable piece of the default paging text (defaults to "Previous Page")
27471      * @type String
27472      */
27473     prevText : "Previous Page",
27474     /**
27475      * Customizable piece of the default paging text (defaults to "Next Page")
27476      * @type String
27477      */
27478     nextText : "Next Page",
27479     /**
27480      * Customizable piece of the default paging text (defaults to "Last Page")
27481      * @type String
27482      */
27483     lastText : "Last Page",
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Refresh")
27486      * @type String
27487      */
27488     refreshText : "Refresh",
27489
27490     // private
27491     renderButtons : function(el){
27492         Roo.PagingToolbar.superclass.render.call(this, el);
27493         this.first = this.addButton({
27494             tooltip: this.firstText,
27495             cls: "x-btn-icon x-grid-page-first",
27496             disabled: true,
27497             handler: this.onClick.createDelegate(this, ["first"])
27498         });
27499         this.prev = this.addButton({
27500             tooltip: this.prevText,
27501             cls: "x-btn-icon x-grid-page-prev",
27502             disabled: true,
27503             handler: this.onClick.createDelegate(this, ["prev"])
27504         });
27505         //this.addSeparator();
27506         this.add(this.beforePageText);
27507         this.field = Roo.get(this.addDom({
27508            tag: "input",
27509            type: "text",
27510            size: "3",
27511            value: "1",
27512            cls: "x-grid-page-number"
27513         }).el);
27514         this.field.on("keydown", this.onPagingKeydown, this);
27515         this.field.on("focus", function(){this.dom.select();});
27516         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27517         this.field.setHeight(18);
27518         //this.addSeparator();
27519         this.next = this.addButton({
27520             tooltip: this.nextText,
27521             cls: "x-btn-icon x-grid-page-next",
27522             disabled: true,
27523             handler: this.onClick.createDelegate(this, ["next"])
27524         });
27525         this.last = this.addButton({
27526             tooltip: this.lastText,
27527             cls: "x-btn-icon x-grid-page-last",
27528             disabled: true,
27529             handler: this.onClick.createDelegate(this, ["last"])
27530         });
27531         //this.addSeparator();
27532         this.loading = this.addButton({
27533             tooltip: this.refreshText,
27534             cls: "x-btn-icon x-grid-loading",
27535             handler: this.onClick.createDelegate(this, ["refresh"])
27536         });
27537
27538         if(this.displayInfo){
27539             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27540         }
27541     },
27542
27543     // private
27544     updateInfo : function(){
27545         if(this.displayEl){
27546             var count = this.ds.getCount();
27547             var msg = count == 0 ?
27548                 this.emptyMsg :
27549                 String.format(
27550                     this.displayMsg,
27551                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27552                 );
27553             this.displayEl.update(msg);
27554         }
27555     },
27556
27557     // private
27558     onLoad : function(ds, r, o){
27559        this.cursor = o.params ? o.params.start : 0;
27560        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27561
27562        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27563        this.field.dom.value = ap;
27564        this.first.setDisabled(ap == 1);
27565        this.prev.setDisabled(ap == 1);
27566        this.next.setDisabled(ap == ps);
27567        this.last.setDisabled(ap == ps);
27568        this.loading.enable();
27569        this.updateInfo();
27570     },
27571
27572     // private
27573     getPageData : function(){
27574         var total = this.ds.getTotalCount();
27575         return {
27576             total : total,
27577             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27578             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27579         };
27580     },
27581
27582     // private
27583     onLoadError : function(){
27584         this.loading.enable();
27585     },
27586
27587     // private
27588     onPagingKeydown : function(e){
27589         var k = e.getKey();
27590         var d = this.getPageData();
27591         if(k == e.RETURN){
27592             var v = this.field.dom.value, pageNum;
27593             if(!v || isNaN(pageNum = parseInt(v, 10))){
27594                 this.field.dom.value = d.activePage;
27595                 return;
27596             }
27597             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27598             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27599             e.stopEvent();
27600         }
27601         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27602         {
27603           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27604           this.field.dom.value = pageNum;
27605           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27606           e.stopEvent();
27607         }
27608         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27609         {
27610           var v = this.field.dom.value, pageNum; 
27611           var increment = (e.shiftKey) ? 10 : 1;
27612           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27613             increment *= -1;
27614           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27615             this.field.dom.value = d.activePage;
27616             return;
27617           }
27618           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27619           {
27620             this.field.dom.value = parseInt(v, 10) + increment;
27621             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623           }
27624           e.stopEvent();
27625         }
27626     },
27627
27628     // private
27629     beforeLoad : function(){
27630         if(this.loading){
27631             this.loading.disable();
27632         }
27633     },
27634
27635     // private
27636     onClick : function(which){
27637         var ds = this.ds;
27638         switch(which){
27639             case "first":
27640                 ds.load({params:{start: 0, limit: this.pageSize}});
27641             break;
27642             case "prev":
27643                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27644             break;
27645             case "next":
27646                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27647             break;
27648             case "last":
27649                 var total = ds.getTotalCount();
27650                 var extra = total % this.pageSize;
27651                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27652                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27653             break;
27654             case "refresh":
27655                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27656             break;
27657         }
27658     },
27659
27660     /**
27661      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27662      * @param {Roo.data.Store} store The data store to unbind
27663      */
27664     unbind : function(ds){
27665         ds.un("beforeload", this.beforeLoad, this);
27666         ds.un("load", this.onLoad, this);
27667         ds.un("loadexception", this.onLoadError, this);
27668         ds.un("remove", this.updateInfo, this);
27669         ds.un("add", this.updateInfo, this);
27670         this.ds = undefined;
27671     },
27672
27673     /**
27674      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27675      * @param {Roo.data.Store} store The data store to bind
27676      */
27677     bind : function(ds){
27678         ds.on("beforeload", this.beforeLoad, this);
27679         ds.on("load", this.onLoad, this);
27680         ds.on("loadexception", this.onLoadError, this);
27681         ds.on("remove", this.updateInfo, this);
27682         ds.on("add", this.updateInfo, this);
27683         this.ds = ds;
27684     }
27685 });/*
27686  * Based on:
27687  * Ext JS Library 1.1.1
27688  * Copyright(c) 2006-2007, Ext JS, LLC.
27689  *
27690  * Originally Released Under LGPL - original licence link has changed is not relivant.
27691  *
27692  * Fork - LGPL
27693  * <script type="text/javascript">
27694  */
27695
27696 /**
27697  * @class Roo.Resizable
27698  * @extends Roo.util.Observable
27699  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27700  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27701  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27702  * the element will be wrapped for you automatically.</p>
27703  * <p>Here is the list of valid resize handles:</p>
27704  * <pre>
27705 Value   Description
27706 ------  -------------------
27707  'n'     north
27708  's'     south
27709  'e'     east
27710  'w'     west
27711  'nw'    northwest
27712  'sw'    southwest
27713  'se'    southeast
27714  'ne'    northeast
27715  'hd'    horizontal drag
27716  'all'   all
27717 </pre>
27718  * <p>Here's an example showing the creation of a typical Resizable:</p>
27719  * <pre><code>
27720 var resizer = new Roo.Resizable("element-id", {
27721     handles: 'all',
27722     minWidth: 200,
27723     minHeight: 100,
27724     maxWidth: 500,
27725     maxHeight: 400,
27726     pinned: true
27727 });
27728 resizer.on("resize", myHandler);
27729 </code></pre>
27730  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27731  * resizer.east.setDisplayed(false);</p>
27732  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27733  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27734  * resize operation's new size (defaults to [0, 0])
27735  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27736  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27737  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27738  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27739  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27740  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27741  * @cfg {Number} width The width of the element in pixels (defaults to null)
27742  * @cfg {Number} height The height of the element in pixels (defaults to null)
27743  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27744  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27745  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27746  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27747  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27748  * in favor of the handles config option (defaults to false)
27749  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27750  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27751  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27752  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27753  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27754  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27755  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27756  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27757  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27758  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27759  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27760  * @constructor
27761  * Create a new resizable component
27762  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27763  * @param {Object} config configuration options
27764   */
27765 Roo.Resizable = function(el, config)
27766 {
27767     this.el = Roo.get(el);
27768
27769     if(config && config.wrap){
27770         config.resizeChild = this.el;
27771         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27772         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27773         this.el.setStyle("overflow", "hidden");
27774         this.el.setPositioning(config.resizeChild.getPositioning());
27775         config.resizeChild.clearPositioning();
27776         if(!config.width || !config.height){
27777             var csize = config.resizeChild.getSize();
27778             this.el.setSize(csize.width, csize.height);
27779         }
27780         if(config.pinned && !config.adjustments){
27781             config.adjustments = "auto";
27782         }
27783     }
27784
27785     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27786     this.proxy.unselectable();
27787     this.proxy.enableDisplayMode('block');
27788
27789     Roo.apply(this, config);
27790
27791     if(this.pinned){
27792         this.disableTrackOver = true;
27793         this.el.addClass("x-resizable-pinned");
27794     }
27795     // if the element isn't positioned, make it relative
27796     var position = this.el.getStyle("position");
27797     if(position != "absolute" && position != "fixed"){
27798         this.el.setStyle("position", "relative");
27799     }
27800     if(!this.handles){ // no handles passed, must be legacy style
27801         this.handles = 's,e,se';
27802         if(this.multiDirectional){
27803             this.handles += ',n,w';
27804         }
27805     }
27806     if(this.handles == "all"){
27807         this.handles = "n s e w ne nw se sw";
27808     }
27809     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27810     var ps = Roo.Resizable.positions;
27811     for(var i = 0, len = hs.length; i < len; i++){
27812         if(hs[i] && ps[hs[i]]){
27813             var pos = ps[hs[i]];
27814             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27815         }
27816     }
27817     // legacy
27818     this.corner = this.southeast;
27819     
27820     // updateBox = the box can move..
27821     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27822         this.updateBox = true;
27823     }
27824
27825     this.activeHandle = null;
27826
27827     if(this.resizeChild){
27828         if(typeof this.resizeChild == "boolean"){
27829             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27830         }else{
27831             this.resizeChild = Roo.get(this.resizeChild, true);
27832         }
27833     }
27834     
27835     if(this.adjustments == "auto"){
27836         var rc = this.resizeChild;
27837         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27838         if(rc && (hw || hn)){
27839             rc.position("relative");
27840             rc.setLeft(hw ? hw.el.getWidth() : 0);
27841             rc.setTop(hn ? hn.el.getHeight() : 0);
27842         }
27843         this.adjustments = [
27844             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27845             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27846         ];
27847     }
27848
27849     if(this.draggable){
27850         this.dd = this.dynamic ?
27851             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27852         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27853     }
27854
27855     // public events
27856     this.addEvents({
27857         /**
27858          * @event beforeresize
27859          * Fired before resize is allowed. Set enabled to false to cancel resize.
27860          * @param {Roo.Resizable} this
27861          * @param {Roo.EventObject} e The mousedown event
27862          */
27863         "beforeresize" : true,
27864         /**
27865          * @event resize
27866          * Fired after a resize.
27867          * @param {Roo.Resizable} this
27868          * @param {Number} width The new width
27869          * @param {Number} height The new height
27870          * @param {Roo.EventObject} e The mouseup event
27871          */
27872         "resize" : true
27873     });
27874
27875     if(this.width !== null && this.height !== null){
27876         this.resizeTo(this.width, this.height);
27877     }else{
27878         this.updateChildSize();
27879     }
27880     if(Roo.isIE){
27881         this.el.dom.style.zoom = 1;
27882     }
27883     Roo.Resizable.superclass.constructor.call(this);
27884 };
27885
27886 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27887         resizeChild : false,
27888         adjustments : [0, 0],
27889         minWidth : 5,
27890         minHeight : 5,
27891         maxWidth : 10000,
27892         maxHeight : 10000,
27893         enabled : true,
27894         animate : false,
27895         duration : .35,
27896         dynamic : false,
27897         handles : false,
27898         multiDirectional : false,
27899         disableTrackOver : false,
27900         easing : 'easeOutStrong',
27901         widthIncrement : 0,
27902         heightIncrement : 0,
27903         pinned : false,
27904         width : null,
27905         height : null,
27906         preserveRatio : false,
27907         transparent: false,
27908         minX: 0,
27909         minY: 0,
27910         draggable: false,
27911
27912         /**
27913          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27914          */
27915         constrainTo: undefined,
27916         /**
27917          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27918          */
27919         resizeRegion: undefined,
27920
27921
27922     /**
27923      * Perform a manual resize
27924      * @param {Number} width
27925      * @param {Number} height
27926      */
27927     resizeTo : function(width, height){
27928         this.el.setSize(width, height);
27929         this.updateChildSize();
27930         this.fireEvent("resize", this, width, height, null);
27931     },
27932
27933     // private
27934     startSizing : function(e, handle){
27935         this.fireEvent("beforeresize", this, e);
27936         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27937
27938             if(!this.overlay){
27939                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27940                 this.overlay.unselectable();
27941                 this.overlay.enableDisplayMode("block");
27942                 this.overlay.on("mousemove", this.onMouseMove, this);
27943                 this.overlay.on("mouseup", this.onMouseUp, this);
27944             }
27945             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27946
27947             this.resizing = true;
27948             this.startBox = this.el.getBox();
27949             this.startPoint = e.getXY();
27950             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27951                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27952
27953             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27954             this.overlay.show();
27955
27956             if(this.constrainTo) {
27957                 var ct = Roo.get(this.constrainTo);
27958                 this.resizeRegion = ct.getRegion().adjust(
27959                     ct.getFrameWidth('t'),
27960                     ct.getFrameWidth('l'),
27961                     -ct.getFrameWidth('b'),
27962                     -ct.getFrameWidth('r')
27963                 );
27964             }
27965
27966             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27967             this.proxy.show();
27968             this.proxy.setBox(this.startBox);
27969             if(!this.dynamic){
27970                 this.proxy.setStyle('visibility', 'visible');
27971             }
27972         }
27973     },
27974
27975     // private
27976     onMouseDown : function(handle, e){
27977         if(this.enabled){
27978             e.stopEvent();
27979             this.activeHandle = handle;
27980             this.startSizing(e, handle);
27981         }
27982     },
27983
27984     // private
27985     onMouseUp : function(e){
27986         var size = this.resizeElement();
27987         this.resizing = false;
27988         this.handleOut();
27989         this.overlay.hide();
27990         this.proxy.hide();
27991         this.fireEvent("resize", this, size.width, size.height, e);
27992     },
27993
27994     // private
27995     updateChildSize : function(){
27996         if(this.resizeChild){
27997             var el = this.el;
27998             var child = this.resizeChild;
27999             var adj = this.adjustments;
28000             if(el.dom.offsetWidth){
28001                 var b = el.getSize(true);
28002                 child.setSize(b.width+adj[0], b.height+adj[1]);
28003             }
28004             // Second call here for IE
28005             // The first call enables instant resizing and
28006             // the second call corrects scroll bars if they
28007             // exist
28008             if(Roo.isIE){
28009                 setTimeout(function(){
28010                     if(el.dom.offsetWidth){
28011                         var b = el.getSize(true);
28012                         child.setSize(b.width+adj[0], b.height+adj[1]);
28013                     }
28014                 }, 10);
28015             }
28016         }
28017     },
28018
28019     // private
28020     snap : function(value, inc, min){
28021         if(!inc || !value) return value;
28022         var newValue = value;
28023         var m = value % inc;
28024         if(m > 0){
28025             if(m > (inc/2)){
28026                 newValue = value + (inc-m);
28027             }else{
28028                 newValue = value - m;
28029             }
28030         }
28031         return Math.max(min, newValue);
28032     },
28033
28034     // private
28035     resizeElement : function(){
28036         var box = this.proxy.getBox();
28037         if(this.updateBox){
28038             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28039         }else{
28040             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28041         }
28042         this.updateChildSize();
28043         if(!this.dynamic){
28044             this.proxy.hide();
28045         }
28046         return box;
28047     },
28048
28049     // private
28050     constrain : function(v, diff, m, mx){
28051         if(v - diff < m){
28052             diff = v - m;
28053         }else if(v - diff > mx){
28054             diff = mx - v;
28055         }
28056         return diff;
28057     },
28058
28059     // private
28060     onMouseMove : function(e){
28061         if(this.enabled){
28062             try{// try catch so if something goes wrong the user doesn't get hung
28063
28064             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28065                 return;
28066             }
28067
28068             //var curXY = this.startPoint;
28069             var curSize = this.curSize || this.startBox;
28070             var x = this.startBox.x, y = this.startBox.y;
28071             var ox = x, oy = y;
28072             var w = curSize.width, h = curSize.height;
28073             var ow = w, oh = h;
28074             var mw = this.minWidth, mh = this.minHeight;
28075             var mxw = this.maxWidth, mxh = this.maxHeight;
28076             var wi = this.widthIncrement;
28077             var hi = this.heightIncrement;
28078
28079             var eventXY = e.getXY();
28080             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28081             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28082
28083             var pos = this.activeHandle.position;
28084
28085             switch(pos){
28086                 case "east":
28087                     w += diffX;
28088                     w = Math.min(Math.max(mw, w), mxw);
28089                     break;
28090              
28091                 case "south":
28092                     h += diffY;
28093                     h = Math.min(Math.max(mh, h), mxh);
28094                     break;
28095                 case "southeast":
28096                     w += diffX;
28097                     h += diffY;
28098                     w = Math.min(Math.max(mw, w), mxw);
28099                     h = Math.min(Math.max(mh, h), mxh);
28100                     break;
28101                 case "north":
28102                     diffY = this.constrain(h, diffY, mh, mxh);
28103                     y += diffY;
28104                     h -= diffY;
28105                     break;
28106                 case "hdrag":
28107                     
28108                     if (wi) {
28109                         var adiffX = Math.abs(diffX);
28110                         var sub = (adiffX % wi); // how much 
28111                         if (sub > (wi/2)) { // far enough to snap
28112                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28113                         } else {
28114                             // remove difference.. 
28115                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28116                         }
28117                     }
28118                     x += diffX;
28119                     x = Math.max(this.minX, x);
28120                     break;
28121                 case "west":
28122                     diffX = this.constrain(w, diffX, mw, mxw);
28123                     x += diffX;
28124                     w -= diffX;
28125                     break;
28126                 case "northeast":
28127                     w += diffX;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     diffY = this.constrain(h, diffY, mh, mxh);
28130                     y += diffY;
28131                     h -= diffY;
28132                     break;
28133                 case "northwest":
28134                     diffX = this.constrain(w, diffX, mw, mxw);
28135                     diffY = this.constrain(h, diffY, mh, mxh);
28136                     y += diffY;
28137                     h -= diffY;
28138                     x += diffX;
28139                     w -= diffX;
28140                     break;
28141                case "southwest":
28142                     diffX = this.constrain(w, diffX, mw, mxw);
28143                     h += diffY;
28144                     h = Math.min(Math.max(mh, h), mxh);
28145                     x += diffX;
28146                     w -= diffX;
28147                     break;
28148             }
28149
28150             var sw = this.snap(w, wi, mw);
28151             var sh = this.snap(h, hi, mh);
28152             if(sw != w || sh != h){
28153                 switch(pos){
28154                     case "northeast":
28155                         y -= sh - h;
28156                     break;
28157                     case "north":
28158                         y -= sh - h;
28159                         break;
28160                     case "southwest":
28161                         x -= sw - w;
28162                     break;
28163                     case "west":
28164                         x -= sw - w;
28165                         break;
28166                     case "northwest":
28167                         x -= sw - w;
28168                         y -= sh - h;
28169                     break;
28170                 }
28171                 w = sw;
28172                 h = sh;
28173             }
28174
28175             if(this.preserveRatio){
28176                 switch(pos){
28177                     case "southeast":
28178                     case "east":
28179                         h = oh * (w/ow);
28180                         h = Math.min(Math.max(mh, h), mxh);
28181                         w = ow * (h/oh);
28182                        break;
28183                     case "south":
28184                         w = ow * (h/oh);
28185                         w = Math.min(Math.max(mw, w), mxw);
28186                         h = oh * (w/ow);
28187                         break;
28188                     case "northeast":
28189                         w = ow * (h/oh);
28190                         w = Math.min(Math.max(mw, w), mxw);
28191                         h = oh * (w/ow);
28192                     break;
28193                     case "north":
28194                         var tw = w;
28195                         w = ow * (h/oh);
28196                         w = Math.min(Math.max(mw, w), mxw);
28197                         h = oh * (w/ow);
28198                         x += (tw - w) / 2;
28199                         break;
28200                     case "southwest":
28201                         h = oh * (w/ow);
28202                         h = Math.min(Math.max(mh, h), mxh);
28203                         var tw = w;
28204                         w = ow * (h/oh);
28205                         x += tw - w;
28206                         break;
28207                     case "west":
28208                         var th = h;
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         y += (th - h) / 2;
28212                         var tw = w;
28213                         w = ow * (h/oh);
28214                         x += tw - w;
28215                        break;
28216                     case "northwest":
28217                         var tw = w;
28218                         var th = h;
28219                         h = oh * (w/ow);
28220                         h = Math.min(Math.max(mh, h), mxh);
28221                         w = ow * (h/oh);
28222                         y += th - h;
28223                         x += tw - w;
28224                        break;
28225
28226                 }
28227             }
28228             if (pos == 'hdrag') {
28229                 w = ow;
28230             }
28231             this.proxy.setBounds(x, y, w, h);
28232             if(this.dynamic){
28233                 this.resizeElement();
28234             }
28235             }catch(e){}
28236         }
28237     },
28238
28239     // private
28240     handleOver : function(){
28241         if(this.enabled){
28242             this.el.addClass("x-resizable-over");
28243         }
28244     },
28245
28246     // private
28247     handleOut : function(){
28248         if(!this.resizing){
28249             this.el.removeClass("x-resizable-over");
28250         }
28251     },
28252
28253     /**
28254      * Returns the element this component is bound to.
28255      * @return {Roo.Element}
28256      */
28257     getEl : function(){
28258         return this.el;
28259     },
28260
28261     /**
28262      * Returns the resizeChild element (or null).
28263      * @return {Roo.Element}
28264      */
28265     getResizeChild : function(){
28266         return this.resizeChild;
28267     },
28268
28269     /**
28270      * Destroys this resizable. If the element was wrapped and
28271      * removeEl is not true then the element remains.
28272      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28273      */
28274     destroy : function(removeEl){
28275         this.proxy.remove();
28276         if(this.overlay){
28277             this.overlay.removeAllListeners();
28278             this.overlay.remove();
28279         }
28280         var ps = Roo.Resizable.positions;
28281         for(var k in ps){
28282             if(typeof ps[k] != "function" && this[ps[k]]){
28283                 var h = this[ps[k]];
28284                 h.el.removeAllListeners();
28285                 h.el.remove();
28286             }
28287         }
28288         if(removeEl){
28289             this.el.update("");
28290             this.el.remove();
28291         }
28292     }
28293 });
28294
28295 // private
28296 // hash to map config positions to true positions
28297 Roo.Resizable.positions = {
28298     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28299     hd: "hdrag"
28300 };
28301
28302 // private
28303 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28304     if(!this.tpl){
28305         // only initialize the template if resizable is used
28306         var tpl = Roo.DomHelper.createTemplate(
28307             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28308         );
28309         tpl.compile();
28310         Roo.Resizable.Handle.prototype.tpl = tpl;
28311     }
28312     this.position = pos;
28313     this.rz = rz;
28314     // show north drag fro topdra
28315     var handlepos = pos == 'hdrag' ? 'north' : pos;
28316     
28317     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28318     if (pos == 'hdrag') {
28319         this.el.setStyle('cursor', 'pointer');
28320     }
28321     this.el.unselectable();
28322     if(transparent){
28323         this.el.setOpacity(0);
28324     }
28325     this.el.on("mousedown", this.onMouseDown, this);
28326     if(!disableTrackOver){
28327         this.el.on("mouseover", this.onMouseOver, this);
28328         this.el.on("mouseout", this.onMouseOut, this);
28329     }
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle.prototype = {
28334     afterResize : function(rz){
28335         // do nothing
28336     },
28337     // private
28338     onMouseDown : function(e){
28339         this.rz.onMouseDown(this, e);
28340     },
28341     // private
28342     onMouseOver : function(e){
28343         this.rz.handleOver(this, e);
28344     },
28345     // private
28346     onMouseOut : function(e){
28347         this.rz.handleOut(this, e);
28348     }
28349 };/*
28350  * Based on:
28351  * Ext JS Library 1.1.1
28352  * Copyright(c) 2006-2007, Ext JS, LLC.
28353  *
28354  * Originally Released Under LGPL - original licence link has changed is not relivant.
28355  *
28356  * Fork - LGPL
28357  * <script type="text/javascript">
28358  */
28359
28360 /**
28361  * @class Roo.Editor
28362  * @extends Roo.Component
28363  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28364  * @constructor
28365  * Create a new Editor
28366  * @param {Roo.form.Field} field The Field object (or descendant)
28367  * @param {Object} config The config object
28368  */
28369 Roo.Editor = function(field, config){
28370     Roo.Editor.superclass.constructor.call(this, config);
28371     this.field = field;
28372     this.addEvents({
28373         /**
28374              * @event beforestartedit
28375              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28376              * false from the handler of this event.
28377              * @param {Editor} this
28378              * @param {Roo.Element} boundEl The underlying element bound to this editor
28379              * @param {Mixed} value The field value being set
28380              */
28381         "beforestartedit" : true,
28382         /**
28383              * @event startedit
28384              * Fires when this editor is displayed
28385              * @param {Roo.Element} boundEl The underlying element bound to this editor
28386              * @param {Mixed} value The starting field value
28387              */
28388         "startedit" : true,
28389         /**
28390              * @event beforecomplete
28391              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28392              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28393              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28394              * event will not fire since no edit actually occurred.
28395              * @param {Editor} this
28396              * @param {Mixed} value The current field value
28397              * @param {Mixed} startValue The original field value
28398              */
28399         "beforecomplete" : true,
28400         /**
28401              * @event complete
28402              * Fires after editing is complete and any changed value has been written to the underlying field.
28403              * @param {Editor} this
28404              * @param {Mixed} value The current field value
28405              * @param {Mixed} startValue The original field value
28406              */
28407         "complete" : true,
28408         /**
28409          * @event specialkey
28410          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28411          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28412          * @param {Roo.form.Field} this
28413          * @param {Roo.EventObject} e The event object
28414          */
28415         "specialkey" : true
28416     });
28417 };
28418
28419 Roo.extend(Roo.Editor, Roo.Component, {
28420     /**
28421      * @cfg {Boolean/String} autosize
28422      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28423      * or "height" to adopt the height only (defaults to false)
28424      */
28425     /**
28426      * @cfg {Boolean} revertInvalid
28427      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28428      * validation fails (defaults to true)
28429      */
28430     /**
28431      * @cfg {Boolean} ignoreNoChange
28432      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28433      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28434      * will never be ignored.
28435      */
28436     /**
28437      * @cfg {Boolean} hideEl
28438      * False to keep the bound element visible while the editor is displayed (defaults to true)
28439      */
28440     /**
28441      * @cfg {Mixed} value
28442      * The data value of the underlying field (defaults to "")
28443      */
28444     value : "",
28445     /**
28446      * @cfg {String} alignment
28447      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28448      */
28449     alignment: "c-c?",
28450     /**
28451      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28452      * for bottom-right shadow (defaults to "frame")
28453      */
28454     shadow : "frame",
28455     /**
28456      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28457      */
28458     constrain : false,
28459     /**
28460      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28461      */
28462     completeOnEnter : false,
28463     /**
28464      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28465      */
28466     cancelOnEsc : false,
28467     /**
28468      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28469      */
28470     updateEl : false,
28471
28472     // private
28473     onRender : function(ct, position){
28474         this.el = new Roo.Layer({
28475             shadow: this.shadow,
28476             cls: "x-editor",
28477             parentEl : ct,
28478             shim : this.shim,
28479             shadowOffset:4,
28480             id: this.id,
28481             constrain: this.constrain
28482         });
28483         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28484         if(this.field.msgTarget != 'title'){
28485             this.field.msgTarget = 'qtip';
28486         }
28487         this.field.render(this.el);
28488         if(Roo.isGecko){
28489             this.field.el.dom.setAttribute('autocomplete', 'off');
28490         }
28491         this.field.on("specialkey", this.onSpecialKey, this);
28492         if(this.swallowKeys){
28493             this.field.el.swallowEvent(['keydown','keypress']);
28494         }
28495         this.field.show();
28496         this.field.on("blur", this.onBlur, this);
28497         if(this.field.grow){
28498             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28499         }
28500     },
28501
28502     onSpecialKey : function(field, e)
28503     {
28504         //Roo.log('editor onSpecialKey');
28505         if(this.completeOnEnter && e.getKey() == e.ENTER){
28506             e.stopEvent();
28507             this.completeEdit();
28508             return;
28509         }
28510         // do not fire special key otherwise it might hide close the editor...
28511         if(e.getKey() == e.ENTER){    
28512             return;
28513         }
28514         if(this.cancelOnEsc && e.getKey() == e.ESC){
28515             this.cancelEdit();
28516             return;
28517         } 
28518         this.fireEvent('specialkey', field, e);
28519     
28520     },
28521
28522     /**
28523      * Starts the editing process and shows the editor.
28524      * @param {String/HTMLElement/Element} el The element to edit
28525      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28526       * to the innerHTML of el.
28527      */
28528     startEdit : function(el, value){
28529         if(this.editing){
28530             this.completeEdit();
28531         }
28532         this.boundEl = Roo.get(el);
28533         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28534         if(!this.rendered){
28535             this.render(this.parentEl || document.body);
28536         }
28537         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28538             return;
28539         }
28540         this.startValue = v;
28541         this.field.setValue(v);
28542         if(this.autoSize){
28543             var sz = this.boundEl.getSize();
28544             switch(this.autoSize){
28545                 case "width":
28546                 this.setSize(sz.width,  "");
28547                 break;
28548                 case "height":
28549                 this.setSize("",  sz.height);
28550                 break;
28551                 default:
28552                 this.setSize(sz.width,  sz.height);
28553             }
28554         }
28555         this.el.alignTo(this.boundEl, this.alignment);
28556         this.editing = true;
28557         if(Roo.QuickTips){
28558             Roo.QuickTips.disable();
28559         }
28560         this.show();
28561     },
28562
28563     /**
28564      * Sets the height and width of this editor.
28565      * @param {Number} width The new width
28566      * @param {Number} height The new height
28567      */
28568     setSize : function(w, h){
28569         this.field.setSize(w, h);
28570         if(this.el){
28571             this.el.sync();
28572         }
28573     },
28574
28575     /**
28576      * Realigns the editor to the bound field based on the current alignment config value.
28577      */
28578     realign : function(){
28579         this.el.alignTo(this.boundEl, this.alignment);
28580     },
28581
28582     /**
28583      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28584      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28585      */
28586     completeEdit : function(remainVisible){
28587         if(!this.editing){
28588             return;
28589         }
28590         var v = this.getValue();
28591         if(this.revertInvalid !== false && !this.field.isValid()){
28592             v = this.startValue;
28593             this.cancelEdit(true);
28594         }
28595         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28596             this.editing = false;
28597             this.hide();
28598             return;
28599         }
28600         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28601             this.editing = false;
28602             if(this.updateEl && this.boundEl){
28603                 this.boundEl.update(v);
28604             }
28605             if(remainVisible !== true){
28606                 this.hide();
28607             }
28608             this.fireEvent("complete", this, v, this.startValue);
28609         }
28610     },
28611
28612     // private
28613     onShow : function(){
28614         this.el.show();
28615         if(this.hideEl !== false){
28616             this.boundEl.hide();
28617         }
28618         this.field.show();
28619         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28620             this.fixIEFocus = true;
28621             this.deferredFocus.defer(50, this);
28622         }else{
28623             this.field.focus();
28624         }
28625         this.fireEvent("startedit", this.boundEl, this.startValue);
28626     },
28627
28628     deferredFocus : function(){
28629         if(this.editing){
28630             this.field.focus();
28631         }
28632     },
28633
28634     /**
28635      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28636      * reverted to the original starting value.
28637      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28638      * cancel (defaults to false)
28639      */
28640     cancelEdit : function(remainVisible){
28641         if(this.editing){
28642             this.setValue(this.startValue);
28643             if(remainVisible !== true){
28644                 this.hide();
28645             }
28646         }
28647     },
28648
28649     // private
28650     onBlur : function(){
28651         if(this.allowBlur !== true && this.editing){
28652             this.completeEdit();
28653         }
28654     },
28655
28656     // private
28657     onHide : function(){
28658         if(this.editing){
28659             this.completeEdit();
28660             return;
28661         }
28662         this.field.blur();
28663         if(this.field.collapse){
28664             this.field.collapse();
28665         }
28666         this.el.hide();
28667         if(this.hideEl !== false){
28668             this.boundEl.show();
28669         }
28670         if(Roo.QuickTips){
28671             Roo.QuickTips.enable();
28672         }
28673     },
28674
28675     /**
28676      * Sets the data value of the editor
28677      * @param {Mixed} value Any valid value supported by the underlying field
28678      */
28679     setValue : function(v){
28680         this.field.setValue(v);
28681     },
28682
28683     /**
28684      * Gets the data value of the editor
28685      * @return {Mixed} The data value
28686      */
28687     getValue : function(){
28688         return this.field.getValue();
28689     }
28690 });/*
28691  * Based on:
28692  * Ext JS Library 1.1.1
28693  * Copyright(c) 2006-2007, Ext JS, LLC.
28694  *
28695  * Originally Released Under LGPL - original licence link has changed is not relivant.
28696  *
28697  * Fork - LGPL
28698  * <script type="text/javascript">
28699  */
28700  
28701 /**
28702  * @class Roo.BasicDialog
28703  * @extends Roo.util.Observable
28704  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28705  * <pre><code>
28706 var dlg = new Roo.BasicDialog("my-dlg", {
28707     height: 200,
28708     width: 300,
28709     minHeight: 100,
28710     minWidth: 150,
28711     modal: true,
28712     proxyDrag: true,
28713     shadow: true
28714 });
28715 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28716 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28717 dlg.addButton('Cancel', dlg.hide, dlg);
28718 dlg.show();
28719 </code></pre>
28720   <b>A Dialog should always be a direct child of the body element.</b>
28721  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28722  * @cfg {String} title Default text to display in the title bar (defaults to null)
28723  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28724  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28725  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28726  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28727  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28728  * (defaults to null with no animation)
28729  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28730  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28731  * property for valid values (defaults to 'all')
28732  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28733  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28734  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28735  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28736  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28737  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28738  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28739  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28740  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28741  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28742  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28743  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28744  * draggable = true (defaults to false)
28745  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28746  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28747  * shadow (defaults to false)
28748  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28749  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28750  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28751  * @cfg {Array} buttons Array of buttons
28752  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28753  * @constructor
28754  * Create a new BasicDialog.
28755  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28756  * @param {Object} config Configuration options
28757  */
28758 Roo.BasicDialog = function(el, config){
28759     this.el = Roo.get(el);
28760     var dh = Roo.DomHelper;
28761     if(!this.el && config && config.autoCreate){
28762         if(typeof config.autoCreate == "object"){
28763             if(!config.autoCreate.id){
28764                 config.autoCreate.id = el;
28765             }
28766             this.el = dh.append(document.body,
28767                         config.autoCreate, true);
28768         }else{
28769             this.el = dh.append(document.body,
28770                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28771         }
28772     }
28773     el = this.el;
28774     el.setDisplayed(true);
28775     el.hide = this.hideAction;
28776     this.id = el.id;
28777     el.addClass("x-dlg");
28778
28779     Roo.apply(this, config);
28780
28781     this.proxy = el.createProxy("x-dlg-proxy");
28782     this.proxy.hide = this.hideAction;
28783     this.proxy.setOpacity(.5);
28784     this.proxy.hide();
28785
28786     if(config.width){
28787         el.setWidth(config.width);
28788     }
28789     if(config.height){
28790         el.setHeight(config.height);
28791     }
28792     this.size = el.getSize();
28793     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28794         this.xy = [config.x,config.y];
28795     }else{
28796         this.xy = el.getCenterXY(true);
28797     }
28798     /** The header element @type Roo.Element */
28799     this.header = el.child("> .x-dlg-hd");
28800     /** The body element @type Roo.Element */
28801     this.body = el.child("> .x-dlg-bd");
28802     /** The footer element @type Roo.Element */
28803     this.footer = el.child("> .x-dlg-ft");
28804
28805     if(!this.header){
28806         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28807     }
28808     if(!this.body){
28809         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28810     }
28811
28812     this.header.unselectable();
28813     if(this.title){
28814         this.header.update(this.title);
28815     }
28816     // this element allows the dialog to be focused for keyboard event
28817     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28818     this.focusEl.swallowEvent("click", true);
28819
28820     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28821
28822     // wrap the body and footer for special rendering
28823     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28824     if(this.footer){
28825         this.bwrap.dom.appendChild(this.footer.dom);
28826     }
28827
28828     this.bg = this.el.createChild({
28829         tag: "div", cls:"x-dlg-bg",
28830         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28831     });
28832     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28833
28834
28835     if(this.autoScroll !== false && !this.autoTabs){
28836         this.body.setStyle("overflow", "auto");
28837     }
28838
28839     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28840
28841     if(this.closable !== false){
28842         this.el.addClass("x-dlg-closable");
28843         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28844         this.close.on("click", this.closeClick, this);
28845         this.close.addClassOnOver("x-dlg-close-over");
28846     }
28847     if(this.collapsible !== false){
28848         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28849         this.collapseBtn.on("click", this.collapseClick, this);
28850         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28851         this.header.on("dblclick", this.collapseClick, this);
28852     }
28853     if(this.resizable !== false){
28854         this.el.addClass("x-dlg-resizable");
28855         this.resizer = new Roo.Resizable(el, {
28856             minWidth: this.minWidth || 80,
28857             minHeight:this.minHeight || 80,
28858             handles: this.resizeHandles || "all",
28859             pinned: true
28860         });
28861         this.resizer.on("beforeresize", this.beforeResize, this);
28862         this.resizer.on("resize", this.onResize, this);
28863     }
28864     if(this.draggable !== false){
28865         el.addClass("x-dlg-draggable");
28866         if (!this.proxyDrag) {
28867             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28868         }
28869         else {
28870             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28871         }
28872         dd.setHandleElId(this.header.id);
28873         dd.endDrag = this.endMove.createDelegate(this);
28874         dd.startDrag = this.startMove.createDelegate(this);
28875         dd.onDrag = this.onDrag.createDelegate(this);
28876         dd.scroll = false;
28877         this.dd = dd;
28878     }
28879     if(this.modal){
28880         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28881         this.mask.enableDisplayMode("block");
28882         this.mask.hide();
28883         this.el.addClass("x-dlg-modal");
28884     }
28885     if(this.shadow){
28886         this.shadow = new Roo.Shadow({
28887             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28888             offset : this.shadowOffset
28889         });
28890     }else{
28891         this.shadowOffset = 0;
28892     }
28893     if(Roo.useShims && this.shim !== false){
28894         this.shim = this.el.createShim();
28895         this.shim.hide = this.hideAction;
28896         this.shim.hide();
28897     }else{
28898         this.shim = false;
28899     }
28900     if(this.autoTabs){
28901         this.initTabs();
28902     }
28903     if (this.buttons) { 
28904         var bts= this.buttons;
28905         this.buttons = [];
28906         Roo.each(bts, function(b) {
28907             this.addButton(b);
28908         }, this);
28909     }
28910     
28911     
28912     this.addEvents({
28913         /**
28914          * @event keydown
28915          * Fires when a key is pressed
28916          * @param {Roo.BasicDialog} this
28917          * @param {Roo.EventObject} e
28918          */
28919         "keydown" : true,
28920         /**
28921          * @event move
28922          * Fires when this dialog is moved by the user.
28923          * @param {Roo.BasicDialog} this
28924          * @param {Number} x The new page X
28925          * @param {Number} y The new page Y
28926          */
28927         "move" : true,
28928         /**
28929          * @event resize
28930          * Fires when this dialog is resized by the user.
28931          * @param {Roo.BasicDialog} this
28932          * @param {Number} width The new width
28933          * @param {Number} height The new height
28934          */
28935         "resize" : true,
28936         /**
28937          * @event beforehide
28938          * Fires before this dialog is hidden.
28939          * @param {Roo.BasicDialog} this
28940          */
28941         "beforehide" : true,
28942         /**
28943          * @event hide
28944          * Fires when this dialog is hidden.
28945          * @param {Roo.BasicDialog} this
28946          */
28947         "hide" : true,
28948         /**
28949          * @event beforeshow
28950          * Fires before this dialog is shown.
28951          * @param {Roo.BasicDialog} this
28952          */
28953         "beforeshow" : true,
28954         /**
28955          * @event show
28956          * Fires when this dialog is shown.
28957          * @param {Roo.BasicDialog} this
28958          */
28959         "show" : true
28960     });
28961     el.on("keydown", this.onKeyDown, this);
28962     el.on("mousedown", this.toFront, this);
28963     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28964     this.el.hide();
28965     Roo.DialogManager.register(this);
28966     Roo.BasicDialog.superclass.constructor.call(this);
28967 };
28968
28969 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28970     shadowOffset: Roo.isIE ? 6 : 5,
28971     minHeight: 80,
28972     minWidth: 200,
28973     minButtonWidth: 75,
28974     defaultButton: null,
28975     buttonAlign: "right",
28976     tabTag: 'div',
28977     firstShow: true,
28978
28979     /**
28980      * Sets the dialog title text
28981      * @param {String} text The title text to display
28982      * @return {Roo.BasicDialog} this
28983      */
28984     setTitle : function(text){
28985         this.header.update(text);
28986         return this;
28987     },
28988
28989     // private
28990     closeClick : function(){
28991         this.hide();
28992     },
28993
28994     // private
28995     collapseClick : function(){
28996         this[this.collapsed ? "expand" : "collapse"]();
28997     },
28998
28999     /**
29000      * Collapses the dialog to its minimized state (only the title bar is visible).
29001      * Equivalent to the user clicking the collapse dialog button.
29002      */
29003     collapse : function(){
29004         if(!this.collapsed){
29005             this.collapsed = true;
29006             this.el.addClass("x-dlg-collapsed");
29007             this.restoreHeight = this.el.getHeight();
29008             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29009         }
29010     },
29011
29012     /**
29013      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29014      * clicking the expand dialog button.
29015      */
29016     expand : function(){
29017         if(this.collapsed){
29018             this.collapsed = false;
29019             this.el.removeClass("x-dlg-collapsed");
29020             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29021         }
29022     },
29023
29024     /**
29025      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29026      * @return {Roo.TabPanel} The tabs component
29027      */
29028     initTabs : function(){
29029         var tabs = this.getTabs();
29030         while(tabs.getTab(0)){
29031             tabs.removeTab(0);
29032         }
29033         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29034             var dom = el.dom;
29035             tabs.addTab(Roo.id(dom), dom.title);
29036             dom.title = "";
29037         });
29038         tabs.activate(0);
29039         return tabs;
29040     },
29041
29042     // private
29043     beforeResize : function(){
29044         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29045     },
29046
29047     // private
29048     onResize : function(){
29049         this.refreshSize();
29050         this.syncBodyHeight();
29051         this.adjustAssets();
29052         this.focus();
29053         this.fireEvent("resize", this, this.size.width, this.size.height);
29054     },
29055
29056     // private
29057     onKeyDown : function(e){
29058         if(this.isVisible()){
29059             this.fireEvent("keydown", this, e);
29060         }
29061     },
29062
29063     /**
29064      * Resizes the dialog.
29065      * @param {Number} width
29066      * @param {Number} height
29067      * @return {Roo.BasicDialog} this
29068      */
29069     resizeTo : function(width, height){
29070         this.el.setSize(width, height);
29071         this.size = {width: width, height: height};
29072         this.syncBodyHeight();
29073         if(this.fixedcenter){
29074             this.center();
29075         }
29076         if(this.isVisible()){
29077             this.constrainXY();
29078             this.adjustAssets();
29079         }
29080         this.fireEvent("resize", this, width, height);
29081         return this;
29082     },
29083
29084
29085     /**
29086      * Resizes the dialog to fit the specified content size.
29087      * @param {Number} width
29088      * @param {Number} height
29089      * @return {Roo.BasicDialog} this
29090      */
29091     setContentSize : function(w, h){
29092         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29093         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29094         //if(!this.el.isBorderBox()){
29095             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29096             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29097         //}
29098         if(this.tabs){
29099             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29100             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29101         }
29102         this.resizeTo(w, h);
29103         return this;
29104     },
29105
29106     /**
29107      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29108      * executed in response to a particular key being pressed while the dialog is active.
29109      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29110      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29111      * @param {Function} fn The function to call
29112      * @param {Object} scope (optional) The scope of the function
29113      * @return {Roo.BasicDialog} this
29114      */
29115     addKeyListener : function(key, fn, scope){
29116         var keyCode, shift, ctrl, alt;
29117         if(typeof key == "object" && !(key instanceof Array)){
29118             keyCode = key["key"];
29119             shift = key["shift"];
29120             ctrl = key["ctrl"];
29121             alt = key["alt"];
29122         }else{
29123             keyCode = key;
29124         }
29125         var handler = function(dlg, e){
29126             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29127                 var k = e.getKey();
29128                 if(keyCode instanceof Array){
29129                     for(var i = 0, len = keyCode.length; i < len; i++){
29130                         if(keyCode[i] == k){
29131                           fn.call(scope || window, dlg, k, e);
29132                           return;
29133                         }
29134                     }
29135                 }else{
29136                     if(k == keyCode){
29137                         fn.call(scope || window, dlg, k, e);
29138                     }
29139                 }
29140             }
29141         };
29142         this.on("keydown", handler);
29143         return this;
29144     },
29145
29146     /**
29147      * Returns the TabPanel component (creates it if it doesn't exist).
29148      * Note: If you wish to simply check for the existence of tabs without creating them,
29149      * check for a null 'tabs' property.
29150      * @return {Roo.TabPanel} The tabs component
29151      */
29152     getTabs : function(){
29153         if(!this.tabs){
29154             this.el.addClass("x-dlg-auto-tabs");
29155             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29156             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29157         }
29158         return this.tabs;
29159     },
29160
29161     /**
29162      * Adds a button to the footer section of the dialog.
29163      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29164      * object or a valid Roo.DomHelper element config
29165      * @param {Function} handler The function called when the button is clicked
29166      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29167      * @return {Roo.Button} The new button
29168      */
29169     addButton : function(config, handler, scope){
29170         var dh = Roo.DomHelper;
29171         if(!this.footer){
29172             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29173         }
29174         if(!this.btnContainer){
29175             var tb = this.footer.createChild({
29176
29177                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29178                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29179             }, null, true);
29180             this.btnContainer = tb.firstChild.firstChild.firstChild;
29181         }
29182         var bconfig = {
29183             handler: handler,
29184             scope: scope,
29185             minWidth: this.minButtonWidth,
29186             hideParent:true
29187         };
29188         if(typeof config == "string"){
29189             bconfig.text = config;
29190         }else{
29191             if(config.tag){
29192                 bconfig.dhconfig = config;
29193             }else{
29194                 Roo.apply(bconfig, config);
29195             }
29196         }
29197         var fc = false;
29198         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29199             bconfig.position = Math.max(0, bconfig.position);
29200             fc = this.btnContainer.childNodes[bconfig.position];
29201         }
29202          
29203         var btn = new Roo.Button(
29204             fc ? 
29205                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29206                 : this.btnContainer.appendChild(document.createElement("td")),
29207             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29208             bconfig
29209         );
29210         this.syncBodyHeight();
29211         if(!this.buttons){
29212             /**
29213              * Array of all the buttons that have been added to this dialog via addButton
29214              * @type Array
29215              */
29216             this.buttons = [];
29217         }
29218         this.buttons.push(btn);
29219         return btn;
29220     },
29221
29222     /**
29223      * Sets the default button to be focused when the dialog is displayed.
29224      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29225      * @return {Roo.BasicDialog} this
29226      */
29227     setDefaultButton : function(btn){
29228         this.defaultButton = btn;
29229         return this;
29230     },
29231
29232     // private
29233     getHeaderFooterHeight : function(safe){
29234         var height = 0;
29235         if(this.header){
29236            height += this.header.getHeight();
29237         }
29238         if(this.footer){
29239            var fm = this.footer.getMargins();
29240             height += (this.footer.getHeight()+fm.top+fm.bottom);
29241         }
29242         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29243         height += this.centerBg.getPadding("tb");
29244         return height;
29245     },
29246
29247     // private
29248     syncBodyHeight : function(){
29249         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29250         var height = this.size.height - this.getHeaderFooterHeight(false);
29251         bd.setHeight(height-bd.getMargins("tb"));
29252         var hh = this.header.getHeight();
29253         var h = this.size.height-hh;
29254         cb.setHeight(h);
29255         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29256         bw.setHeight(h-cb.getPadding("tb"));
29257         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29258         bd.setWidth(bw.getWidth(true));
29259         if(this.tabs){
29260             this.tabs.syncHeight();
29261             if(Roo.isIE){
29262                 this.tabs.el.repaint();
29263             }
29264         }
29265     },
29266
29267     /**
29268      * Restores the previous state of the dialog if Roo.state is configured.
29269      * @return {Roo.BasicDialog} this
29270      */
29271     restoreState : function(){
29272         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29273         if(box && box.width){
29274             this.xy = [box.x, box.y];
29275             this.resizeTo(box.width, box.height);
29276         }
29277         return this;
29278     },
29279
29280     // private
29281     beforeShow : function(){
29282         this.expand();
29283         if(this.fixedcenter){
29284             this.xy = this.el.getCenterXY(true);
29285         }
29286         if(this.modal){
29287             Roo.get(document.body).addClass("x-body-masked");
29288             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29289             this.mask.show();
29290         }
29291         this.constrainXY();
29292     },
29293
29294     // private
29295     animShow : function(){
29296         var b = Roo.get(this.animateTarget).getBox();
29297         this.proxy.setSize(b.width, b.height);
29298         this.proxy.setLocation(b.x, b.y);
29299         this.proxy.show();
29300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29301                     true, .35, this.showEl.createDelegate(this));
29302     },
29303
29304     /**
29305      * Shows the dialog.
29306      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29307      * @return {Roo.BasicDialog} this
29308      */
29309     show : function(animateTarget){
29310         if (this.fireEvent("beforeshow", this) === false){
29311             return;
29312         }
29313         if(this.syncHeightBeforeShow){
29314             this.syncBodyHeight();
29315         }else if(this.firstShow){
29316             this.firstShow = false;
29317             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29318         }
29319         this.animateTarget = animateTarget || this.animateTarget;
29320         if(!this.el.isVisible()){
29321             this.beforeShow();
29322             if(this.animateTarget && Roo.get(this.animateTarget)){
29323                 this.animShow();
29324             }else{
29325                 this.showEl();
29326             }
29327         }
29328         return this;
29329     },
29330
29331     // private
29332     showEl : function(){
29333         this.proxy.hide();
29334         this.el.setXY(this.xy);
29335         this.el.show();
29336         this.adjustAssets(true);
29337         this.toFront();
29338         this.focus();
29339         // IE peekaboo bug - fix found by Dave Fenwick
29340         if(Roo.isIE){
29341             this.el.repaint();
29342         }
29343         this.fireEvent("show", this);
29344     },
29345
29346     /**
29347      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29348      * dialog itself will receive focus.
29349      */
29350     focus : function(){
29351         if(this.defaultButton){
29352             this.defaultButton.focus();
29353         }else{
29354             this.focusEl.focus();
29355         }
29356     },
29357
29358     // private
29359     constrainXY : function(){
29360         if(this.constraintoviewport !== false){
29361             if(!this.viewSize){
29362                 if(this.container){
29363                     var s = this.container.getSize();
29364                     this.viewSize = [s.width, s.height];
29365                 }else{
29366                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29367                 }
29368             }
29369             var s = Roo.get(this.container||document).getScroll();
29370
29371             var x = this.xy[0], y = this.xy[1];
29372             var w = this.size.width, h = this.size.height;
29373             var vw = this.viewSize[0], vh = this.viewSize[1];
29374             // only move it if it needs it
29375             var moved = false;
29376             // first validate right/bottom
29377             if(x + w > vw+s.left){
29378                 x = vw - w;
29379                 moved = true;
29380             }
29381             if(y + h > vh+s.top){
29382                 y = vh - h;
29383                 moved = true;
29384             }
29385             // then make sure top/left isn't negative
29386             if(x < s.left){
29387                 x = s.left;
29388                 moved = true;
29389             }
29390             if(y < s.top){
29391                 y = s.top;
29392                 moved = true;
29393             }
29394             if(moved){
29395                 // cache xy
29396                 this.xy = [x, y];
29397                 if(this.isVisible()){
29398                     this.el.setLocation(x, y);
29399                     this.adjustAssets();
29400                 }
29401             }
29402         }
29403     },
29404
29405     // private
29406     onDrag : function(){
29407         if(!this.proxyDrag){
29408             this.xy = this.el.getXY();
29409             this.adjustAssets();
29410         }
29411     },
29412
29413     // private
29414     adjustAssets : function(doShow){
29415         var x = this.xy[0], y = this.xy[1];
29416         var w = this.size.width, h = this.size.height;
29417         if(doShow === true){
29418             if(this.shadow){
29419                 this.shadow.show(this.el);
29420             }
29421             if(this.shim){
29422                 this.shim.show();
29423             }
29424         }
29425         if(this.shadow && this.shadow.isVisible()){
29426             this.shadow.show(this.el);
29427         }
29428         if(this.shim && this.shim.isVisible()){
29429             this.shim.setBounds(x, y, w, h);
29430         }
29431     },
29432
29433     // private
29434     adjustViewport : function(w, h){
29435         if(!w || !h){
29436             w = Roo.lib.Dom.getViewWidth();
29437             h = Roo.lib.Dom.getViewHeight();
29438         }
29439         // cache the size
29440         this.viewSize = [w, h];
29441         if(this.modal && this.mask.isVisible()){
29442             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29443             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29444         }
29445         if(this.isVisible()){
29446             this.constrainXY();
29447         }
29448     },
29449
29450     /**
29451      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29452      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29453      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29454      */
29455     destroy : function(removeEl){
29456         if(this.isVisible()){
29457             this.animateTarget = null;
29458             this.hide();
29459         }
29460         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29461         if(this.tabs){
29462             this.tabs.destroy(removeEl);
29463         }
29464         Roo.destroy(
29465              this.shim,
29466              this.proxy,
29467              this.resizer,
29468              this.close,
29469              this.mask
29470         );
29471         if(this.dd){
29472             this.dd.unreg();
29473         }
29474         if(this.buttons){
29475            for(var i = 0, len = this.buttons.length; i < len; i++){
29476                this.buttons[i].destroy();
29477            }
29478         }
29479         this.el.removeAllListeners();
29480         if(removeEl === true){
29481             this.el.update("");
29482             this.el.remove();
29483         }
29484         Roo.DialogManager.unregister(this);
29485     },
29486
29487     // private
29488     startMove : function(){
29489         if(this.proxyDrag){
29490             this.proxy.show();
29491         }
29492         if(this.constraintoviewport !== false){
29493             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29494         }
29495     },
29496
29497     // private
29498     endMove : function(){
29499         if(!this.proxyDrag){
29500             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29501         }else{
29502             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29503             this.proxy.hide();
29504         }
29505         this.refreshSize();
29506         this.adjustAssets();
29507         this.focus();
29508         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29509     },
29510
29511     /**
29512      * Brings this dialog to the front of any other visible dialogs
29513      * @return {Roo.BasicDialog} this
29514      */
29515     toFront : function(){
29516         Roo.DialogManager.bringToFront(this);
29517         return this;
29518     },
29519
29520     /**
29521      * Sends this dialog to the back (under) of any other visible dialogs
29522      * @return {Roo.BasicDialog} this
29523      */
29524     toBack : function(){
29525         Roo.DialogManager.sendToBack(this);
29526         return this;
29527     },
29528
29529     /**
29530      * Centers this dialog in the viewport
29531      * @return {Roo.BasicDialog} this
29532      */
29533     center : function(){
29534         var xy = this.el.getCenterXY(true);
29535         this.moveTo(xy[0], xy[1]);
29536         return this;
29537     },
29538
29539     /**
29540      * Moves the dialog's top-left corner to the specified point
29541      * @param {Number} x
29542      * @param {Number} y
29543      * @return {Roo.BasicDialog} this
29544      */
29545     moveTo : function(x, y){
29546         this.xy = [x,y];
29547         if(this.isVisible()){
29548             this.el.setXY(this.xy);
29549             this.adjustAssets();
29550         }
29551         return this;
29552     },
29553
29554     /**
29555      * Aligns the dialog to the specified element
29556      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29557      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29558      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29559      * @return {Roo.BasicDialog} this
29560      */
29561     alignTo : function(element, position, offsets){
29562         this.xy = this.el.getAlignToXY(element, position, offsets);
29563         if(this.isVisible()){
29564             this.el.setXY(this.xy);
29565             this.adjustAssets();
29566         }
29567         return this;
29568     },
29569
29570     /**
29571      * Anchors an element to another element and realigns it when the window is resized.
29572      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29573      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29574      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29575      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29576      * is a number, it is used as the buffer delay (defaults to 50ms).
29577      * @return {Roo.BasicDialog} this
29578      */
29579     anchorTo : function(el, alignment, offsets, monitorScroll){
29580         var action = function(){
29581             this.alignTo(el, alignment, offsets);
29582         };
29583         Roo.EventManager.onWindowResize(action, this);
29584         var tm = typeof monitorScroll;
29585         if(tm != 'undefined'){
29586             Roo.EventManager.on(window, 'scroll', action, this,
29587                 {buffer: tm == 'number' ? monitorScroll : 50});
29588         }
29589         action.call(this);
29590         return this;
29591     },
29592
29593     /**
29594      * Returns true if the dialog is visible
29595      * @return {Boolean}
29596      */
29597     isVisible : function(){
29598         return this.el.isVisible();
29599     },
29600
29601     // private
29602     animHide : function(callback){
29603         var b = Roo.get(this.animateTarget).getBox();
29604         this.proxy.show();
29605         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29606         this.el.hide();
29607         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29608                     this.hideEl.createDelegate(this, [callback]));
29609     },
29610
29611     /**
29612      * Hides the dialog.
29613      * @param {Function} callback (optional) Function to call when the dialog is hidden
29614      * @return {Roo.BasicDialog} this
29615      */
29616     hide : function(callback){
29617         if (this.fireEvent("beforehide", this) === false){
29618             return;
29619         }
29620         if(this.shadow){
29621             this.shadow.hide();
29622         }
29623         if(this.shim) {
29624           this.shim.hide();
29625         }
29626         // sometimes animateTarget seems to get set.. causing problems...
29627         // this just double checks..
29628         if(this.animateTarget && Roo.get(this.animateTarget)) {
29629            this.animHide(callback);
29630         }else{
29631             this.el.hide();
29632             this.hideEl(callback);
29633         }
29634         return this;
29635     },
29636
29637     // private
29638     hideEl : function(callback){
29639         this.proxy.hide();
29640         if(this.modal){
29641             this.mask.hide();
29642             Roo.get(document.body).removeClass("x-body-masked");
29643         }
29644         this.fireEvent("hide", this);
29645         if(typeof callback == "function"){
29646             callback();
29647         }
29648     },
29649
29650     // private
29651     hideAction : function(){
29652         this.setLeft("-10000px");
29653         this.setTop("-10000px");
29654         this.setStyle("visibility", "hidden");
29655     },
29656
29657     // private
29658     refreshSize : function(){
29659         this.size = this.el.getSize();
29660         this.xy = this.el.getXY();
29661         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29662     },
29663
29664     // private
29665     // z-index is managed by the DialogManager and may be overwritten at any time
29666     setZIndex : function(index){
29667         if(this.modal){
29668             this.mask.setStyle("z-index", index);
29669         }
29670         if(this.shim){
29671             this.shim.setStyle("z-index", ++index);
29672         }
29673         if(this.shadow){
29674             this.shadow.setZIndex(++index);
29675         }
29676         this.el.setStyle("z-index", ++index);
29677         if(this.proxy){
29678             this.proxy.setStyle("z-index", ++index);
29679         }
29680         if(this.resizer){
29681             this.resizer.proxy.setStyle("z-index", ++index);
29682         }
29683
29684         this.lastZIndex = index;
29685     },
29686
29687     /**
29688      * Returns the element for this dialog
29689      * @return {Roo.Element} The underlying dialog Element
29690      */
29691     getEl : function(){
29692         return this.el;
29693     }
29694 });
29695
29696 /**
29697  * @class Roo.DialogManager
29698  * Provides global access to BasicDialogs that have been created and
29699  * support for z-indexing (layering) multiple open dialogs.
29700  */
29701 Roo.DialogManager = function(){
29702     var list = {};
29703     var accessList = [];
29704     var front = null;
29705
29706     // private
29707     var sortDialogs = function(d1, d2){
29708         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29709     };
29710
29711     // private
29712     var orderDialogs = function(){
29713         accessList.sort(sortDialogs);
29714         var seed = Roo.DialogManager.zseed;
29715         for(var i = 0, len = accessList.length; i < len; i++){
29716             var dlg = accessList[i];
29717             if(dlg){
29718                 dlg.setZIndex(seed + (i*10));
29719             }
29720         }
29721     };
29722
29723     return {
29724         /**
29725          * The starting z-index for BasicDialogs (defaults to 9000)
29726          * @type Number The z-index value
29727          */
29728         zseed : 9000,
29729
29730         // private
29731         register : function(dlg){
29732             list[dlg.id] = dlg;
29733             accessList.push(dlg);
29734         },
29735
29736         // private
29737         unregister : function(dlg){
29738             delete list[dlg.id];
29739             var i=0;
29740             var len=0;
29741             if(!accessList.indexOf){
29742                 for(  i = 0, len = accessList.length; i < len; i++){
29743                     if(accessList[i] == dlg){
29744                         accessList.splice(i, 1);
29745                         return;
29746                     }
29747                 }
29748             }else{
29749                  i = accessList.indexOf(dlg);
29750                 if(i != -1){
29751                     accessList.splice(i, 1);
29752                 }
29753             }
29754         },
29755
29756         /**
29757          * Gets a registered dialog by id
29758          * @param {String/Object} id The id of the dialog or a dialog
29759          * @return {Roo.BasicDialog} this
29760          */
29761         get : function(id){
29762             return typeof id == "object" ? id : list[id];
29763         },
29764
29765         /**
29766          * Brings the specified dialog to the front
29767          * @param {String/Object} dlg The id of the dialog or a dialog
29768          * @return {Roo.BasicDialog} this
29769          */
29770         bringToFront : function(dlg){
29771             dlg = this.get(dlg);
29772             if(dlg != front){
29773                 front = dlg;
29774                 dlg._lastAccess = new Date().getTime();
29775                 orderDialogs();
29776             }
29777             return dlg;
29778         },
29779
29780         /**
29781          * Sends the specified dialog to the back
29782          * @param {String/Object} dlg The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         sendToBack : function(dlg){
29786             dlg = this.get(dlg);
29787             dlg._lastAccess = -(new Date().getTime());
29788             orderDialogs();
29789             return dlg;
29790         },
29791
29792         /**
29793          * Hides all dialogs
29794          */
29795         hideAll : function(){
29796             for(var id in list){
29797                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29798                     list[id].hide();
29799                 }
29800             }
29801         }
29802     };
29803 }();
29804
29805 /**
29806  * @class Roo.LayoutDialog
29807  * @extends Roo.BasicDialog
29808  * Dialog which provides adjustments for working with a layout in a Dialog.
29809  * Add your necessary layout config options to the dialog's config.<br>
29810  * Example usage (including a nested layout):
29811  * <pre><code>
29812 if(!dialog){
29813     dialog = new Roo.LayoutDialog("download-dlg", {
29814         modal: true,
29815         width:600,
29816         height:450,
29817         shadow:true,
29818         minWidth:500,
29819         minHeight:350,
29820         autoTabs:true,
29821         proxyDrag:true,
29822         // layout config merges with the dialog config
29823         center:{
29824             tabPosition: "top",
29825             alwaysShowTabs: true
29826         }
29827     });
29828     dialog.addKeyListener(27, dialog.hide, dialog);
29829     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29830     dialog.addButton("Build It!", this.getDownload, this);
29831
29832     // we can even add nested layouts
29833     var innerLayout = new Roo.BorderLayout("dl-inner", {
29834         east: {
29835             initialSize: 200,
29836             autoScroll:true,
29837             split:true
29838         },
29839         center: {
29840             autoScroll:true
29841         }
29842     });
29843     innerLayout.beginUpdate();
29844     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29845     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29846     innerLayout.endUpdate(true);
29847
29848     var layout = dialog.getLayout();
29849     layout.beginUpdate();
29850     layout.add("center", new Roo.ContentPanel("standard-panel",
29851                         {title: "Download the Source", fitToFrame:true}));
29852     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29853                {title: "Build your own roo.js"}));
29854     layout.getRegion("center").showPanel(sp);
29855     layout.endUpdate();
29856 }
29857 </code></pre>
29858     * @constructor
29859     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29860     * @param {Object} config configuration options
29861   */
29862 Roo.LayoutDialog = function(el, cfg){
29863     
29864     var config=  cfg;
29865     if (typeof(cfg) == 'undefined') {
29866         config = Roo.apply({}, el);
29867         // not sure why we use documentElement here.. - it should always be body.
29868         // IE7 borks horribly if we use documentElement.
29869         // webkit also does not like documentElement - it creates a body element...
29870         el = Roo.get( document.body || document.documentElement ).createChild();
29871         //config.autoCreate = true;
29872     }
29873     
29874     
29875     config.autoTabs = false;
29876     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29877     this.body.setStyle({overflow:"hidden", position:"relative"});
29878     this.layout = new Roo.BorderLayout(this.body.dom, config);
29879     this.layout.monitorWindowResize = false;
29880     this.el.addClass("x-dlg-auto-layout");
29881     // fix case when center region overwrites center function
29882     this.center = Roo.BasicDialog.prototype.center;
29883     this.on("show", this.layout.layout, this.layout, true);
29884     if (config.items) {
29885         var xitems = config.items;
29886         delete config.items;
29887         Roo.each(xitems, this.addxtype, this);
29888     }
29889     
29890     
29891 };
29892 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29893     /**
29894      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29895      * @deprecated
29896      */
29897     endUpdate : function(){
29898         this.layout.endUpdate();
29899     },
29900
29901     /**
29902      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29903      *  @deprecated
29904      */
29905     beginUpdate : function(){
29906         this.layout.beginUpdate();
29907     },
29908
29909     /**
29910      * Get the BorderLayout for this dialog
29911      * @return {Roo.BorderLayout}
29912      */
29913     getLayout : function(){
29914         return this.layout;
29915     },
29916
29917     showEl : function(){
29918         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29919         if(Roo.isIE7){
29920             this.layout.layout();
29921         }
29922     },
29923
29924     // private
29925     // Use the syncHeightBeforeShow config option to control this automatically
29926     syncBodyHeight : function(){
29927         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29928         if(this.layout){this.layout.layout();}
29929     },
29930     
29931       /**
29932      * Add an xtype element (actually adds to the layout.)
29933      * @return {Object} xdata xtype object data.
29934      */
29935     
29936     addxtype : function(c) {
29937         return this.layout.addxtype(c);
29938     }
29939 });/*
29940  * Based on:
29941  * Ext JS Library 1.1.1
29942  * Copyright(c) 2006-2007, Ext JS, LLC.
29943  *
29944  * Originally Released Under LGPL - original licence link has changed is not relivant.
29945  *
29946  * Fork - LGPL
29947  * <script type="text/javascript">
29948  */
29949  
29950 /**
29951  * @class Roo.MessageBox
29952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29953  * Example usage:
29954  *<pre><code>
29955 // Basic alert:
29956 Roo.Msg.alert('Status', 'Changes saved successfully.');
29957
29958 // Prompt for user data:
29959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29960     if (btn == 'ok'){
29961         // process text value...
29962     }
29963 });
29964
29965 // Show a dialog using config options:
29966 Roo.Msg.show({
29967    title:'Save Changes?',
29968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29969    buttons: Roo.Msg.YESNOCANCEL,
29970    fn: processResult,
29971    animEl: 'elId'
29972 });
29973 </code></pre>
29974  * @singleton
29975  */
29976 Roo.MessageBox = function(){
29977     var dlg, opt, mask, waitTimer;
29978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29979     var buttons, activeTextEl, bwidth;
29980
29981     // private
29982     var handleButton = function(button){
29983         dlg.hide();
29984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29985     };
29986
29987     // private
29988     var handleHide = function(){
29989         if(opt && opt.cls){
29990             dlg.el.removeClass(opt.cls);
29991         }
29992         if(waitTimer){
29993             Roo.TaskMgr.stop(waitTimer);
29994             waitTimer = null;
29995         }
29996     };
29997
29998     // private
29999     var updateButtons = function(b){
30000         var width = 0;
30001         if(!b){
30002             buttons["ok"].hide();
30003             buttons["cancel"].hide();
30004             buttons["yes"].hide();
30005             buttons["no"].hide();
30006             dlg.footer.dom.style.display = 'none';
30007             return width;
30008         }
30009         dlg.footer.dom.style.display = '';
30010         for(var k in buttons){
30011             if(typeof buttons[k] != "function"){
30012                 if(b[k]){
30013                     buttons[k].show();
30014                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30015                     width += buttons[k].el.getWidth()+15;
30016                 }else{
30017                     buttons[k].hide();
30018                 }
30019             }
30020         }
30021         return width;
30022     };
30023
30024     // private
30025     var handleEsc = function(d, k, e){
30026         if(opt && opt.closable !== false){
30027             dlg.hide();
30028         }
30029         if(e){
30030             e.stopEvent();
30031         }
30032     };
30033
30034     return {
30035         /**
30036          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30037          * @return {Roo.BasicDialog} The BasicDialog element
30038          */
30039         getDialog : function(){
30040            if(!dlg){
30041                 dlg = new Roo.BasicDialog("x-msg-box", {
30042                     autoCreate : true,
30043                     shadow: true,
30044                     draggable: true,
30045                     resizable:false,
30046                     constraintoviewport:false,
30047                     fixedcenter:true,
30048                     collapsible : false,
30049                     shim:true,
30050                     modal: true,
30051                     width:400, height:100,
30052                     buttonAlign:"center",
30053                     closeClick : function(){
30054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30055                             handleButton("no");
30056                         }else{
30057                             handleButton("cancel");
30058                         }
30059                     }
30060                 });
30061                 dlg.on("hide", handleHide);
30062                 mask = dlg.mask;
30063                 dlg.addKeyListener(27, handleEsc);
30064                 buttons = {};
30065                 var bt = this.buttonText;
30066                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30067                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30068                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30069                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30070                 bodyEl = dlg.body.createChild({
30071
30072                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30073                 });
30074                 msgEl = bodyEl.dom.firstChild;
30075                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30076                 textboxEl.enableDisplayMode();
30077                 textboxEl.addKeyListener([10,13], function(){
30078                     if(dlg.isVisible() && opt && opt.buttons){
30079                         if(opt.buttons.ok){
30080                             handleButton("ok");
30081                         }else if(opt.buttons.yes){
30082                             handleButton("yes");
30083                         }
30084                     }
30085                 });
30086                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30087                 textareaEl.enableDisplayMode();
30088                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30089                 progressEl.enableDisplayMode();
30090                 var pf = progressEl.dom.firstChild;
30091                 if (pf) {
30092                     pp = Roo.get(pf.firstChild);
30093                     pp.setHeight(pf.offsetHeight);
30094                 }
30095                 
30096             }
30097             return dlg;
30098         },
30099
30100         /**
30101          * Updates the message box body text
30102          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30103          * the XHTML-compliant non-breaking space character '&amp;#160;')
30104          * @return {Roo.MessageBox} This message box
30105          */
30106         updateText : function(text){
30107             if(!dlg.isVisible() && !opt.width){
30108                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30109             }
30110             msgEl.innerHTML = text || '&#160;';
30111       
30112             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30113             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30114             var w = Math.max(
30115                     Math.min(opt.width || cw , this.maxWidth), 
30116                     Math.max(opt.minWidth || this.minWidth, bwidth)
30117             );
30118             if(opt.prompt){
30119                 activeTextEl.setWidth(w);
30120             }
30121             if(dlg.isVisible()){
30122                 dlg.fixedcenter = false;
30123             }
30124             // to big, make it scroll. = But as usual stupid IE does not support
30125             // !important..
30126             
30127             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30128                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30129                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30130             } else {
30131                 bodyEl.dom.style.height = '';
30132                 bodyEl.dom.style.overflowY = '';
30133             }
30134             if (cw > w) {
30135                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30136             } else {
30137                 bodyEl.dom.style.overflowX = '';
30138             }
30139             
30140             dlg.setContentSize(w, bodyEl.getHeight());
30141             if(dlg.isVisible()){
30142                 dlg.fixedcenter = true;
30143             }
30144             return this;
30145         },
30146
30147         /**
30148          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30149          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30150          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30151          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30152          * @return {Roo.MessageBox} This message box
30153          */
30154         updateProgress : function(value, text){
30155             if(text){
30156                 this.updateText(text);
30157             }
30158             if (pp) { // weird bug on my firefox - for some reason this is not defined
30159                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30160             }
30161             return this;
30162         },        
30163
30164         /**
30165          * Returns true if the message box is currently displayed
30166          * @return {Boolean} True if the message box is visible, else false
30167          */
30168         isVisible : function(){
30169             return dlg && dlg.isVisible();  
30170         },
30171
30172         /**
30173          * Hides the message box if it is displayed
30174          */
30175         hide : function(){
30176             if(this.isVisible()){
30177                 dlg.hide();
30178             }  
30179         },
30180
30181         /**
30182          * Displays a new message box, or reinitializes an existing message box, based on the config options
30183          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30184          * The following config object properties are supported:
30185          * <pre>
30186 Property    Type             Description
30187 ----------  ---------------  ------------------------------------------------------------------------------------
30188 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30189                                    closes (defaults to undefined)
30190 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30191                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30192 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30193                                    progress and wait dialogs will ignore this property and always hide the
30194                                    close button as they can only be closed programmatically.
30195 cls               String           A custom CSS class to apply to the message box element
30196 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30197                                    displayed (defaults to 75)
30198 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30199                                    function will be btn (the name of the button that was clicked, if applicable,
30200                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30201                                    Progress and wait dialogs will ignore this option since they do not respond to
30202                                    user actions and can only be closed programmatically, so any required function
30203                                    should be called by the same code after it closes the dialog.
30204 icon              String           A CSS class that provides a background image to be used as an icon for
30205                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30206 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30207 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30208 modal             Boolean          False to allow user interaction with the page while the message box is
30209                                    displayed (defaults to true)
30210 msg               String           A string that will replace the existing message box body text (defaults
30211                                    to the XHTML-compliant non-breaking space character '&#160;')
30212 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30213 progress          Boolean          True to display a progress bar (defaults to false)
30214 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30215 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30216 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30217 title             String           The title text
30218 value             String           The string value to set into the active textbox element if displayed
30219 wait              Boolean          True to display a progress bar (defaults to false)
30220 width             Number           The width of the dialog in pixels
30221 </pre>
30222          *
30223          * Example usage:
30224          * <pre><code>
30225 Roo.Msg.show({
30226    title: 'Address',
30227    msg: 'Please enter your address:',
30228    width: 300,
30229    buttons: Roo.MessageBox.OKCANCEL,
30230    multiline: true,
30231    fn: saveAddress,
30232    animEl: 'addAddressBtn'
30233 });
30234 </code></pre>
30235          * @param {Object} config Configuration options
30236          * @return {Roo.MessageBox} This message box
30237          */
30238         show : function(options)
30239         {
30240             
30241             // this causes nightmares if you show one dialog after another
30242             // especially on callbacks..
30243              
30244             if(this.isVisible()){
30245                 
30246                 this.hide();
30247                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30248                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30249                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30250                 
30251             }
30252             var d = this.getDialog();
30253             opt = options;
30254             d.setTitle(opt.title || "&#160;");
30255             d.close.setDisplayed(opt.closable !== false);
30256             activeTextEl = textboxEl;
30257             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30258             if(opt.prompt){
30259                 if(opt.multiline){
30260                     textboxEl.hide();
30261                     textareaEl.show();
30262                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30263                         opt.multiline : this.defaultTextHeight);
30264                     activeTextEl = textareaEl;
30265                 }else{
30266                     textboxEl.show();
30267                     textareaEl.hide();
30268                 }
30269             }else{
30270                 textboxEl.hide();
30271                 textareaEl.hide();
30272             }
30273             progressEl.setDisplayed(opt.progress === true);
30274             this.updateProgress(0);
30275             activeTextEl.dom.value = opt.value || "";
30276             if(opt.prompt){
30277                 dlg.setDefaultButton(activeTextEl);
30278             }else{
30279                 var bs = opt.buttons;
30280                 var db = null;
30281                 if(bs && bs.ok){
30282                     db = buttons["ok"];
30283                 }else if(bs && bs.yes){
30284                     db = buttons["yes"];
30285                 }
30286                 dlg.setDefaultButton(db);
30287             }
30288             bwidth = updateButtons(opt.buttons);
30289             this.updateText(opt.msg);
30290             if(opt.cls){
30291                 d.el.addClass(opt.cls);
30292             }
30293             d.proxyDrag = opt.proxyDrag === true;
30294             d.modal = opt.modal !== false;
30295             d.mask = opt.modal !== false ? mask : false;
30296             if(!d.isVisible()){
30297                 // force it to the end of the z-index stack so it gets a cursor in FF
30298                 document.body.appendChild(dlg.el.dom);
30299                 d.animateTarget = null;
30300                 d.show(options.animEl);
30301             }
30302             return this;
30303         },
30304
30305         /**
30306          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30307          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30308          * and closing the message box when the process is complete.
30309          * @param {String} title The title bar text
30310          * @param {String} msg The message box body text
30311          * @return {Roo.MessageBox} This message box
30312          */
30313         progress : function(title, msg){
30314             this.show({
30315                 title : title,
30316                 msg : msg,
30317                 buttons: false,
30318                 progress:true,
30319                 closable:false,
30320                 minWidth: this.minProgressWidth,
30321                 modal : true
30322             });
30323             return this;
30324         },
30325
30326         /**
30327          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30328          * If a callback function is passed it will be called after the user clicks the button, and the
30329          * id of the button that was clicked will be passed as the only parameter to the callback
30330          * (could also be the top-right close button).
30331          * @param {String} title The title bar text
30332          * @param {String} msg The message box body text
30333          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30334          * @param {Object} scope (optional) The scope of the callback function
30335          * @return {Roo.MessageBox} This message box
30336          */
30337         alert : function(title, msg, fn, scope){
30338             this.show({
30339                 title : title,
30340                 msg : msg,
30341                 buttons: this.OK,
30342                 fn: fn,
30343                 scope : scope,
30344                 modal : true
30345             });
30346             return this;
30347         },
30348
30349         /**
30350          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30351          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30352          * You are responsible for closing the message box when the process is complete.
30353          * @param {String} msg The message box body text
30354          * @param {String} title (optional) The title bar text
30355          * @return {Roo.MessageBox} This message box
30356          */
30357         wait : function(msg, title){
30358             this.show({
30359                 title : title,
30360                 msg : msg,
30361                 buttons: false,
30362                 closable:false,
30363                 progress:true,
30364                 modal:true,
30365                 width:300,
30366                 wait:true
30367             });
30368             waitTimer = Roo.TaskMgr.start({
30369                 run: function(i){
30370                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30371                 },
30372                 interval: 1000
30373             });
30374             return this;
30375         },
30376
30377         /**
30378          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30379          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30380          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30381          * @param {String} title The title bar text
30382          * @param {String} msg The message box body text
30383          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30384          * @param {Object} scope (optional) The scope of the callback function
30385          * @return {Roo.MessageBox} This message box
30386          */
30387         confirm : function(title, msg, fn, scope){
30388             this.show({
30389                 title : title,
30390                 msg : msg,
30391                 buttons: this.YESNO,
30392                 fn: fn,
30393                 scope : scope,
30394                 modal : true
30395             });
30396             return this;
30397         },
30398
30399         /**
30400          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30401          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30402          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30403          * (could also be the top-right close button) and the text that was entered will be passed as the two
30404          * parameters to the callback.
30405          * @param {String} title The title bar text
30406          * @param {String} msg The message box body text
30407          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30408          * @param {Object} scope (optional) The scope of the callback function
30409          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30410          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30411          * @return {Roo.MessageBox} This message box
30412          */
30413         prompt : function(title, msg, fn, scope, multiline){
30414             this.show({
30415                 title : title,
30416                 msg : msg,
30417                 buttons: this.OKCANCEL,
30418                 fn: fn,
30419                 minWidth:250,
30420                 scope : scope,
30421                 prompt:true,
30422                 multiline: multiline,
30423                 modal : true
30424             });
30425             return this;
30426         },
30427
30428         /**
30429          * Button config that displays a single OK button
30430          * @type Object
30431          */
30432         OK : {ok:true},
30433         /**
30434          * Button config that displays Yes and No buttons
30435          * @type Object
30436          */
30437         YESNO : {yes:true, no:true},
30438         /**
30439          * Button config that displays OK and Cancel buttons
30440          * @type Object
30441          */
30442         OKCANCEL : {ok:true, cancel:true},
30443         /**
30444          * Button config that displays Yes, No and Cancel buttons
30445          * @type Object
30446          */
30447         YESNOCANCEL : {yes:true, no:true, cancel:true},
30448
30449         /**
30450          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30451          * @type Number
30452          */
30453         defaultTextHeight : 75,
30454         /**
30455          * The maximum width in pixels of the message box (defaults to 600)
30456          * @type Number
30457          */
30458         maxWidth : 600,
30459         /**
30460          * The minimum width in pixels of the message box (defaults to 100)
30461          * @type Number
30462          */
30463         minWidth : 100,
30464         /**
30465          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30466          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30467          * @type Number
30468          */
30469         minProgressWidth : 250,
30470         /**
30471          * An object containing the default button text strings that can be overriden for localized language support.
30472          * Supported properties are: ok, cancel, yes and no.
30473          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30474          * @type Object
30475          */
30476         buttonText : {
30477             ok : "OK",
30478             cancel : "Cancel",
30479             yes : "Yes",
30480             no : "No"
30481         }
30482     };
30483 }();
30484
30485 /**
30486  * Shorthand for {@link Roo.MessageBox}
30487  */
30488 Roo.Msg = Roo.MessageBox;/*
30489  * Based on:
30490  * Ext JS Library 1.1.1
30491  * Copyright(c) 2006-2007, Ext JS, LLC.
30492  *
30493  * Originally Released Under LGPL - original licence link has changed is not relivant.
30494  *
30495  * Fork - LGPL
30496  * <script type="text/javascript">
30497  */
30498 /**
30499  * @class Roo.QuickTips
30500  * Provides attractive and customizable tooltips for any element.
30501  * @singleton
30502  */
30503 Roo.QuickTips = function(){
30504     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30505     var ce, bd, xy, dd;
30506     var visible = false, disabled = true, inited = false;
30507     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30508     
30509     var onOver = function(e){
30510         if(disabled){
30511             return;
30512         }
30513         var t = e.getTarget();
30514         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30515             return;
30516         }
30517         if(ce && t == ce.el){
30518             clearTimeout(hideProc);
30519             return;
30520         }
30521         if(t && tagEls[t.id]){
30522             tagEls[t.id].el = t;
30523             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30524             return;
30525         }
30526         var ttp, et = Roo.fly(t);
30527         var ns = cfg.namespace;
30528         if(tm.interceptTitles && t.title){
30529             ttp = t.title;
30530             t.qtip = ttp;
30531             t.removeAttribute("title");
30532             e.preventDefault();
30533         }else{
30534             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30535         }
30536         if(ttp){
30537             showProc = show.defer(tm.showDelay, tm, [{
30538                 el: t, 
30539                 text: ttp, 
30540                 width: et.getAttributeNS(ns, cfg.width),
30541                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30542                 title: et.getAttributeNS(ns, cfg.title),
30543                     cls: et.getAttributeNS(ns, cfg.cls)
30544             }]);
30545         }
30546     };
30547     
30548     var onOut = function(e){
30549         clearTimeout(showProc);
30550         var t = e.getTarget();
30551         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30552             hideProc = setTimeout(hide, tm.hideDelay);
30553         }
30554     };
30555     
30556     var onMove = function(e){
30557         if(disabled){
30558             return;
30559         }
30560         xy = e.getXY();
30561         xy[1] += 18;
30562         if(tm.trackMouse && ce){
30563             el.setXY(xy);
30564         }
30565     };
30566     
30567     var onDown = function(e){
30568         clearTimeout(showProc);
30569         clearTimeout(hideProc);
30570         if(!e.within(el)){
30571             if(tm.hideOnClick){
30572                 hide();
30573                 tm.disable();
30574                 tm.enable.defer(100, tm);
30575             }
30576         }
30577     };
30578     
30579     var getPad = function(){
30580         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30581     };
30582
30583     var show = function(o){
30584         if(disabled){
30585             return;
30586         }
30587         clearTimeout(dismissProc);
30588         ce = o;
30589         if(removeCls){ // in case manually hidden
30590             el.removeClass(removeCls);
30591             removeCls = null;
30592         }
30593         if(ce.cls){
30594             el.addClass(ce.cls);
30595             removeCls = ce.cls;
30596         }
30597         if(ce.title){
30598             tipTitle.update(ce.title);
30599             tipTitle.show();
30600         }else{
30601             tipTitle.update('');
30602             tipTitle.hide();
30603         }
30604         el.dom.style.width  = tm.maxWidth+'px';
30605         //tipBody.dom.style.width = '';
30606         tipBodyText.update(o.text);
30607         var p = getPad(), w = ce.width;
30608         if(!w){
30609             var td = tipBodyText.dom;
30610             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30611             if(aw > tm.maxWidth){
30612                 w = tm.maxWidth;
30613             }else if(aw < tm.minWidth){
30614                 w = tm.minWidth;
30615             }else{
30616                 w = aw;
30617             }
30618         }
30619         //tipBody.setWidth(w);
30620         el.setWidth(parseInt(w, 10) + p);
30621         if(ce.autoHide === false){
30622             close.setDisplayed(true);
30623             if(dd){
30624                 dd.unlock();
30625             }
30626         }else{
30627             close.setDisplayed(false);
30628             if(dd){
30629                 dd.lock();
30630             }
30631         }
30632         if(xy){
30633             el.avoidY = xy[1]-18;
30634             el.setXY(xy);
30635         }
30636         if(tm.animate){
30637             el.setOpacity(.1);
30638             el.setStyle("visibility", "visible");
30639             el.fadeIn({callback: afterShow});
30640         }else{
30641             afterShow();
30642         }
30643     };
30644     
30645     var afterShow = function(){
30646         if(ce){
30647             el.show();
30648             esc.enable();
30649             if(tm.autoDismiss && ce.autoHide !== false){
30650                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30651             }
30652         }
30653     };
30654     
30655     var hide = function(noanim){
30656         clearTimeout(dismissProc);
30657         clearTimeout(hideProc);
30658         ce = null;
30659         if(el.isVisible()){
30660             esc.disable();
30661             if(noanim !== true && tm.animate){
30662                 el.fadeOut({callback: afterHide});
30663             }else{
30664                 afterHide();
30665             } 
30666         }
30667     };
30668     
30669     var afterHide = function(){
30670         el.hide();
30671         if(removeCls){
30672             el.removeClass(removeCls);
30673             removeCls = null;
30674         }
30675     };
30676     
30677     return {
30678         /**
30679         * @cfg {Number} minWidth
30680         * The minimum width of the quick tip (defaults to 40)
30681         */
30682        minWidth : 40,
30683         /**
30684         * @cfg {Number} maxWidth
30685         * The maximum width of the quick tip (defaults to 300)
30686         */
30687        maxWidth : 300,
30688         /**
30689         * @cfg {Boolean} interceptTitles
30690         * True to automatically use the element's DOM title value if available (defaults to false)
30691         */
30692        interceptTitles : false,
30693         /**
30694         * @cfg {Boolean} trackMouse
30695         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30696         */
30697        trackMouse : false,
30698         /**
30699         * @cfg {Boolean} hideOnClick
30700         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30701         */
30702        hideOnClick : true,
30703         /**
30704         * @cfg {Number} showDelay
30705         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30706         */
30707        showDelay : 500,
30708         /**
30709         * @cfg {Number} hideDelay
30710         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30711         */
30712        hideDelay : 200,
30713         /**
30714         * @cfg {Boolean} autoHide
30715         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30716         * Used in conjunction with hideDelay.
30717         */
30718        autoHide : true,
30719         /**
30720         * @cfg {Boolean}
30721         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30722         * (defaults to true).  Used in conjunction with autoDismissDelay.
30723         */
30724        autoDismiss : true,
30725         /**
30726         * @cfg {Number}
30727         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30728         */
30729        autoDismissDelay : 5000,
30730        /**
30731         * @cfg {Boolean} animate
30732         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30733         */
30734        animate : false,
30735
30736        /**
30737         * @cfg {String} title
30738         * Title text to display (defaults to '').  This can be any valid HTML markup.
30739         */
30740         title: '',
30741        /**
30742         * @cfg {String} text
30743         * Body text to display (defaults to '').  This can be any valid HTML markup.
30744         */
30745         text : '',
30746        /**
30747         * @cfg {String} cls
30748         * A CSS class to apply to the base quick tip element (defaults to '').
30749         */
30750         cls : '',
30751        /**
30752         * @cfg {Number} width
30753         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30754         * minWidth or maxWidth.
30755         */
30756         width : null,
30757
30758     /**
30759      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30760      * or display QuickTips in a page.
30761      */
30762        init : function(){
30763           tm = Roo.QuickTips;
30764           cfg = tm.tagConfig;
30765           if(!inited){
30766               if(!Roo.isReady){ // allow calling of init() before onReady
30767                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30768                   return;
30769               }
30770               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30771               el.fxDefaults = {stopFx: true};
30772               // maximum custom styling
30773               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30774               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30775               tipTitle = el.child('h3');
30776               tipTitle.enableDisplayMode("block");
30777               tipBody = el.child('div.x-tip-bd');
30778               tipBodyText = el.child('div.x-tip-bd-inner');
30779               //bdLeft = el.child('div.x-tip-bd-left');
30780               //bdRight = el.child('div.x-tip-bd-right');
30781               close = el.child('div.x-tip-close');
30782               close.enableDisplayMode("block");
30783               close.on("click", hide);
30784               var d = Roo.get(document);
30785               d.on("mousedown", onDown);
30786               d.on("mouseover", onOver);
30787               d.on("mouseout", onOut);
30788               d.on("mousemove", onMove);
30789               esc = d.addKeyListener(27, hide);
30790               esc.disable();
30791               if(Roo.dd.DD){
30792                   dd = el.initDD("default", null, {
30793                       onDrag : function(){
30794                           el.sync();  
30795                       }
30796                   });
30797                   dd.setHandleElId(tipTitle.id);
30798                   dd.lock();
30799               }
30800               inited = true;
30801           }
30802           this.enable(); 
30803        },
30804
30805     /**
30806      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30807      * are supported:
30808      * <pre>
30809 Property    Type                   Description
30810 ----------  ---------------------  ------------------------------------------------------------------------
30811 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30812      * </ul>
30813      * @param {Object} config The config object
30814      */
30815        register : function(config){
30816            var cs = config instanceof Array ? config : arguments;
30817            for(var i = 0, len = cs.length; i < len; i++) {
30818                var c = cs[i];
30819                var target = c.target;
30820                if(target){
30821                    if(target instanceof Array){
30822                        for(var j = 0, jlen = target.length; j < jlen; j++){
30823                            tagEls[target[j]] = c;
30824                        }
30825                    }else{
30826                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30827                    }
30828                }
30829            }
30830        },
30831
30832     /**
30833      * Removes this quick tip from its element and destroys it.
30834      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30835      */
30836        unregister : function(el){
30837            delete tagEls[Roo.id(el)];
30838        },
30839
30840     /**
30841      * Enable this quick tip.
30842      */
30843        enable : function(){
30844            if(inited && disabled){
30845                locks.pop();
30846                if(locks.length < 1){
30847                    disabled = false;
30848                }
30849            }
30850        },
30851
30852     /**
30853      * Disable this quick tip.
30854      */
30855        disable : function(){
30856           disabled = true;
30857           clearTimeout(showProc);
30858           clearTimeout(hideProc);
30859           clearTimeout(dismissProc);
30860           if(ce){
30861               hide(true);
30862           }
30863           locks.push(1);
30864        },
30865
30866     /**
30867      * Returns true if the quick tip is enabled, else false.
30868      */
30869        isEnabled : function(){
30870             return !disabled;
30871        },
30872
30873         // private
30874        tagConfig : {
30875            namespace : "ext",
30876            attribute : "qtip",
30877            width : "width",
30878            target : "target",
30879            title : "qtitle",
30880            hide : "hide",
30881            cls : "qclass"
30882        }
30883    };
30884 }();
30885
30886 // backwards compat
30887 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30888  * Based on:
30889  * Ext JS Library 1.1.1
30890  * Copyright(c) 2006-2007, Ext JS, LLC.
30891  *
30892  * Originally Released Under LGPL - original licence link has changed is not relivant.
30893  *
30894  * Fork - LGPL
30895  * <script type="text/javascript">
30896  */
30897  
30898
30899 /**
30900  * @class Roo.tree.TreePanel
30901  * @extends Roo.data.Tree
30902
30903  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30904  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30905  * @cfg {Boolean} enableDD true to enable drag and drop
30906  * @cfg {Boolean} enableDrag true to enable just drag
30907  * @cfg {Boolean} enableDrop true to enable just drop
30908  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30909  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30910  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30911  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30912  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30913  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30914  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30915  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30916  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30917  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30918  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30919  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30920  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30921  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30922  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30923  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30924  * 
30925  * @constructor
30926  * @param {String/HTMLElement/Element} el The container element
30927  * @param {Object} config
30928  */
30929 Roo.tree.TreePanel = function(el, config){
30930     var root = false;
30931     var loader = false;
30932     if (config.root) {
30933         root = config.root;
30934         delete config.root;
30935     }
30936     if (config.loader) {
30937         loader = config.loader;
30938         delete config.loader;
30939     }
30940     
30941     Roo.apply(this, config);
30942     Roo.tree.TreePanel.superclass.constructor.call(this);
30943     this.el = Roo.get(el);
30944     this.el.addClass('x-tree');
30945     //console.log(root);
30946     if (root) {
30947         this.setRootNode( Roo.factory(root, Roo.tree));
30948     }
30949     if (loader) {
30950         this.loader = Roo.factory(loader, Roo.tree);
30951     }
30952    /**
30953     * Read-only. The id of the container element becomes this TreePanel's id.
30954     */
30955     this.id = this.el.id;
30956     this.addEvents({
30957         /**
30958         * @event beforeload
30959         * Fires before a node is loaded, return false to cancel
30960         * @param {Node} node The node being loaded
30961         */
30962         "beforeload" : true,
30963         /**
30964         * @event load
30965         * Fires when a node is loaded
30966         * @param {Node} node The node that was loaded
30967         */
30968         "load" : true,
30969         /**
30970         * @event textchange
30971         * Fires when the text for a node is changed
30972         * @param {Node} node The node
30973         * @param {String} text The new text
30974         * @param {String} oldText The old text
30975         */
30976         "textchange" : true,
30977         /**
30978         * @event beforeexpand
30979         * Fires before a node is expanded, return false to cancel.
30980         * @param {Node} node The node
30981         * @param {Boolean} deep
30982         * @param {Boolean} anim
30983         */
30984         "beforeexpand" : true,
30985         /**
30986         * @event beforecollapse
30987         * Fires before a node is collapsed, return false to cancel.
30988         * @param {Node} node The node
30989         * @param {Boolean} deep
30990         * @param {Boolean} anim
30991         */
30992         "beforecollapse" : true,
30993         /**
30994         * @event expand
30995         * Fires when a node is expanded
30996         * @param {Node} node The node
30997         */
30998         "expand" : true,
30999         /**
31000         * @event disabledchange
31001         * Fires when the disabled status of a node changes
31002         * @param {Node} node The node
31003         * @param {Boolean} disabled
31004         */
31005         "disabledchange" : true,
31006         /**
31007         * @event collapse
31008         * Fires when a node is collapsed
31009         * @param {Node} node The node
31010         */
31011         "collapse" : true,
31012         /**
31013         * @event beforeclick
31014         * Fires before click processing on a node. Return false to cancel the default action.
31015         * @param {Node} node The node
31016         * @param {Roo.EventObject} e The event object
31017         */
31018         "beforeclick":true,
31019         /**
31020         * @event checkchange
31021         * Fires when a node with a checkbox's checked property changes
31022         * @param {Node} this This node
31023         * @param {Boolean} checked
31024         */
31025         "checkchange":true,
31026         /**
31027         * @event click
31028         * Fires when a node is clicked
31029         * @param {Node} node The node
31030         * @param {Roo.EventObject} e The event object
31031         */
31032         "click":true,
31033         /**
31034         * @event dblclick
31035         * Fires when a node is double clicked
31036         * @param {Node} node The node
31037         * @param {Roo.EventObject} e The event object
31038         */
31039         "dblclick":true,
31040         /**
31041         * @event contextmenu
31042         * Fires when a node is right clicked
31043         * @param {Node} node The node
31044         * @param {Roo.EventObject} e The event object
31045         */
31046         "contextmenu":true,
31047         /**
31048         * @event beforechildrenrendered
31049         * Fires right before the child nodes for a node are rendered
31050         * @param {Node} node The node
31051         */
31052         "beforechildrenrendered":true,
31053         /**
31054         * @event startdrag
31055         * Fires when a node starts being dragged
31056         * @param {Roo.tree.TreePanel} this
31057         * @param {Roo.tree.TreeNode} node
31058         * @param {event} e The raw browser event
31059         */ 
31060        "startdrag" : true,
31061        /**
31062         * @event enddrag
31063         * Fires when a drag operation is complete
31064         * @param {Roo.tree.TreePanel} this
31065         * @param {Roo.tree.TreeNode} node
31066         * @param {event} e The raw browser event
31067         */
31068        "enddrag" : true,
31069        /**
31070         * @event dragdrop
31071         * Fires when a dragged node is dropped on a valid DD target
31072         * @param {Roo.tree.TreePanel} this
31073         * @param {Roo.tree.TreeNode} node
31074         * @param {DD} dd The dd it was dropped on
31075         * @param {event} e The raw browser event
31076         */
31077        "dragdrop" : true,
31078        /**
31079         * @event beforenodedrop
31080         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31081         * passed to handlers has the following properties:<br />
31082         * <ul style="padding:5px;padding-left:16px;">
31083         * <li>tree - The TreePanel</li>
31084         * <li>target - The node being targeted for the drop</li>
31085         * <li>data - The drag data from the drag source</li>
31086         * <li>point - The point of the drop - append, above or below</li>
31087         * <li>source - The drag source</li>
31088         * <li>rawEvent - Raw mouse event</li>
31089         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31090         * to be inserted by setting them on this object.</li>
31091         * <li>cancel - Set this to true to cancel the drop.</li>
31092         * </ul>
31093         * @param {Object} dropEvent
31094         */
31095        "beforenodedrop" : true,
31096        /**
31097         * @event nodedrop
31098         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31099         * passed to handlers has the following properties:<br />
31100         * <ul style="padding:5px;padding-left:16px;">
31101         * <li>tree - The TreePanel</li>
31102         * <li>target - The node being targeted for the drop</li>
31103         * <li>data - The drag data from the drag source</li>
31104         * <li>point - The point of the drop - append, above or below</li>
31105         * <li>source - The drag source</li>
31106         * <li>rawEvent - Raw mouse event</li>
31107         * <li>dropNode - Dropped node(s).</li>
31108         * </ul>
31109         * @param {Object} dropEvent
31110         */
31111        "nodedrop" : true,
31112         /**
31113         * @event nodedragover
31114         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31115         * passed to handlers has the following properties:<br />
31116         * <ul style="padding:5px;padding-left:16px;">
31117         * <li>tree - The TreePanel</li>
31118         * <li>target - The node being targeted for the drop</li>
31119         * <li>data - The drag data from the drag source</li>
31120         * <li>point - The point of the drop - append, above or below</li>
31121         * <li>source - The drag source</li>
31122         * <li>rawEvent - Raw mouse event</li>
31123         * <li>dropNode - Drop node(s) provided by the source.</li>
31124         * <li>cancel - Set this to true to signal drop not allowed.</li>
31125         * </ul>
31126         * @param {Object} dragOverEvent
31127         */
31128        "nodedragover" : true
31129         
31130     });
31131     if(this.singleExpand){
31132        this.on("beforeexpand", this.restrictExpand, this);
31133     }
31134     if (this.editor) {
31135         this.editor.tree = this;
31136         this.editor = Roo.factory(this.editor, Roo.tree);
31137     }
31138     
31139     if (this.selModel) {
31140         this.selModel = Roo.factory(this.selModel, Roo.tree);
31141     }
31142    
31143 };
31144 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31145     rootVisible : true,
31146     animate: Roo.enableFx,
31147     lines : true,
31148     enableDD : false,
31149     hlDrop : Roo.enableFx,
31150   
31151     renderer: false,
31152     
31153     rendererTip: false,
31154     // private
31155     restrictExpand : function(node){
31156         var p = node.parentNode;
31157         if(p){
31158             if(p.expandedChild && p.expandedChild.parentNode == p){
31159                 p.expandedChild.collapse();
31160             }
31161             p.expandedChild = node;
31162         }
31163     },
31164
31165     // private override
31166     setRootNode : function(node){
31167         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31168         if(!this.rootVisible){
31169             node.ui = new Roo.tree.RootTreeNodeUI(node);
31170         }
31171         return node;
31172     },
31173
31174     /**
31175      * Returns the container element for this TreePanel
31176      */
31177     getEl : function(){
31178         return this.el;
31179     },
31180
31181     /**
31182      * Returns the default TreeLoader for this TreePanel
31183      */
31184     getLoader : function(){
31185         return this.loader;
31186     },
31187
31188     /**
31189      * Expand all nodes
31190      */
31191     expandAll : function(){
31192         this.root.expand(true);
31193     },
31194
31195     /**
31196      * Collapse all nodes
31197      */
31198     collapseAll : function(){
31199         this.root.collapse(true);
31200     },
31201
31202     /**
31203      * Returns the selection model used by this TreePanel
31204      */
31205     getSelectionModel : function(){
31206         if(!this.selModel){
31207             this.selModel = new Roo.tree.DefaultSelectionModel();
31208         }
31209         return this.selModel;
31210     },
31211
31212     /**
31213      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31214      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31215      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31216      * @return {Array}
31217      */
31218     getChecked : function(a, startNode){
31219         startNode = startNode || this.root;
31220         var r = [];
31221         var f = function(){
31222             if(this.attributes.checked){
31223                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31224             }
31225         }
31226         startNode.cascade(f);
31227         return r;
31228     },
31229
31230     /**
31231      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31232      * @param {String} path
31233      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31234      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31235      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31236      */
31237     expandPath : function(path, attr, callback){
31238         attr = attr || "id";
31239         var keys = path.split(this.pathSeparator);
31240         var curNode = this.root;
31241         if(curNode.attributes[attr] != keys[1]){ // invalid root
31242             if(callback){
31243                 callback(false, null);
31244             }
31245             return;
31246         }
31247         var index = 1;
31248         var f = function(){
31249             if(++index == keys.length){
31250                 if(callback){
31251                     callback(true, curNode);
31252                 }
31253                 return;
31254             }
31255             var c = curNode.findChild(attr, keys[index]);
31256             if(!c){
31257                 if(callback){
31258                     callback(false, curNode);
31259                 }
31260                 return;
31261             }
31262             curNode = c;
31263             c.expand(false, false, f);
31264         };
31265         curNode.expand(false, false, f);
31266     },
31267
31268     /**
31269      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31270      * @param {String} path
31271      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31272      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31273      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31274      */
31275     selectPath : function(path, attr, callback){
31276         attr = attr || "id";
31277         var keys = path.split(this.pathSeparator);
31278         var v = keys.pop();
31279         if(keys.length > 0){
31280             var f = function(success, node){
31281                 if(success && node){
31282                     var n = node.findChild(attr, v);
31283                     if(n){
31284                         n.select();
31285                         if(callback){
31286                             callback(true, n);
31287                         }
31288                     }else if(callback){
31289                         callback(false, n);
31290                     }
31291                 }else{
31292                     if(callback){
31293                         callback(false, n);
31294                     }
31295                 }
31296             };
31297             this.expandPath(keys.join(this.pathSeparator), attr, f);
31298         }else{
31299             this.root.select();
31300             if(callback){
31301                 callback(true, this.root);
31302             }
31303         }
31304     },
31305
31306     getTreeEl : function(){
31307         return this.el;
31308     },
31309
31310     /**
31311      * Trigger rendering of this TreePanel
31312      */
31313     render : function(){
31314         if (this.innerCt) {
31315             return this; // stop it rendering more than once!!
31316         }
31317         
31318         this.innerCt = this.el.createChild({tag:"ul",
31319                cls:"x-tree-root-ct " +
31320                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31321
31322         if(this.containerScroll){
31323             Roo.dd.ScrollManager.register(this.el);
31324         }
31325         if((this.enableDD || this.enableDrop) && !this.dropZone){
31326            /**
31327             * The dropZone used by this tree if drop is enabled
31328             * @type Roo.tree.TreeDropZone
31329             */
31330              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31331                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31332            });
31333         }
31334         if((this.enableDD || this.enableDrag) && !this.dragZone){
31335            /**
31336             * The dragZone used by this tree if drag is enabled
31337             * @type Roo.tree.TreeDragZone
31338             */
31339             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31340                ddGroup: this.ddGroup || "TreeDD",
31341                scroll: this.ddScroll
31342            });
31343         }
31344         this.getSelectionModel().init(this);
31345         if (!this.root) {
31346             Roo.log("ROOT not set in tree");
31347             return this;
31348         }
31349         this.root.render();
31350         if(!this.rootVisible){
31351             this.root.renderChildren();
31352         }
31353         return this;
31354     }
31355 });/*
31356  * Based on:
31357  * Ext JS Library 1.1.1
31358  * Copyright(c) 2006-2007, Ext JS, LLC.
31359  *
31360  * Originally Released Under LGPL - original licence link has changed is not relivant.
31361  *
31362  * Fork - LGPL
31363  * <script type="text/javascript">
31364  */
31365  
31366
31367 /**
31368  * @class Roo.tree.DefaultSelectionModel
31369  * @extends Roo.util.Observable
31370  * The default single selection for a TreePanel.
31371  * @param {Object} cfg Configuration
31372  */
31373 Roo.tree.DefaultSelectionModel = function(cfg){
31374    this.selNode = null;
31375    
31376    
31377    
31378    this.addEvents({
31379        /**
31380         * @event selectionchange
31381         * Fires when the selected node changes
31382         * @param {DefaultSelectionModel} this
31383         * @param {TreeNode} node the new selection
31384         */
31385        "selectionchange" : true,
31386
31387        /**
31388         * @event beforeselect
31389         * Fires before the selected node changes, return false to cancel the change
31390         * @param {DefaultSelectionModel} this
31391         * @param {TreeNode} node the new selection
31392         * @param {TreeNode} node the old selection
31393         */
31394        "beforeselect" : true
31395    });
31396    
31397     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31398 };
31399
31400 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31401     init : function(tree){
31402         this.tree = tree;
31403         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31404         tree.on("click", this.onNodeClick, this);
31405     },
31406     
31407     onNodeClick : function(node, e){
31408         if (e.ctrlKey && this.selNode == node)  {
31409             this.unselect(node);
31410             return;
31411         }
31412         this.select(node);
31413     },
31414     
31415     /**
31416      * Select a node.
31417      * @param {TreeNode} node The node to select
31418      * @return {TreeNode} The selected node
31419      */
31420     select : function(node){
31421         var last = this.selNode;
31422         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31423             if(last){
31424                 last.ui.onSelectedChange(false);
31425             }
31426             this.selNode = node;
31427             node.ui.onSelectedChange(true);
31428             this.fireEvent("selectionchange", this, node, last);
31429         }
31430         return node;
31431     },
31432     
31433     /**
31434      * Deselect a node.
31435      * @param {TreeNode} node The node to unselect
31436      */
31437     unselect : function(node){
31438         if(this.selNode == node){
31439             this.clearSelections();
31440         }    
31441     },
31442     
31443     /**
31444      * Clear all selections
31445      */
31446     clearSelections : function(){
31447         var n = this.selNode;
31448         if(n){
31449             n.ui.onSelectedChange(false);
31450             this.selNode = null;
31451             this.fireEvent("selectionchange", this, null);
31452         }
31453         return n;
31454     },
31455     
31456     /**
31457      * Get the selected node
31458      * @return {TreeNode} The selected node
31459      */
31460     getSelectedNode : function(){
31461         return this.selNode;    
31462     },
31463     
31464     /**
31465      * Returns true if the node is selected
31466      * @param {TreeNode} node The node to check
31467      * @return {Boolean}
31468      */
31469     isSelected : function(node){
31470         return this.selNode == node;  
31471     },
31472
31473     /**
31474      * Selects the node above the selected node in the tree, intelligently walking the nodes
31475      * @return TreeNode The new selection
31476      */
31477     selectPrevious : function(){
31478         var s = this.selNode || this.lastSelNode;
31479         if(!s){
31480             return null;
31481         }
31482         var ps = s.previousSibling;
31483         if(ps){
31484             if(!ps.isExpanded() || ps.childNodes.length < 1){
31485                 return this.select(ps);
31486             } else{
31487                 var lc = ps.lastChild;
31488                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31489                     lc = lc.lastChild;
31490                 }
31491                 return this.select(lc);
31492             }
31493         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31494             return this.select(s.parentNode);
31495         }
31496         return null;
31497     },
31498
31499     /**
31500      * Selects the node above the selected node in the tree, intelligently walking the nodes
31501      * @return TreeNode The new selection
31502      */
31503     selectNext : function(){
31504         var s = this.selNode || this.lastSelNode;
31505         if(!s){
31506             return null;
31507         }
31508         if(s.firstChild && s.isExpanded()){
31509              return this.select(s.firstChild);
31510          }else if(s.nextSibling){
31511              return this.select(s.nextSibling);
31512          }else if(s.parentNode){
31513             var newS = null;
31514             s.parentNode.bubble(function(){
31515                 if(this.nextSibling){
31516                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31517                     return false;
31518                 }
31519             });
31520             return newS;
31521          }
31522         return null;
31523     },
31524
31525     onKeyDown : function(e){
31526         var s = this.selNode || this.lastSelNode;
31527         // undesirable, but required
31528         var sm = this;
31529         if(!s){
31530             return;
31531         }
31532         var k = e.getKey();
31533         switch(k){
31534              case e.DOWN:
31535                  e.stopEvent();
31536                  this.selectNext();
31537              break;
31538              case e.UP:
31539                  e.stopEvent();
31540                  this.selectPrevious();
31541              break;
31542              case e.RIGHT:
31543                  e.preventDefault();
31544                  if(s.hasChildNodes()){
31545                      if(!s.isExpanded()){
31546                          s.expand();
31547                      }else if(s.firstChild){
31548                          this.select(s.firstChild, e);
31549                      }
31550                  }
31551              break;
31552              case e.LEFT:
31553                  e.preventDefault();
31554                  if(s.hasChildNodes() && s.isExpanded()){
31555                      s.collapse();
31556                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31557                      this.select(s.parentNode, e);
31558                  }
31559              break;
31560         };
31561     }
31562 });
31563
31564 /**
31565  * @class Roo.tree.MultiSelectionModel
31566  * @extends Roo.util.Observable
31567  * Multi selection for a TreePanel.
31568  * @param {Object} cfg Configuration
31569  */
31570 Roo.tree.MultiSelectionModel = function(){
31571    this.selNodes = [];
31572    this.selMap = {};
31573    this.addEvents({
31574        /**
31575         * @event selectionchange
31576         * Fires when the selected nodes change
31577         * @param {MultiSelectionModel} this
31578         * @param {Array} nodes Array of the selected nodes
31579         */
31580        "selectionchange" : true
31581    });
31582    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31583    
31584 };
31585
31586 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31587     init : function(tree){
31588         this.tree = tree;
31589         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31590         tree.on("click", this.onNodeClick, this);
31591     },
31592     
31593     onNodeClick : function(node, e){
31594         this.select(node, e, e.ctrlKey);
31595     },
31596     
31597     /**
31598      * Select a node.
31599      * @param {TreeNode} node The node to select
31600      * @param {EventObject} e (optional) An event associated with the selection
31601      * @param {Boolean} keepExisting True to retain existing selections
31602      * @return {TreeNode} The selected node
31603      */
31604     select : function(node, e, keepExisting){
31605         if(keepExisting !== true){
31606             this.clearSelections(true);
31607         }
31608         if(this.isSelected(node)){
31609             this.lastSelNode = node;
31610             return node;
31611         }
31612         this.selNodes.push(node);
31613         this.selMap[node.id] = node;
31614         this.lastSelNode = node;
31615         node.ui.onSelectedChange(true);
31616         this.fireEvent("selectionchange", this, this.selNodes);
31617         return node;
31618     },
31619     
31620     /**
31621      * Deselect a node.
31622      * @param {TreeNode} node The node to unselect
31623      */
31624     unselect : function(node){
31625         if(this.selMap[node.id]){
31626             node.ui.onSelectedChange(false);
31627             var sn = this.selNodes;
31628             var index = -1;
31629             if(sn.indexOf){
31630                 index = sn.indexOf(node);
31631             }else{
31632                 for(var i = 0, len = sn.length; i < len; i++){
31633                     if(sn[i] == node){
31634                         index = i;
31635                         break;
31636                     }
31637                 }
31638             }
31639             if(index != -1){
31640                 this.selNodes.splice(index, 1);
31641             }
31642             delete this.selMap[node.id];
31643             this.fireEvent("selectionchange", this, this.selNodes);
31644         }
31645     },
31646     
31647     /**
31648      * Clear all selections
31649      */
31650     clearSelections : function(suppressEvent){
31651         var sn = this.selNodes;
31652         if(sn.length > 0){
31653             for(var i = 0, len = sn.length; i < len; i++){
31654                 sn[i].ui.onSelectedChange(false);
31655             }
31656             this.selNodes = [];
31657             this.selMap = {};
31658             if(suppressEvent !== true){
31659                 this.fireEvent("selectionchange", this, this.selNodes);
31660             }
31661         }
31662     },
31663     
31664     /**
31665      * Returns true if the node is selected
31666      * @param {TreeNode} node The node to check
31667      * @return {Boolean}
31668      */
31669     isSelected : function(node){
31670         return this.selMap[node.id] ? true : false;  
31671     },
31672     
31673     /**
31674      * Returns an array of the selected nodes
31675      * @return {Array}
31676      */
31677     getSelectedNodes : function(){
31678         return this.selNodes;    
31679     },
31680
31681     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31682
31683     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31684
31685     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31686 });/*
31687  * Based on:
31688  * Ext JS Library 1.1.1
31689  * Copyright(c) 2006-2007, Ext JS, LLC.
31690  *
31691  * Originally Released Under LGPL - original licence link has changed is not relivant.
31692  *
31693  * Fork - LGPL
31694  * <script type="text/javascript">
31695  */
31696  
31697 /**
31698  * @class Roo.tree.TreeNode
31699  * @extends Roo.data.Node
31700  * @cfg {String} text The text for this node
31701  * @cfg {Boolean} expanded true to start the node expanded
31702  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31703  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31704  * @cfg {Boolean} disabled true to start the node disabled
31705  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31706  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31707  * @cfg {String} cls A css class to be added to the node
31708  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31709  * @cfg {String} href URL of the link used for the node (defaults to #)
31710  * @cfg {String} hrefTarget target frame for the link
31711  * @cfg {String} qtip An Ext QuickTip for the node
31712  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31713  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31714  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31715  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31716  * (defaults to undefined with no checkbox rendered)
31717  * @constructor
31718  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31719  */
31720 Roo.tree.TreeNode = function(attributes){
31721     attributes = attributes || {};
31722     if(typeof attributes == "string"){
31723         attributes = {text: attributes};
31724     }
31725     this.childrenRendered = false;
31726     this.rendered = false;
31727     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31728     this.expanded = attributes.expanded === true;
31729     this.isTarget = attributes.isTarget !== false;
31730     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31731     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31732
31733     /**
31734      * Read-only. The text for this node. To change it use setText().
31735      * @type String
31736      */
31737     this.text = attributes.text;
31738     /**
31739      * True if this node is disabled.
31740      * @type Boolean
31741      */
31742     this.disabled = attributes.disabled === true;
31743
31744     this.addEvents({
31745         /**
31746         * @event textchange
31747         * Fires when the text for this node is changed
31748         * @param {Node} this This node
31749         * @param {String} text The new text
31750         * @param {String} oldText The old text
31751         */
31752         "textchange" : true,
31753         /**
31754         * @event beforeexpand
31755         * Fires before this node is expanded, return false to cancel.
31756         * @param {Node} this This node
31757         * @param {Boolean} deep
31758         * @param {Boolean} anim
31759         */
31760         "beforeexpand" : true,
31761         /**
31762         * @event beforecollapse
31763         * Fires before this node is collapsed, return false to cancel.
31764         * @param {Node} this This node
31765         * @param {Boolean} deep
31766         * @param {Boolean} anim
31767         */
31768         "beforecollapse" : true,
31769         /**
31770         * @event expand
31771         * Fires when this node is expanded
31772         * @param {Node} this This node
31773         */
31774         "expand" : true,
31775         /**
31776         * @event disabledchange
31777         * Fires when the disabled status of this node changes
31778         * @param {Node} this This node
31779         * @param {Boolean} disabled
31780         */
31781         "disabledchange" : true,
31782         /**
31783         * @event collapse
31784         * Fires when this node is collapsed
31785         * @param {Node} this This node
31786         */
31787         "collapse" : true,
31788         /**
31789         * @event beforeclick
31790         * Fires before click processing. Return false to cancel the default action.
31791         * @param {Node} this This node
31792         * @param {Roo.EventObject} e The event object
31793         */
31794         "beforeclick":true,
31795         /**
31796         * @event checkchange
31797         * Fires when a node with a checkbox's checked property changes
31798         * @param {Node} this This node
31799         * @param {Boolean} checked
31800         */
31801         "checkchange":true,
31802         /**
31803         * @event click
31804         * Fires when this node is clicked
31805         * @param {Node} this This node
31806         * @param {Roo.EventObject} e The event object
31807         */
31808         "click":true,
31809         /**
31810         * @event dblclick
31811         * Fires when this node is double clicked
31812         * @param {Node} this This node
31813         * @param {Roo.EventObject} e The event object
31814         */
31815         "dblclick":true,
31816         /**
31817         * @event contextmenu
31818         * Fires when this node is right clicked
31819         * @param {Node} this This node
31820         * @param {Roo.EventObject} e The event object
31821         */
31822         "contextmenu":true,
31823         /**
31824         * @event beforechildrenrendered
31825         * Fires right before the child nodes for this node are rendered
31826         * @param {Node} this This node
31827         */
31828         "beforechildrenrendered":true
31829     });
31830
31831     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31832
31833     /**
31834      * Read-only. The UI for this node
31835      * @type TreeNodeUI
31836      */
31837     this.ui = new uiClass(this);
31838     
31839     // finally support items[]
31840     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31841         return;
31842     }
31843     
31844     
31845     Roo.each(this.attributes.items, function(c) {
31846         this.appendChild(Roo.factory(c,Roo.Tree));
31847     }, this);
31848     delete this.attributes.items;
31849     
31850     
31851     
31852 };
31853 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31854     preventHScroll: true,
31855     /**
31856      * Returns true if this node is expanded
31857      * @return {Boolean}
31858      */
31859     isExpanded : function(){
31860         return this.expanded;
31861     },
31862
31863     /**
31864      * Returns the UI object for this node
31865      * @return {TreeNodeUI}
31866      */
31867     getUI : function(){
31868         return this.ui;
31869     },
31870
31871     // private override
31872     setFirstChild : function(node){
31873         var of = this.firstChild;
31874         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31875         if(this.childrenRendered && of && node != of){
31876             of.renderIndent(true, true);
31877         }
31878         if(this.rendered){
31879             this.renderIndent(true, true);
31880         }
31881     },
31882
31883     // private override
31884     setLastChild : function(node){
31885         var ol = this.lastChild;
31886         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31887         if(this.childrenRendered && ol && node != ol){
31888             ol.renderIndent(true, true);
31889         }
31890         if(this.rendered){
31891             this.renderIndent(true, true);
31892         }
31893     },
31894
31895     // these methods are overridden to provide lazy rendering support
31896     // private override
31897     appendChild : function()
31898     {
31899         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31900         if(node && this.childrenRendered){
31901             node.render();
31902         }
31903         this.ui.updateExpandIcon();
31904         return node;
31905     },
31906
31907     // private override
31908     removeChild : function(node){
31909         this.ownerTree.getSelectionModel().unselect(node);
31910         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31911         // if it's been rendered remove dom node
31912         if(this.childrenRendered){
31913             node.ui.remove();
31914         }
31915         if(this.childNodes.length < 1){
31916             this.collapse(false, false);
31917         }else{
31918             this.ui.updateExpandIcon();
31919         }
31920         if(!this.firstChild) {
31921             this.childrenRendered = false;
31922         }
31923         return node;
31924     },
31925
31926     // private override
31927     insertBefore : function(node, refNode){
31928         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31929         if(newNode && refNode && this.childrenRendered){
31930             node.render();
31931         }
31932         this.ui.updateExpandIcon();
31933         return newNode;
31934     },
31935
31936     /**
31937      * Sets the text for this node
31938      * @param {String} text
31939      */
31940     setText : function(text){
31941         var oldText = this.text;
31942         this.text = text;
31943         this.attributes.text = text;
31944         if(this.rendered){ // event without subscribing
31945             this.ui.onTextChange(this, text, oldText);
31946         }
31947         this.fireEvent("textchange", this, text, oldText);
31948     },
31949
31950     /**
31951      * Triggers selection of this node
31952      */
31953     select : function(){
31954         this.getOwnerTree().getSelectionModel().select(this);
31955     },
31956
31957     /**
31958      * Triggers deselection of this node
31959      */
31960     unselect : function(){
31961         this.getOwnerTree().getSelectionModel().unselect(this);
31962     },
31963
31964     /**
31965      * Returns true if this node is selected
31966      * @return {Boolean}
31967      */
31968     isSelected : function(){
31969         return this.getOwnerTree().getSelectionModel().isSelected(this);
31970     },
31971
31972     /**
31973      * Expand this node.
31974      * @param {Boolean} deep (optional) True to expand all children as well
31975      * @param {Boolean} anim (optional) false to cancel the default animation
31976      * @param {Function} callback (optional) A callback to be called when
31977      * expanding this node completes (does not wait for deep expand to complete).
31978      * Called with 1 parameter, this node.
31979      */
31980     expand : function(deep, anim, callback){
31981         if(!this.expanded){
31982             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31983                 return;
31984             }
31985             if(!this.childrenRendered){
31986                 this.renderChildren();
31987             }
31988             this.expanded = true;
31989             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31990                 this.ui.animExpand(function(){
31991                     this.fireEvent("expand", this);
31992                     if(typeof callback == "function"){
31993                         callback(this);
31994                     }
31995                     if(deep === true){
31996                         this.expandChildNodes(true);
31997                     }
31998                 }.createDelegate(this));
31999                 return;
32000             }else{
32001                 this.ui.expand();
32002                 this.fireEvent("expand", this);
32003                 if(typeof callback == "function"){
32004                     callback(this);
32005                 }
32006             }
32007         }else{
32008            if(typeof callback == "function"){
32009                callback(this);
32010            }
32011         }
32012         if(deep === true){
32013             this.expandChildNodes(true);
32014         }
32015     },
32016
32017     isHiddenRoot : function(){
32018         return this.isRoot && !this.getOwnerTree().rootVisible;
32019     },
32020
32021     /**
32022      * Collapse this node.
32023      * @param {Boolean} deep (optional) True to collapse all children as well
32024      * @param {Boolean} anim (optional) false to cancel the default animation
32025      */
32026     collapse : function(deep, anim){
32027         if(this.expanded && !this.isHiddenRoot()){
32028             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32029                 return;
32030             }
32031             this.expanded = false;
32032             if((this.getOwnerTree().animate && anim !== false) || anim){
32033                 this.ui.animCollapse(function(){
32034                     this.fireEvent("collapse", this);
32035                     if(deep === true){
32036                         this.collapseChildNodes(true);
32037                     }
32038                 }.createDelegate(this));
32039                 return;
32040             }else{
32041                 this.ui.collapse();
32042                 this.fireEvent("collapse", this);
32043             }
32044         }
32045         if(deep === true){
32046             var cs = this.childNodes;
32047             for(var i = 0, len = cs.length; i < len; i++) {
32048                 cs[i].collapse(true, false);
32049             }
32050         }
32051     },
32052
32053     // private
32054     delayedExpand : function(delay){
32055         if(!this.expandProcId){
32056             this.expandProcId = this.expand.defer(delay, this);
32057         }
32058     },
32059
32060     // private
32061     cancelExpand : function(){
32062         if(this.expandProcId){
32063             clearTimeout(this.expandProcId);
32064         }
32065         this.expandProcId = false;
32066     },
32067
32068     /**
32069      * Toggles expanded/collapsed state of the node
32070      */
32071     toggle : function(){
32072         if(this.expanded){
32073             this.collapse();
32074         }else{
32075             this.expand();
32076         }
32077     },
32078
32079     /**
32080      * Ensures all parent nodes are expanded
32081      */
32082     ensureVisible : function(callback){
32083         var tree = this.getOwnerTree();
32084         tree.expandPath(this.parentNode.getPath(), false, function(){
32085             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32086             Roo.callback(callback);
32087         }.createDelegate(this));
32088     },
32089
32090     /**
32091      * Expand all child nodes
32092      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32093      */
32094     expandChildNodes : function(deep){
32095         var cs = this.childNodes;
32096         for(var i = 0, len = cs.length; i < len; i++) {
32097                 cs[i].expand(deep);
32098         }
32099     },
32100
32101     /**
32102      * Collapse all child nodes
32103      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32104      */
32105     collapseChildNodes : function(deep){
32106         var cs = this.childNodes;
32107         for(var i = 0, len = cs.length; i < len; i++) {
32108                 cs[i].collapse(deep);
32109         }
32110     },
32111
32112     /**
32113      * Disables this node
32114      */
32115     disable : function(){
32116         this.disabled = true;
32117         this.unselect();
32118         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32119             this.ui.onDisableChange(this, true);
32120         }
32121         this.fireEvent("disabledchange", this, true);
32122     },
32123
32124     /**
32125      * Enables this node
32126      */
32127     enable : function(){
32128         this.disabled = false;
32129         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32130             this.ui.onDisableChange(this, false);
32131         }
32132         this.fireEvent("disabledchange", this, false);
32133     },
32134
32135     // private
32136     renderChildren : function(suppressEvent){
32137         if(suppressEvent !== false){
32138             this.fireEvent("beforechildrenrendered", this);
32139         }
32140         var cs = this.childNodes;
32141         for(var i = 0, len = cs.length; i < len; i++){
32142             cs[i].render(true);
32143         }
32144         this.childrenRendered = true;
32145     },
32146
32147     // private
32148     sort : function(fn, scope){
32149         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32150         if(this.childrenRendered){
32151             var cs = this.childNodes;
32152             for(var i = 0, len = cs.length; i < len; i++){
32153                 cs[i].render(true);
32154             }
32155         }
32156     },
32157
32158     // private
32159     render : function(bulkRender){
32160         this.ui.render(bulkRender);
32161         if(!this.rendered){
32162             this.rendered = true;
32163             if(this.expanded){
32164                 this.expanded = false;
32165                 this.expand(false, false);
32166             }
32167         }
32168     },
32169
32170     // private
32171     renderIndent : function(deep, refresh){
32172         if(refresh){
32173             this.ui.childIndent = null;
32174         }
32175         this.ui.renderIndent();
32176         if(deep === true && this.childrenRendered){
32177             var cs = this.childNodes;
32178             for(var i = 0, len = cs.length; i < len; i++){
32179                 cs[i].renderIndent(true, refresh);
32180             }
32181         }
32182     }
32183 });/*
32184  * Based on:
32185  * Ext JS Library 1.1.1
32186  * Copyright(c) 2006-2007, Ext JS, LLC.
32187  *
32188  * Originally Released Under LGPL - original licence link has changed is not relivant.
32189  *
32190  * Fork - LGPL
32191  * <script type="text/javascript">
32192  */
32193  
32194 /**
32195  * @class Roo.tree.AsyncTreeNode
32196  * @extends Roo.tree.TreeNode
32197  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32198  * @constructor
32199  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32200  */
32201  Roo.tree.AsyncTreeNode = function(config){
32202     this.loaded = false;
32203     this.loading = false;
32204     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32205     /**
32206     * @event beforeload
32207     * Fires before this node is loaded, return false to cancel
32208     * @param {Node} this This node
32209     */
32210     this.addEvents({'beforeload':true, 'load': true});
32211     /**
32212     * @event load
32213     * Fires when this node is loaded
32214     * @param {Node} this This node
32215     */
32216     /**
32217      * The loader used by this node (defaults to using the tree's defined loader)
32218      * @type TreeLoader
32219      * @property loader
32220      */
32221 };
32222 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32223     expand : function(deep, anim, callback){
32224         if(this.loading){ // if an async load is already running, waiting til it's done
32225             var timer;
32226             var f = function(){
32227                 if(!this.loading){ // done loading
32228                     clearInterval(timer);
32229                     this.expand(deep, anim, callback);
32230                 }
32231             }.createDelegate(this);
32232             timer = setInterval(f, 200);
32233             return;
32234         }
32235         if(!this.loaded){
32236             if(this.fireEvent("beforeload", this) === false){
32237                 return;
32238             }
32239             this.loading = true;
32240             this.ui.beforeLoad(this);
32241             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32242             if(loader){
32243                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32244                 return;
32245             }
32246         }
32247         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32248     },
32249     
32250     /**
32251      * Returns true if this node is currently loading
32252      * @return {Boolean}
32253      */
32254     isLoading : function(){
32255         return this.loading;  
32256     },
32257     
32258     loadComplete : function(deep, anim, callback){
32259         this.loading = false;
32260         this.loaded = true;
32261         this.ui.afterLoad(this);
32262         this.fireEvent("load", this);
32263         this.expand(deep, anim, callback);
32264     },
32265     
32266     /**
32267      * Returns true if this node has been loaded
32268      * @return {Boolean}
32269      */
32270     isLoaded : function(){
32271         return this.loaded;
32272     },
32273     
32274     hasChildNodes : function(){
32275         if(!this.isLeaf() && !this.loaded){
32276             return true;
32277         }else{
32278             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32279         }
32280     },
32281
32282     /**
32283      * Trigger a reload for this node
32284      * @param {Function} callback
32285      */
32286     reload : function(callback){
32287         this.collapse(false, false);
32288         while(this.firstChild){
32289             this.removeChild(this.firstChild);
32290         }
32291         this.childrenRendered = false;
32292         this.loaded = false;
32293         if(this.isHiddenRoot()){
32294             this.expanded = false;
32295         }
32296         this.expand(false, false, callback);
32297     }
32298 });/*
32299  * Based on:
32300  * Ext JS Library 1.1.1
32301  * Copyright(c) 2006-2007, Ext JS, LLC.
32302  *
32303  * Originally Released Under LGPL - original licence link has changed is not relivant.
32304  *
32305  * Fork - LGPL
32306  * <script type="text/javascript">
32307  */
32308  
32309 /**
32310  * @class Roo.tree.TreeNodeUI
32311  * @constructor
32312  * @param {Object} node The node to render
32313  * The TreeNode UI implementation is separate from the
32314  * tree implementation. Unless you are customizing the tree UI,
32315  * you should never have to use this directly.
32316  */
32317 Roo.tree.TreeNodeUI = function(node){
32318     this.node = node;
32319     this.rendered = false;
32320     this.animating = false;
32321     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32322 };
32323
32324 Roo.tree.TreeNodeUI.prototype = {
32325     removeChild : function(node){
32326         if(this.rendered){
32327             this.ctNode.removeChild(node.ui.getEl());
32328         }
32329     },
32330
32331     beforeLoad : function(){
32332          this.addClass("x-tree-node-loading");
32333     },
32334
32335     afterLoad : function(){
32336          this.removeClass("x-tree-node-loading");
32337     },
32338
32339     onTextChange : function(node, text, oldText){
32340         if(this.rendered){
32341             this.textNode.innerHTML = text;
32342         }
32343     },
32344
32345     onDisableChange : function(node, state){
32346         this.disabled = state;
32347         if(state){
32348             this.addClass("x-tree-node-disabled");
32349         }else{
32350             this.removeClass("x-tree-node-disabled");
32351         }
32352     },
32353
32354     onSelectedChange : function(state){
32355         if(state){
32356             this.focus();
32357             this.addClass("x-tree-selected");
32358         }else{
32359             //this.blur();
32360             this.removeClass("x-tree-selected");
32361         }
32362     },
32363
32364     onMove : function(tree, node, oldParent, newParent, index, refNode){
32365         this.childIndent = null;
32366         if(this.rendered){
32367             var targetNode = newParent.ui.getContainer();
32368             if(!targetNode){//target not rendered
32369                 this.holder = document.createElement("div");
32370                 this.holder.appendChild(this.wrap);
32371                 return;
32372             }
32373             var insertBefore = refNode ? refNode.ui.getEl() : null;
32374             if(insertBefore){
32375                 targetNode.insertBefore(this.wrap, insertBefore);
32376             }else{
32377                 targetNode.appendChild(this.wrap);
32378             }
32379             this.node.renderIndent(true);
32380         }
32381     },
32382
32383     addClass : function(cls){
32384         if(this.elNode){
32385             Roo.fly(this.elNode).addClass(cls);
32386         }
32387     },
32388
32389     removeClass : function(cls){
32390         if(this.elNode){
32391             Roo.fly(this.elNode).removeClass(cls);
32392         }
32393     },
32394
32395     remove : function(){
32396         if(this.rendered){
32397             this.holder = document.createElement("div");
32398             this.holder.appendChild(this.wrap);
32399         }
32400     },
32401
32402     fireEvent : function(){
32403         return this.node.fireEvent.apply(this.node, arguments);
32404     },
32405
32406     initEvents : function(){
32407         this.node.on("move", this.onMove, this);
32408         var E = Roo.EventManager;
32409         var a = this.anchor;
32410
32411         var el = Roo.fly(a, '_treeui');
32412
32413         if(Roo.isOpera){ // opera render bug ignores the CSS
32414             el.setStyle("text-decoration", "none");
32415         }
32416
32417         el.on("click", this.onClick, this);
32418         el.on("dblclick", this.onDblClick, this);
32419
32420         if(this.checkbox){
32421             Roo.EventManager.on(this.checkbox,
32422                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32423         }
32424
32425         el.on("contextmenu", this.onContextMenu, this);
32426
32427         var icon = Roo.fly(this.iconNode);
32428         icon.on("click", this.onClick, this);
32429         icon.on("dblclick", this.onDblClick, this);
32430         icon.on("contextmenu", this.onContextMenu, this);
32431         E.on(this.ecNode, "click", this.ecClick, this, true);
32432
32433         if(this.node.disabled){
32434             this.addClass("x-tree-node-disabled");
32435         }
32436         if(this.node.hidden){
32437             this.addClass("x-tree-node-disabled");
32438         }
32439         var ot = this.node.getOwnerTree();
32440         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32441         if(dd && (!this.node.isRoot || ot.rootVisible)){
32442             Roo.dd.Registry.register(this.elNode, {
32443                 node: this.node,
32444                 handles: this.getDDHandles(),
32445                 isHandle: false
32446             });
32447         }
32448     },
32449
32450     getDDHandles : function(){
32451         return [this.iconNode, this.textNode];
32452     },
32453
32454     hide : function(){
32455         if(this.rendered){
32456             this.wrap.style.display = "none";
32457         }
32458     },
32459
32460     show : function(){
32461         if(this.rendered){
32462             this.wrap.style.display = "";
32463         }
32464     },
32465
32466     onContextMenu : function(e){
32467         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32468             e.preventDefault();
32469             this.focus();
32470             this.fireEvent("contextmenu", this.node, e);
32471         }
32472     },
32473
32474     onClick : function(e){
32475         if(this.dropping){
32476             e.stopEvent();
32477             return;
32478         }
32479         if(this.fireEvent("beforeclick", this.node, e) !== false){
32480             if(!this.disabled && this.node.attributes.href){
32481                 this.fireEvent("click", this.node, e);
32482                 return;
32483             }
32484             e.preventDefault();
32485             if(this.disabled){
32486                 return;
32487             }
32488
32489             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32490                 this.node.toggle();
32491             }
32492
32493             this.fireEvent("click", this.node, e);
32494         }else{
32495             e.stopEvent();
32496         }
32497     },
32498
32499     onDblClick : function(e){
32500         e.preventDefault();
32501         if(this.disabled){
32502             return;
32503         }
32504         if(this.checkbox){
32505             this.toggleCheck();
32506         }
32507         if(!this.animating && this.node.hasChildNodes()){
32508             this.node.toggle();
32509         }
32510         this.fireEvent("dblclick", this.node, e);
32511     },
32512
32513     onCheckChange : function(){
32514         var checked = this.checkbox.checked;
32515         this.node.attributes.checked = checked;
32516         this.fireEvent('checkchange', this.node, checked);
32517     },
32518
32519     ecClick : function(e){
32520         if(!this.animating && this.node.hasChildNodes()){
32521             this.node.toggle();
32522         }
32523     },
32524
32525     startDrop : function(){
32526         this.dropping = true;
32527     },
32528
32529     // delayed drop so the click event doesn't get fired on a drop
32530     endDrop : function(){
32531        setTimeout(function(){
32532            this.dropping = false;
32533        }.createDelegate(this), 50);
32534     },
32535
32536     expand : function(){
32537         this.updateExpandIcon();
32538         this.ctNode.style.display = "";
32539     },
32540
32541     focus : function(){
32542         if(!this.node.preventHScroll){
32543             try{this.anchor.focus();
32544             }catch(e){}
32545         }else if(!Roo.isIE){
32546             try{
32547                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32548                 var l = noscroll.scrollLeft;
32549                 this.anchor.focus();
32550                 noscroll.scrollLeft = l;
32551             }catch(e){}
32552         }
32553     },
32554
32555     toggleCheck : function(value){
32556         var cb = this.checkbox;
32557         if(cb){
32558             cb.checked = (value === undefined ? !cb.checked : value);
32559         }
32560     },
32561
32562     blur : function(){
32563         try{
32564             this.anchor.blur();
32565         }catch(e){}
32566     },
32567
32568     animExpand : function(callback){
32569         var ct = Roo.get(this.ctNode);
32570         ct.stopFx();
32571         if(!this.node.hasChildNodes()){
32572             this.updateExpandIcon();
32573             this.ctNode.style.display = "";
32574             Roo.callback(callback);
32575             return;
32576         }
32577         this.animating = true;
32578         this.updateExpandIcon();
32579
32580         ct.slideIn('t', {
32581            callback : function(){
32582                this.animating = false;
32583                Roo.callback(callback);
32584             },
32585             scope: this,
32586             duration: this.node.ownerTree.duration || .25
32587         });
32588     },
32589
32590     highlight : function(){
32591         var tree = this.node.getOwnerTree();
32592         Roo.fly(this.wrap).highlight(
32593             tree.hlColor || "C3DAF9",
32594             {endColor: tree.hlBaseColor}
32595         );
32596     },
32597
32598     collapse : function(){
32599         this.updateExpandIcon();
32600         this.ctNode.style.display = "none";
32601     },
32602
32603     animCollapse : function(callback){
32604         var ct = Roo.get(this.ctNode);
32605         ct.enableDisplayMode('block');
32606         ct.stopFx();
32607
32608         this.animating = true;
32609         this.updateExpandIcon();
32610
32611         ct.slideOut('t', {
32612             callback : function(){
32613                this.animating = false;
32614                Roo.callback(callback);
32615             },
32616             scope: this,
32617             duration: this.node.ownerTree.duration || .25
32618         });
32619     },
32620
32621     getContainer : function(){
32622         return this.ctNode;
32623     },
32624
32625     getEl : function(){
32626         return this.wrap;
32627     },
32628
32629     appendDDGhost : function(ghostNode){
32630         ghostNode.appendChild(this.elNode.cloneNode(true));
32631     },
32632
32633     getDDRepairXY : function(){
32634         return Roo.lib.Dom.getXY(this.iconNode);
32635     },
32636
32637     onRender : function(){
32638         this.render();
32639     },
32640
32641     render : function(bulkRender){
32642         var n = this.node, a = n.attributes;
32643         var targetNode = n.parentNode ?
32644               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32645
32646         if(!this.rendered){
32647             this.rendered = true;
32648
32649             this.renderElements(n, a, targetNode, bulkRender);
32650
32651             if(a.qtip){
32652                if(this.textNode.setAttributeNS){
32653                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32654                    if(a.qtipTitle){
32655                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32656                    }
32657                }else{
32658                    this.textNode.setAttribute("ext:qtip", a.qtip);
32659                    if(a.qtipTitle){
32660                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32661                    }
32662                }
32663             }else if(a.qtipCfg){
32664                 a.qtipCfg.target = Roo.id(this.textNode);
32665                 Roo.QuickTips.register(a.qtipCfg);
32666             }
32667             this.initEvents();
32668             if(!this.node.expanded){
32669                 this.updateExpandIcon();
32670             }
32671         }else{
32672             if(bulkRender === true) {
32673                 targetNode.appendChild(this.wrap);
32674             }
32675         }
32676     },
32677
32678     renderElements : function(n, a, targetNode, bulkRender)
32679     {
32680         // add some indent caching, this helps performance when rendering a large tree
32681         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32682         var t = n.getOwnerTree();
32683         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32684         if (typeof(n.attributes.html) != 'undefined') {
32685             txt = n.attributes.html;
32686         }
32687         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32688         var cb = typeof a.checked == 'boolean';
32689         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32690         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32691             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32692             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32693             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32694             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32695             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32696              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32697                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32698             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32699             "</li>"];
32700
32701         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32702             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32703                                 n.nextSibling.ui.getEl(), buf.join(""));
32704         }else{
32705             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32706         }
32707
32708         this.elNode = this.wrap.childNodes[0];
32709         this.ctNode = this.wrap.childNodes[1];
32710         var cs = this.elNode.childNodes;
32711         this.indentNode = cs[0];
32712         this.ecNode = cs[1];
32713         this.iconNode = cs[2];
32714         var index = 3;
32715         if(cb){
32716             this.checkbox = cs[3];
32717             index++;
32718         }
32719         this.anchor = cs[index];
32720         this.textNode = cs[index].firstChild;
32721     },
32722
32723     getAnchor : function(){
32724         return this.anchor;
32725     },
32726
32727     getTextEl : function(){
32728         return this.textNode;
32729     },
32730
32731     getIconEl : function(){
32732         return this.iconNode;
32733     },
32734
32735     isChecked : function(){
32736         return this.checkbox ? this.checkbox.checked : false;
32737     },
32738
32739     updateExpandIcon : function(){
32740         if(this.rendered){
32741             var n = this.node, c1, c2;
32742             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32743             var hasChild = n.hasChildNodes();
32744             if(hasChild){
32745                 if(n.expanded){
32746                     cls += "-minus";
32747                     c1 = "x-tree-node-collapsed";
32748                     c2 = "x-tree-node-expanded";
32749                 }else{
32750                     cls += "-plus";
32751                     c1 = "x-tree-node-expanded";
32752                     c2 = "x-tree-node-collapsed";
32753                 }
32754                 if(this.wasLeaf){
32755                     this.removeClass("x-tree-node-leaf");
32756                     this.wasLeaf = false;
32757                 }
32758                 if(this.c1 != c1 || this.c2 != c2){
32759                     Roo.fly(this.elNode).replaceClass(c1, c2);
32760                     this.c1 = c1; this.c2 = c2;
32761                 }
32762             }else{
32763                 // this changes non-leafs into leafs if they have no children.
32764                 // it's not very rational behaviour..
32765                 
32766                 if(!this.wasLeaf && this.node.leaf){
32767                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32768                     delete this.c1;
32769                     delete this.c2;
32770                     this.wasLeaf = true;
32771                 }
32772             }
32773             var ecc = "x-tree-ec-icon "+cls;
32774             if(this.ecc != ecc){
32775                 this.ecNode.className = ecc;
32776                 this.ecc = ecc;
32777             }
32778         }
32779     },
32780
32781     getChildIndent : function(){
32782         if(!this.childIndent){
32783             var buf = [];
32784             var p = this.node;
32785             while(p){
32786                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32787                     if(!p.isLast()) {
32788                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32789                     } else {
32790                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32791                     }
32792                 }
32793                 p = p.parentNode;
32794             }
32795             this.childIndent = buf.join("");
32796         }
32797         return this.childIndent;
32798     },
32799
32800     renderIndent : function(){
32801         if(this.rendered){
32802             var indent = "";
32803             var p = this.node.parentNode;
32804             if(p){
32805                 indent = p.ui.getChildIndent();
32806             }
32807             if(this.indentMarkup != indent){ // don't rerender if not required
32808                 this.indentNode.innerHTML = indent;
32809                 this.indentMarkup = indent;
32810             }
32811             this.updateExpandIcon();
32812         }
32813     }
32814 };
32815
32816 Roo.tree.RootTreeNodeUI = function(){
32817     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32818 };
32819 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32820     render : function(){
32821         if(!this.rendered){
32822             var targetNode = this.node.ownerTree.innerCt.dom;
32823             this.node.expanded = true;
32824             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32825             this.wrap = this.ctNode = targetNode.firstChild;
32826         }
32827     },
32828     collapse : function(){
32829     },
32830     expand : function(){
32831     }
32832 });/*
32833  * Based on:
32834  * Ext JS Library 1.1.1
32835  * Copyright(c) 2006-2007, Ext JS, LLC.
32836  *
32837  * Originally Released Under LGPL - original licence link has changed is not relivant.
32838  *
32839  * Fork - LGPL
32840  * <script type="text/javascript">
32841  */
32842 /**
32843  * @class Roo.tree.TreeLoader
32844  * @extends Roo.util.Observable
32845  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32846  * nodes from a specified URL. The response must be a javascript Array definition
32847  * who's elements are node definition objects. eg:
32848  * <pre><code>
32849    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32850     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32851 </code></pre>
32852  * <br><br>
32853  * A server request is sent, and child nodes are loaded only when a node is expanded.
32854  * The loading node's id is passed to the server under the parameter name "node" to
32855  * enable the server to produce the correct child nodes.
32856  * <br><br>
32857  * To pass extra parameters, an event handler may be attached to the "beforeload"
32858  * event, and the parameters specified in the TreeLoader's baseParams property:
32859  * <pre><code>
32860     myTreeLoader.on("beforeload", function(treeLoader, node) {
32861         this.baseParams.category = node.attributes.category;
32862     }, this);
32863 </code></pre><
32864  * This would pass an HTTP parameter called "category" to the server containing
32865  * the value of the Node's "category" attribute.
32866  * @constructor
32867  * Creates a new Treeloader.
32868  * @param {Object} config A config object containing config properties.
32869  */
32870 Roo.tree.TreeLoader = function(config){
32871     this.baseParams = {};
32872     this.requestMethod = "POST";
32873     Roo.apply(this, config);
32874
32875     this.addEvents({
32876     
32877         /**
32878          * @event beforeload
32879          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32880          * @param {Object} This TreeLoader object.
32881          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32882          * @param {Object} callback The callback function specified in the {@link #load} call.
32883          */
32884         beforeload : true,
32885         /**
32886          * @event load
32887          * Fires when the node has been successfuly loaded.
32888          * @param {Object} This TreeLoader object.
32889          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32890          * @param {Object} response The response object containing the data from the server.
32891          */
32892         load : true,
32893         /**
32894          * @event loadexception
32895          * Fires if the network request failed.
32896          * @param {Object} This TreeLoader object.
32897          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32898          * @param {Object} response The response object containing the data from the server.
32899          */
32900         loadexception : true,
32901         /**
32902          * @event create
32903          * Fires before a node is created, enabling you to return custom Node types 
32904          * @param {Object} This TreeLoader object.
32905          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32906          */
32907         create : true
32908     });
32909
32910     Roo.tree.TreeLoader.superclass.constructor.call(this);
32911 };
32912
32913 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32914     /**
32915     * @cfg {String} dataUrl The URL from which to request a Json string which
32916     * specifies an array of node definition object representing the child nodes
32917     * to be loaded.
32918     */
32919     /**
32920     * @cfg {Object} baseParams (optional) An object containing properties which
32921     * specify HTTP parameters to be passed to each request for child nodes.
32922     */
32923     /**
32924     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32925     * created by this loader. If the attributes sent by the server have an attribute in this object,
32926     * they take priority.
32927     */
32928     /**
32929     * @cfg {Object} uiProviders (optional) An object containing properties which
32930     * 
32931     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32932     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32933     * <i>uiProvider</i> attribute of a returned child node is a string rather
32934     * than a reference to a TreeNodeUI implementation, this that string value
32935     * is used as a property name in the uiProviders object. You can define the provider named
32936     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32937     */
32938     uiProviders : {},
32939
32940     /**
32941     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32942     * child nodes before loading.
32943     */
32944     clearOnLoad : true,
32945
32946     /**
32947     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32948     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32949     * Grid query { data : [ .....] }
32950     */
32951     
32952     root : false,
32953      /**
32954     * @cfg {String} queryParam (optional) 
32955     * Name of the query as it will be passed on the querystring (defaults to 'node')
32956     * eg. the request will be ?node=[id]
32957     */
32958     
32959     
32960     queryParam: false,
32961     
32962     /**
32963      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32964      * This is called automatically when a node is expanded, but may be used to reload
32965      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32966      * @param {Roo.tree.TreeNode} node
32967      * @param {Function} callback
32968      */
32969     load : function(node, callback){
32970         if(this.clearOnLoad){
32971             while(node.firstChild){
32972                 node.removeChild(node.firstChild);
32973             }
32974         }
32975         if(node.attributes.children){ // preloaded json children
32976             var cs = node.attributes.children;
32977             for(var i = 0, len = cs.length; i < len; i++){
32978                 node.appendChild(this.createNode(cs[i]));
32979             }
32980             if(typeof callback == "function"){
32981                 callback();
32982             }
32983         }else if(this.dataUrl){
32984             this.requestData(node, callback);
32985         }
32986     },
32987
32988     getParams: function(node){
32989         var buf = [], bp = this.baseParams;
32990         for(var key in bp){
32991             if(typeof bp[key] != "function"){
32992                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32993             }
32994         }
32995         var n = this.queryParam === false ? 'node' : this.queryParam;
32996         buf.push(n + "=", encodeURIComponent(node.id));
32997         return buf.join("");
32998     },
32999
33000     requestData : function(node, callback){
33001         if(this.fireEvent("beforeload", this, node, callback) !== false){
33002             this.transId = Roo.Ajax.request({
33003                 method:this.requestMethod,
33004                 url: this.dataUrl||this.url,
33005                 success: this.handleResponse,
33006                 failure: this.handleFailure,
33007                 scope: this,
33008                 argument: {callback: callback, node: node},
33009                 params: this.getParams(node)
33010             });
33011         }else{
33012             // if the load is cancelled, make sure we notify
33013             // the node that we are done
33014             if(typeof callback == "function"){
33015                 callback();
33016             }
33017         }
33018     },
33019
33020     isLoading : function(){
33021         return this.transId ? true : false;
33022     },
33023
33024     abort : function(){
33025         if(this.isLoading()){
33026             Roo.Ajax.abort(this.transId);
33027         }
33028     },
33029
33030     // private
33031     createNode : function(attr)
33032     {
33033         // apply baseAttrs, nice idea Corey!
33034         if(this.baseAttrs){
33035             Roo.applyIf(attr, this.baseAttrs);
33036         }
33037         if(this.applyLoader !== false){
33038             attr.loader = this;
33039         }
33040         // uiProvider = depreciated..
33041         
33042         if(typeof(attr.uiProvider) == 'string'){
33043            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33044                 /**  eval:var:attr */ eval(attr.uiProvider);
33045         }
33046         if(typeof(this.uiProviders['default']) != 'undefined') {
33047             attr.uiProvider = this.uiProviders['default'];
33048         }
33049         
33050         this.fireEvent('create', this, attr);
33051         
33052         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33053         return(attr.leaf ?
33054                         new Roo.tree.TreeNode(attr) :
33055                         new Roo.tree.AsyncTreeNode(attr));
33056     },
33057
33058     processResponse : function(response, node, callback)
33059     {
33060         var json = response.responseText;
33061         try {
33062             
33063             var o = Roo.decode(json);
33064             
33065             if (!o.success) {
33066                 // it's a failure condition.
33067                 var a = response.argument;
33068                 this.fireEvent("loadexception", this, a.node, response);
33069                 Roo.log("Load failed - should have a handler really");
33070                 return;
33071             }
33072             
33073             if (this.root !== false) {
33074                 o = o[this.root];
33075             }
33076             
33077             for(var i = 0, len = o.length; i < len; i++){
33078                 var n = this.createNode(o[i]);
33079                 if(n){
33080                     node.appendChild(n);
33081                 }
33082             }
33083             if(typeof callback == "function"){
33084                 callback(this, node);
33085             }
33086         }catch(e){
33087             this.handleFailure(response);
33088         }
33089     },
33090
33091     handleResponse : function(response){
33092         this.transId = false;
33093         var a = response.argument;
33094         this.processResponse(response, a.node, a.callback);
33095         this.fireEvent("load", this, a.node, response);
33096     },
33097
33098     handleFailure : function(response)
33099     {
33100         // should handle failure better..
33101         this.transId = false;
33102         var a = response.argument;
33103         this.fireEvent("loadexception", this, a.node, response);
33104         if(typeof a.callback == "function"){
33105             a.callback(this, a.node);
33106         }
33107     }
33108 });/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118
33119 /**
33120 * @class Roo.tree.TreeFilter
33121 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33122 * @param {TreePanel} tree
33123 * @param {Object} config (optional)
33124  */
33125 Roo.tree.TreeFilter = function(tree, config){
33126     this.tree = tree;
33127     this.filtered = {};
33128     Roo.apply(this, config);
33129 };
33130
33131 Roo.tree.TreeFilter.prototype = {
33132     clearBlank:false,
33133     reverse:false,
33134     autoClear:false,
33135     remove:false,
33136
33137      /**
33138      * Filter the data by a specific attribute.
33139      * @param {String/RegExp} value Either string that the attribute value
33140      * should start with or a RegExp to test against the attribute
33141      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33142      * @param {TreeNode} startNode (optional) The node to start the filter at.
33143      */
33144     filter : function(value, attr, startNode){
33145         attr = attr || "text";
33146         var f;
33147         if(typeof value == "string"){
33148             var vlen = value.length;
33149             // auto clear empty filter
33150             if(vlen == 0 && this.clearBlank){
33151                 this.clear();
33152                 return;
33153             }
33154             value = value.toLowerCase();
33155             f = function(n){
33156                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33157             };
33158         }else if(value.exec){ // regex?
33159             f = function(n){
33160                 return value.test(n.attributes[attr]);
33161             };
33162         }else{
33163             throw 'Illegal filter type, must be string or regex';
33164         }
33165         this.filterBy(f, null, startNode);
33166         },
33167
33168     /**
33169      * Filter by a function. The passed function will be called with each
33170      * node in the tree (or from the startNode). If the function returns true, the node is kept
33171      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33172      * @param {Function} fn The filter function
33173      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33174      */
33175     filterBy : function(fn, scope, startNode){
33176         startNode = startNode || this.tree.root;
33177         if(this.autoClear){
33178             this.clear();
33179         }
33180         var af = this.filtered, rv = this.reverse;
33181         var f = function(n){
33182             if(n == startNode){
33183                 return true;
33184             }
33185             if(af[n.id]){
33186                 return false;
33187             }
33188             var m = fn.call(scope || n, n);
33189             if(!m || rv){
33190                 af[n.id] = n;
33191                 n.ui.hide();
33192                 return false;
33193             }
33194             return true;
33195         };
33196         startNode.cascade(f);
33197         if(this.remove){
33198            for(var id in af){
33199                if(typeof id != "function"){
33200                    var n = af[id];
33201                    if(n && n.parentNode){
33202                        n.parentNode.removeChild(n);
33203                    }
33204                }
33205            }
33206         }
33207     },
33208
33209     /**
33210      * Clears the current filter. Note: with the "remove" option
33211      * set a filter cannot be cleared.
33212      */
33213     clear : function(){
33214         var t = this.tree;
33215         var af = this.filtered;
33216         for(var id in af){
33217             if(typeof id != "function"){
33218                 var n = af[id];
33219                 if(n){
33220                     n.ui.show();
33221                 }
33222             }
33223         }
33224         this.filtered = {};
33225     }
33226 };
33227 /*
33228  * Based on:
33229  * Ext JS Library 1.1.1
33230  * Copyright(c) 2006-2007, Ext JS, LLC.
33231  *
33232  * Originally Released Under LGPL - original licence link has changed is not relivant.
33233  *
33234  * Fork - LGPL
33235  * <script type="text/javascript">
33236  */
33237  
33238
33239 /**
33240  * @class Roo.tree.TreeSorter
33241  * Provides sorting of nodes in a TreePanel
33242  * 
33243  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33244  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33245  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33246  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33247  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33248  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33249  * @constructor
33250  * @param {TreePanel} tree
33251  * @param {Object} config
33252  */
33253 Roo.tree.TreeSorter = function(tree, config){
33254     Roo.apply(this, config);
33255     tree.on("beforechildrenrendered", this.doSort, this);
33256     tree.on("append", this.updateSort, this);
33257     tree.on("insert", this.updateSort, this);
33258     
33259     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33260     var p = this.property || "text";
33261     var sortType = this.sortType;
33262     var fs = this.folderSort;
33263     var cs = this.caseSensitive === true;
33264     var leafAttr = this.leafAttr || 'leaf';
33265
33266     this.sortFn = function(n1, n2){
33267         if(fs){
33268             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33269                 return 1;
33270             }
33271             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33272                 return -1;
33273             }
33274         }
33275         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33276         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33277         if(v1 < v2){
33278                         return dsc ? +1 : -1;
33279                 }else if(v1 > v2){
33280                         return dsc ? -1 : +1;
33281         }else{
33282                 return 0;
33283         }
33284     };
33285 };
33286
33287 Roo.tree.TreeSorter.prototype = {
33288     doSort : function(node){
33289         node.sort(this.sortFn);
33290     },
33291     
33292     compareNodes : function(n1, n2){
33293         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33294     },
33295     
33296     updateSort : function(tree, node){
33297         if(node.childrenRendered){
33298             this.doSort.defer(1, this, [node]);
33299         }
33300     }
33301 };/*
33302  * Based on:
33303  * Ext JS Library 1.1.1
33304  * Copyright(c) 2006-2007, Ext JS, LLC.
33305  *
33306  * Originally Released Under LGPL - original licence link has changed is not relivant.
33307  *
33308  * Fork - LGPL
33309  * <script type="text/javascript">
33310  */
33311
33312 if(Roo.dd.DropZone){
33313     
33314 Roo.tree.TreeDropZone = function(tree, config){
33315     this.allowParentInsert = false;
33316     this.allowContainerDrop = false;
33317     this.appendOnly = false;
33318     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33319     this.tree = tree;
33320     this.lastInsertClass = "x-tree-no-status";
33321     this.dragOverData = {};
33322 };
33323
33324 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33325     ddGroup : "TreeDD",
33326     
33327     expandDelay : 1000,
33328     
33329     expandNode : function(node){
33330         if(node.hasChildNodes() && !node.isExpanded()){
33331             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33332         }
33333     },
33334     
33335     queueExpand : function(node){
33336         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33337     },
33338     
33339     cancelExpand : function(){
33340         if(this.expandProcId){
33341             clearTimeout(this.expandProcId);
33342             this.expandProcId = false;
33343         }
33344     },
33345     
33346     isValidDropPoint : function(n, pt, dd, e, data){
33347         if(!n || !data){ return false; }
33348         var targetNode = n.node;
33349         var dropNode = data.node;
33350         // default drop rules
33351         if(!(targetNode && targetNode.isTarget && pt)){
33352             return false;
33353         }
33354         if(pt == "append" && targetNode.allowChildren === false){
33355             return false;
33356         }
33357         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33358             return false;
33359         }
33360         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33361             return false;
33362         }
33363         // reuse the object
33364         var overEvent = this.dragOverData;
33365         overEvent.tree = this.tree;
33366         overEvent.target = targetNode;
33367         overEvent.data = data;
33368         overEvent.point = pt;
33369         overEvent.source = dd;
33370         overEvent.rawEvent = e;
33371         overEvent.dropNode = dropNode;
33372         overEvent.cancel = false;  
33373         var result = this.tree.fireEvent("nodedragover", overEvent);
33374         return overEvent.cancel === false && result !== false;
33375     },
33376     
33377     getDropPoint : function(e, n, dd){
33378         var tn = n.node;
33379         if(tn.isRoot){
33380             return tn.allowChildren !== false ? "append" : false; // always append for root
33381         }
33382         var dragEl = n.ddel;
33383         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33384         var y = Roo.lib.Event.getPageY(e);
33385         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33386         
33387         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33388         var noAppend = tn.allowChildren === false;
33389         if(this.appendOnly || tn.parentNode.allowChildren === false){
33390             return noAppend ? false : "append";
33391         }
33392         var noBelow = false;
33393         if(!this.allowParentInsert){
33394             noBelow = tn.hasChildNodes() && tn.isExpanded();
33395         }
33396         var q = (b - t) / (noAppend ? 2 : 3);
33397         if(y >= t && y < (t + q)){
33398             return "above";
33399         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33400             return "below";
33401         }else{
33402             return "append";
33403         }
33404     },
33405     
33406     onNodeEnter : function(n, dd, e, data){
33407         this.cancelExpand();
33408     },
33409     
33410     onNodeOver : function(n, dd, e, data){
33411         var pt = this.getDropPoint(e, n, dd);
33412         var node = n.node;
33413         
33414         // auto node expand check
33415         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33416             this.queueExpand(node);
33417         }else if(pt != "append"){
33418             this.cancelExpand();
33419         }
33420         
33421         // set the insert point style on the target node
33422         var returnCls = this.dropNotAllowed;
33423         if(this.isValidDropPoint(n, pt, dd, e, data)){
33424            if(pt){
33425                var el = n.ddel;
33426                var cls;
33427                if(pt == "above"){
33428                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33429                    cls = "x-tree-drag-insert-above";
33430                }else if(pt == "below"){
33431                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33432                    cls = "x-tree-drag-insert-below";
33433                }else{
33434                    returnCls = "x-tree-drop-ok-append";
33435                    cls = "x-tree-drag-append";
33436                }
33437                if(this.lastInsertClass != cls){
33438                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33439                    this.lastInsertClass = cls;
33440                }
33441            }
33442        }
33443        return returnCls;
33444     },
33445     
33446     onNodeOut : function(n, dd, e, data){
33447         this.cancelExpand();
33448         this.removeDropIndicators(n);
33449     },
33450     
33451     onNodeDrop : function(n, dd, e, data){
33452         var point = this.getDropPoint(e, n, dd);
33453         var targetNode = n.node;
33454         targetNode.ui.startDrop();
33455         if(!this.isValidDropPoint(n, point, dd, e, data)){
33456             targetNode.ui.endDrop();
33457             return false;
33458         }
33459         // first try to find the drop node
33460         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33461         var dropEvent = {
33462             tree : this.tree,
33463             target: targetNode,
33464             data: data,
33465             point: point,
33466             source: dd,
33467             rawEvent: e,
33468             dropNode: dropNode,
33469             cancel: !dropNode   
33470         };
33471         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33472         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33473             targetNode.ui.endDrop();
33474             return false;
33475         }
33476         // allow target changing
33477         targetNode = dropEvent.target;
33478         if(point == "append" && !targetNode.isExpanded()){
33479             targetNode.expand(false, null, function(){
33480                 this.completeDrop(dropEvent);
33481             }.createDelegate(this));
33482         }else{
33483             this.completeDrop(dropEvent);
33484         }
33485         return true;
33486     },
33487     
33488     completeDrop : function(de){
33489         var ns = de.dropNode, p = de.point, t = de.target;
33490         if(!(ns instanceof Array)){
33491             ns = [ns];
33492         }
33493         var n;
33494         for(var i = 0, len = ns.length; i < len; i++){
33495             n = ns[i];
33496             if(p == "above"){
33497                 t.parentNode.insertBefore(n, t);
33498             }else if(p == "below"){
33499                 t.parentNode.insertBefore(n, t.nextSibling);
33500             }else{
33501                 t.appendChild(n);
33502             }
33503         }
33504         n.ui.focus();
33505         if(this.tree.hlDrop){
33506             n.ui.highlight();
33507         }
33508         t.ui.endDrop();
33509         this.tree.fireEvent("nodedrop", de);
33510     },
33511     
33512     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33513         if(this.tree.hlDrop){
33514             dropNode.ui.focus();
33515             dropNode.ui.highlight();
33516         }
33517         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33518     },
33519     
33520     getTree : function(){
33521         return this.tree;
33522     },
33523     
33524     removeDropIndicators : function(n){
33525         if(n && n.ddel){
33526             var el = n.ddel;
33527             Roo.fly(el).removeClass([
33528                     "x-tree-drag-insert-above",
33529                     "x-tree-drag-insert-below",
33530                     "x-tree-drag-append"]);
33531             this.lastInsertClass = "_noclass";
33532         }
33533     },
33534     
33535     beforeDragDrop : function(target, e, id){
33536         this.cancelExpand();
33537         return true;
33538     },
33539     
33540     afterRepair : function(data){
33541         if(data && Roo.enableFx){
33542             data.node.ui.highlight();
33543         }
33544         this.hideProxy();
33545     }    
33546 });
33547
33548 }
33549 /*
33550  * Based on:
33551  * Ext JS Library 1.1.1
33552  * Copyright(c) 2006-2007, Ext JS, LLC.
33553  *
33554  * Originally Released Under LGPL - original licence link has changed is not relivant.
33555  *
33556  * Fork - LGPL
33557  * <script type="text/javascript">
33558  */
33559  
33560
33561 if(Roo.dd.DragZone){
33562 Roo.tree.TreeDragZone = function(tree, config){
33563     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33564     this.tree = tree;
33565 };
33566
33567 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33568     ddGroup : "TreeDD",
33569     
33570     onBeforeDrag : function(data, e){
33571         var n = data.node;
33572         return n && n.draggable && !n.disabled;
33573     },
33574     
33575     onInitDrag : function(e){
33576         var data = this.dragData;
33577         this.tree.getSelectionModel().select(data.node);
33578         this.proxy.update("");
33579         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33580         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33581     },
33582     
33583     getRepairXY : function(e, data){
33584         return data.node.ui.getDDRepairXY();
33585     },
33586     
33587     onEndDrag : function(data, e){
33588         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33589     },
33590     
33591     onValidDrop : function(dd, e, id){
33592         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33593         this.hideProxy();
33594     },
33595     
33596     beforeInvalidDrop : function(e, id){
33597         // this scrolls the original position back into view
33598         var sm = this.tree.getSelectionModel();
33599         sm.clearSelections();
33600         sm.select(this.dragData.node);
33601     }
33602 });
33603 }/*
33604  * Based on:
33605  * Ext JS Library 1.1.1
33606  * Copyright(c) 2006-2007, Ext JS, LLC.
33607  *
33608  * Originally Released Under LGPL - original licence link has changed is not relivant.
33609  *
33610  * Fork - LGPL
33611  * <script type="text/javascript">
33612  */
33613 /**
33614  * @class Roo.tree.TreeEditor
33615  * @extends Roo.Editor
33616  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33617  * as the editor field.
33618  * @constructor
33619  * @param {Object} config (used to be the tree panel.)
33620  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33621  * 
33622  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33623  * @cfg {Roo.form.TextField|Object} field The field configuration
33624  *
33625  * 
33626  */
33627 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33628     var tree = config;
33629     var field;
33630     if (oldconfig) { // old style..
33631         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33632     } else {
33633         // new style..
33634         tree = config.tree;
33635         config.field = config.field  || {};
33636         config.field.xtype = 'TextField';
33637         field = Roo.factory(config.field, Roo.form);
33638     }
33639     config = config || {};
33640     
33641     
33642     this.addEvents({
33643         /**
33644          * @event beforenodeedit
33645          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33646          * false from the handler of this event.
33647          * @param {Editor} this
33648          * @param {Roo.tree.Node} node 
33649          */
33650         "beforenodeedit" : true
33651     });
33652     
33653     //Roo.log(config);
33654     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33655
33656     this.tree = tree;
33657
33658     tree.on('beforeclick', this.beforeNodeClick, this);
33659     tree.getTreeEl().on('mousedown', this.hide, this);
33660     this.on('complete', this.updateNode, this);
33661     this.on('beforestartedit', this.fitToTree, this);
33662     this.on('startedit', this.bindScroll, this, {delay:10});
33663     this.on('specialkey', this.onSpecialKey, this);
33664 };
33665
33666 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33667     /**
33668      * @cfg {String} alignment
33669      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33670      */
33671     alignment: "l-l",
33672     // inherit
33673     autoSize: false,
33674     /**
33675      * @cfg {Boolean} hideEl
33676      * True to hide the bound element while the editor is displayed (defaults to false)
33677      */
33678     hideEl : false,
33679     /**
33680      * @cfg {String} cls
33681      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33682      */
33683     cls: "x-small-editor x-tree-editor",
33684     /**
33685      * @cfg {Boolean} shim
33686      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33687      */
33688     shim:false,
33689     // inherit
33690     shadow:"frame",
33691     /**
33692      * @cfg {Number} maxWidth
33693      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33694      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33695      * scroll and client offsets into account prior to each edit.
33696      */
33697     maxWidth: 250,
33698
33699     editDelay : 350,
33700
33701     // private
33702     fitToTree : function(ed, el){
33703         var td = this.tree.getTreeEl().dom, nd = el.dom;
33704         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33705             td.scrollLeft = nd.offsetLeft;
33706         }
33707         var w = Math.min(
33708                 this.maxWidth,
33709                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33710         this.setSize(w, '');
33711         
33712         return this.fireEvent('beforenodeedit', this, this.editNode);
33713         
33714     },
33715
33716     // private
33717     triggerEdit : function(node){
33718         this.completeEdit();
33719         this.editNode = node;
33720         this.startEdit(node.ui.textNode, node.text);
33721     },
33722
33723     // private
33724     bindScroll : function(){
33725         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33726     },
33727
33728     // private
33729     beforeNodeClick : function(node, e){
33730         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33731         this.lastClick = new Date();
33732         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33733             e.stopEvent();
33734             this.triggerEdit(node);
33735             return false;
33736         }
33737         return true;
33738     },
33739
33740     // private
33741     updateNode : function(ed, value){
33742         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33743         this.editNode.setText(value);
33744     },
33745
33746     // private
33747     onHide : function(){
33748         Roo.tree.TreeEditor.superclass.onHide.call(this);
33749         if(this.editNode){
33750             this.editNode.ui.focus();
33751         }
33752     },
33753
33754     // private
33755     onSpecialKey : function(field, e){
33756         var k = e.getKey();
33757         if(k == e.ESC){
33758             e.stopEvent();
33759             this.cancelEdit();
33760         }else if(k == e.ENTER && !e.hasModifier()){
33761             e.stopEvent();
33762             this.completeEdit();
33763         }
33764     }
33765 });//<Script type="text/javascript">
33766 /*
33767  * Based on:
33768  * Ext JS Library 1.1.1
33769  * Copyright(c) 2006-2007, Ext JS, LLC.
33770  *
33771  * Originally Released Under LGPL - original licence link has changed is not relivant.
33772  *
33773  * Fork - LGPL
33774  * <script type="text/javascript">
33775  */
33776  
33777 /**
33778  * Not documented??? - probably should be...
33779  */
33780
33781 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33782     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33783     
33784     renderElements : function(n, a, targetNode, bulkRender){
33785         //consel.log("renderElements?");
33786         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33787
33788         var t = n.getOwnerTree();
33789         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33790         
33791         var cols = t.columns;
33792         var bw = t.borderWidth;
33793         var c = cols[0];
33794         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33795          var cb = typeof a.checked == "boolean";
33796         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33797         var colcls = 'x-t-' + tid + '-c0';
33798         var buf = [
33799             '<li class="x-tree-node">',
33800             
33801                 
33802                 '<div class="x-tree-node-el ', a.cls,'">',
33803                     // extran...
33804                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33805                 
33806                 
33807                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33808                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33809                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33810                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33811                            (a.iconCls ? ' '+a.iconCls : ''),
33812                            '" unselectable="on" />',
33813                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33814                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33815                              
33816                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33817                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33818                             '<span unselectable="on" qtip="' + tx + '">',
33819                              tx,
33820                              '</span></a>' ,
33821                     '</div>',
33822                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33823                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33824                  ];
33825         for(var i = 1, len = cols.length; i < len; i++){
33826             c = cols[i];
33827             colcls = 'x-t-' + tid + '-c' +i;
33828             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33829             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33830                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33831                       "</div>");
33832          }
33833          
33834          buf.push(
33835             '</a>',
33836             '<div class="x-clear"></div></div>',
33837             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33838             "</li>");
33839         
33840         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33841             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33842                                 n.nextSibling.ui.getEl(), buf.join(""));
33843         }else{
33844             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33845         }
33846         var el = this.wrap.firstChild;
33847         this.elRow = el;
33848         this.elNode = el.firstChild;
33849         this.ranchor = el.childNodes[1];
33850         this.ctNode = this.wrap.childNodes[1];
33851         var cs = el.firstChild.childNodes;
33852         this.indentNode = cs[0];
33853         this.ecNode = cs[1];
33854         this.iconNode = cs[2];
33855         var index = 3;
33856         if(cb){
33857             this.checkbox = cs[3];
33858             index++;
33859         }
33860         this.anchor = cs[index];
33861         
33862         this.textNode = cs[index].firstChild;
33863         
33864         //el.on("click", this.onClick, this);
33865         //el.on("dblclick", this.onDblClick, this);
33866         
33867         
33868        // console.log(this);
33869     },
33870     initEvents : function(){
33871         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33872         
33873             
33874         var a = this.ranchor;
33875
33876         var el = Roo.get(a);
33877
33878         if(Roo.isOpera){ // opera render bug ignores the CSS
33879             el.setStyle("text-decoration", "none");
33880         }
33881
33882         el.on("click", this.onClick, this);
33883         el.on("dblclick", this.onDblClick, this);
33884         el.on("contextmenu", this.onContextMenu, this);
33885         
33886     },
33887     
33888     /*onSelectedChange : function(state){
33889         if(state){
33890             this.focus();
33891             this.addClass("x-tree-selected");
33892         }else{
33893             //this.blur();
33894             this.removeClass("x-tree-selected");
33895         }
33896     },*/
33897     addClass : function(cls){
33898         if(this.elRow){
33899             Roo.fly(this.elRow).addClass(cls);
33900         }
33901         
33902     },
33903     
33904     
33905     removeClass : function(cls){
33906         if(this.elRow){
33907             Roo.fly(this.elRow).removeClass(cls);
33908         }
33909     }
33910
33911     
33912     
33913 });//<Script type="text/javascript">
33914
33915 /*
33916  * Based on:
33917  * Ext JS Library 1.1.1
33918  * Copyright(c) 2006-2007, Ext JS, LLC.
33919  *
33920  * Originally Released Under LGPL - original licence link has changed is not relivant.
33921  *
33922  * Fork - LGPL
33923  * <script type="text/javascript">
33924  */
33925  
33926
33927 /**
33928  * @class Roo.tree.ColumnTree
33929  * @extends Roo.data.TreePanel
33930  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33931  * @cfg {int} borderWidth  compined right/left border allowance
33932  * @constructor
33933  * @param {String/HTMLElement/Element} el The container element
33934  * @param {Object} config
33935  */
33936 Roo.tree.ColumnTree =  function(el, config)
33937 {
33938    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33939    this.addEvents({
33940         /**
33941         * @event resize
33942         * Fire this event on a container when it resizes
33943         * @param {int} w Width
33944         * @param {int} h Height
33945         */
33946        "resize" : true
33947     });
33948     this.on('resize', this.onResize, this);
33949 };
33950
33951 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33952     //lines:false,
33953     
33954     
33955     borderWidth: Roo.isBorderBox ? 0 : 2, 
33956     headEls : false,
33957     
33958     render : function(){
33959         // add the header.....
33960        
33961         Roo.tree.ColumnTree.superclass.render.apply(this);
33962         
33963         this.el.addClass('x-column-tree');
33964         
33965         this.headers = this.el.createChild(
33966             {cls:'x-tree-headers'},this.innerCt.dom);
33967    
33968         var cols = this.columns, c;
33969         var totalWidth = 0;
33970         this.headEls = [];
33971         var  len = cols.length;
33972         for(var i = 0; i < len; i++){
33973              c = cols[i];
33974              totalWidth += c.width;
33975             this.headEls.push(this.headers.createChild({
33976                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33977                  cn: {
33978                      cls:'x-tree-hd-text',
33979                      html: c.header
33980                  },
33981                  style:'width:'+(c.width-this.borderWidth)+'px;'
33982              }));
33983         }
33984         this.headers.createChild({cls:'x-clear'});
33985         // prevent floats from wrapping when clipped
33986         this.headers.setWidth(totalWidth);
33987         //this.innerCt.setWidth(totalWidth);
33988         this.innerCt.setStyle({ overflow: 'auto' });
33989         this.onResize(this.width, this.height);
33990              
33991         
33992     },
33993     onResize : function(w,h)
33994     {
33995         this.height = h;
33996         this.width = w;
33997         // resize cols..
33998         this.innerCt.setWidth(this.width);
33999         this.innerCt.setHeight(this.height-20);
34000         
34001         // headers...
34002         var cols = this.columns, c;
34003         var totalWidth = 0;
34004         var expEl = false;
34005         var len = cols.length;
34006         for(var i = 0; i < len; i++){
34007             c = cols[i];
34008             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34009                 // it's the expander..
34010                 expEl  = this.headEls[i];
34011                 continue;
34012             }
34013             totalWidth += c.width;
34014             
34015         }
34016         if (expEl) {
34017             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34018         }
34019         this.headers.setWidth(w-20);
34020
34021         
34022         
34023         
34024     }
34025 });
34026 /*
34027  * Based on:
34028  * Ext JS Library 1.1.1
34029  * Copyright(c) 2006-2007, Ext JS, LLC.
34030  *
34031  * Originally Released Under LGPL - original licence link has changed is not relivant.
34032  *
34033  * Fork - LGPL
34034  * <script type="text/javascript">
34035  */
34036  
34037 /**
34038  * @class Roo.menu.Menu
34039  * @extends Roo.util.Observable
34040  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34041  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34042  * @constructor
34043  * Creates a new Menu
34044  * @param {Object} config Configuration options
34045  */
34046 Roo.menu.Menu = function(config){
34047     Roo.apply(this, config);
34048     this.id = this.id || Roo.id();
34049     this.addEvents({
34050         /**
34051          * @event beforeshow
34052          * Fires before this menu is displayed
34053          * @param {Roo.menu.Menu} this
34054          */
34055         beforeshow : true,
34056         /**
34057          * @event beforehide
34058          * Fires before this menu is hidden
34059          * @param {Roo.menu.Menu} this
34060          */
34061         beforehide : true,
34062         /**
34063          * @event show
34064          * Fires after this menu is displayed
34065          * @param {Roo.menu.Menu} this
34066          */
34067         show : true,
34068         /**
34069          * @event hide
34070          * Fires after this menu is hidden
34071          * @param {Roo.menu.Menu} this
34072          */
34073         hide : true,
34074         /**
34075          * @event click
34076          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34077          * @param {Roo.menu.Menu} this
34078          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34079          * @param {Roo.EventObject} e
34080          */
34081         click : true,
34082         /**
34083          * @event mouseover
34084          * Fires when the mouse is hovering over this menu
34085          * @param {Roo.menu.Menu} this
34086          * @param {Roo.EventObject} e
34087          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34088          */
34089         mouseover : true,
34090         /**
34091          * @event mouseout
34092          * Fires when the mouse exits this menu
34093          * @param {Roo.menu.Menu} this
34094          * @param {Roo.EventObject} e
34095          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34096          */
34097         mouseout : true,
34098         /**
34099          * @event itemclick
34100          * Fires when a menu item contained in this menu is clicked
34101          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34102          * @param {Roo.EventObject} e
34103          */
34104         itemclick: true
34105     });
34106     if (this.registerMenu) {
34107         Roo.menu.MenuMgr.register(this);
34108     }
34109     
34110     var mis = this.items;
34111     this.items = new Roo.util.MixedCollection();
34112     if(mis){
34113         this.add.apply(this, mis);
34114     }
34115 };
34116
34117 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34118     /**
34119      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34120      */
34121     minWidth : 120,
34122     /**
34123      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34124      * for bottom-right shadow (defaults to "sides")
34125      */
34126     shadow : "sides",
34127     /**
34128      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34129      * this menu (defaults to "tl-tr?")
34130      */
34131     subMenuAlign : "tl-tr?",
34132     /**
34133      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34134      * relative to its element of origin (defaults to "tl-bl?")
34135      */
34136     defaultAlign : "tl-bl?",
34137     /**
34138      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34139      */
34140     allowOtherMenus : false,
34141     /**
34142      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34143      */
34144     registerMenu : true,
34145
34146     hidden:true,
34147
34148     // private
34149     render : function(){
34150         if(this.el){
34151             return;
34152         }
34153         var el = this.el = new Roo.Layer({
34154             cls: "x-menu",
34155             shadow:this.shadow,
34156             constrain: false,
34157             parentEl: this.parentEl || document.body,
34158             zindex:15000
34159         });
34160
34161         this.keyNav = new Roo.menu.MenuNav(this);
34162
34163         if(this.plain){
34164             el.addClass("x-menu-plain");
34165         }
34166         if(this.cls){
34167             el.addClass(this.cls);
34168         }
34169         // generic focus element
34170         this.focusEl = el.createChild({
34171             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34172         });
34173         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34174         ul.on("click", this.onClick, this);
34175         ul.on("mouseover", this.onMouseOver, this);
34176         ul.on("mouseout", this.onMouseOut, this);
34177         this.items.each(function(item){
34178             var li = document.createElement("li");
34179             li.className = "x-menu-list-item";
34180             ul.dom.appendChild(li);
34181             item.render(li, this);
34182         }, this);
34183         this.ul = ul;
34184         this.autoWidth();
34185     },
34186
34187     // private
34188     autoWidth : function(){
34189         var el = this.el, ul = this.ul;
34190         if(!el){
34191             return;
34192         }
34193         var w = this.width;
34194         if(w){
34195             el.setWidth(w);
34196         }else if(Roo.isIE){
34197             el.setWidth(this.minWidth);
34198             var t = el.dom.offsetWidth; // force recalc
34199             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34200         }
34201     },
34202
34203     // private
34204     delayAutoWidth : function(){
34205         if(this.rendered){
34206             if(!this.awTask){
34207                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34208             }
34209             this.awTask.delay(20);
34210         }
34211     },
34212
34213     // private
34214     findTargetItem : function(e){
34215         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34216         if(t && t.menuItemId){
34217             return this.items.get(t.menuItemId);
34218         }
34219     },
34220
34221     // private
34222     onClick : function(e){
34223         var t;
34224         if(t = this.findTargetItem(e)){
34225             t.onClick(e);
34226             this.fireEvent("click", this, t, e);
34227         }
34228     },
34229
34230     // private
34231     setActiveItem : function(item, autoExpand){
34232         if(item != this.activeItem){
34233             if(this.activeItem){
34234                 this.activeItem.deactivate();
34235             }
34236             this.activeItem = item;
34237             item.activate(autoExpand);
34238         }else if(autoExpand){
34239             item.expandMenu();
34240         }
34241     },
34242
34243     // private
34244     tryActivate : function(start, step){
34245         var items = this.items;
34246         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34247             var item = items.get(i);
34248             if(!item.disabled && item.canActivate){
34249                 this.setActiveItem(item, false);
34250                 return item;
34251             }
34252         }
34253         return false;
34254     },
34255
34256     // private
34257     onMouseOver : function(e){
34258         var t;
34259         if(t = this.findTargetItem(e)){
34260             if(t.canActivate && !t.disabled){
34261                 this.setActiveItem(t, true);
34262             }
34263         }
34264         this.fireEvent("mouseover", this, e, t);
34265     },
34266
34267     // private
34268     onMouseOut : function(e){
34269         var t;
34270         if(t = this.findTargetItem(e)){
34271             if(t == this.activeItem && t.shouldDeactivate(e)){
34272                 this.activeItem.deactivate();
34273                 delete this.activeItem;
34274             }
34275         }
34276         this.fireEvent("mouseout", this, e, t);
34277     },
34278
34279     /**
34280      * Read-only.  Returns true if the menu is currently displayed, else false.
34281      * @type Boolean
34282      */
34283     isVisible : function(){
34284         return this.el && !this.hidden;
34285     },
34286
34287     /**
34288      * Displays this menu relative to another element
34289      * @param {String/HTMLElement/Roo.Element} element The element to align to
34290      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34291      * the element (defaults to this.defaultAlign)
34292      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34293      */
34294     show : function(el, pos, parentMenu){
34295         this.parentMenu = parentMenu;
34296         if(!this.el){
34297             this.render();
34298         }
34299         this.fireEvent("beforeshow", this);
34300         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34301     },
34302
34303     /**
34304      * Displays this menu at a specific xy position
34305      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34306      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34307      */
34308     showAt : function(xy, parentMenu, /* private: */_e){
34309         this.parentMenu = parentMenu;
34310         if(!this.el){
34311             this.render();
34312         }
34313         if(_e !== false){
34314             this.fireEvent("beforeshow", this);
34315             xy = this.el.adjustForConstraints(xy);
34316         }
34317         this.el.setXY(xy);
34318         this.el.show();
34319         this.hidden = false;
34320         this.focus();
34321         this.fireEvent("show", this);
34322     },
34323
34324     focus : function(){
34325         if(!this.hidden){
34326             this.doFocus.defer(50, this);
34327         }
34328     },
34329
34330     doFocus : function(){
34331         if(!this.hidden){
34332             this.focusEl.focus();
34333         }
34334     },
34335
34336     /**
34337      * Hides this menu and optionally all parent menus
34338      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34339      */
34340     hide : function(deep){
34341         if(this.el && this.isVisible()){
34342             this.fireEvent("beforehide", this);
34343             if(this.activeItem){
34344                 this.activeItem.deactivate();
34345                 this.activeItem = null;
34346             }
34347             this.el.hide();
34348             this.hidden = true;
34349             this.fireEvent("hide", this);
34350         }
34351         if(deep === true && this.parentMenu){
34352             this.parentMenu.hide(true);
34353         }
34354     },
34355
34356     /**
34357      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34358      * Any of the following are valid:
34359      * <ul>
34360      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34361      * <li>An HTMLElement object which will be converted to a menu item</li>
34362      * <li>A menu item config object that will be created as a new menu item</li>
34363      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34364      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34365      * </ul>
34366      * Usage:
34367      * <pre><code>
34368 // Create the menu
34369 var menu = new Roo.menu.Menu();
34370
34371 // Create a menu item to add by reference
34372 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34373
34374 // Add a bunch of items at once using different methods.
34375 // Only the last item added will be returned.
34376 var item = menu.add(
34377     menuItem,                // add existing item by ref
34378     'Dynamic Item',          // new TextItem
34379     '-',                     // new separator
34380     { text: 'Config Item' }  // new item by config
34381 );
34382 </code></pre>
34383      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34384      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34385      */
34386     add : function(){
34387         var a = arguments, l = a.length, item;
34388         for(var i = 0; i < l; i++){
34389             var el = a[i];
34390             if ((typeof(el) == "object") && el.xtype && el.xns) {
34391                 el = Roo.factory(el, Roo.menu);
34392             }
34393             
34394             if(el.render){ // some kind of Item
34395                 item = this.addItem(el);
34396             }else if(typeof el == "string"){ // string
34397                 if(el == "separator" || el == "-"){
34398                     item = this.addSeparator();
34399                 }else{
34400                     item = this.addText(el);
34401                 }
34402             }else if(el.tagName || el.el){ // element
34403                 item = this.addElement(el);
34404             }else if(typeof el == "object"){ // must be menu item config?
34405                 item = this.addMenuItem(el);
34406             }
34407         }
34408         return item;
34409     },
34410
34411     /**
34412      * Returns this menu's underlying {@link Roo.Element} object
34413      * @return {Roo.Element} The element
34414      */
34415     getEl : function(){
34416         if(!this.el){
34417             this.render();
34418         }
34419         return this.el;
34420     },
34421
34422     /**
34423      * Adds a separator bar to the menu
34424      * @return {Roo.menu.Item} The menu item that was added
34425      */
34426     addSeparator : function(){
34427         return this.addItem(new Roo.menu.Separator());
34428     },
34429
34430     /**
34431      * Adds an {@link Roo.Element} object to the menu
34432      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34433      * @return {Roo.menu.Item} The menu item that was added
34434      */
34435     addElement : function(el){
34436         return this.addItem(new Roo.menu.BaseItem(el));
34437     },
34438
34439     /**
34440      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34441      * @param {Roo.menu.Item} item The menu item to add
34442      * @return {Roo.menu.Item} The menu item that was added
34443      */
34444     addItem : function(item){
34445         this.items.add(item);
34446         if(this.ul){
34447             var li = document.createElement("li");
34448             li.className = "x-menu-list-item";
34449             this.ul.dom.appendChild(li);
34450             item.render(li, this);
34451             this.delayAutoWidth();
34452         }
34453         return item;
34454     },
34455
34456     /**
34457      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34458      * @param {Object} config A MenuItem config object
34459      * @return {Roo.menu.Item} The menu item that was added
34460      */
34461     addMenuItem : function(config){
34462         if(!(config instanceof Roo.menu.Item)){
34463             if(typeof config.checked == "boolean"){ // must be check menu item config?
34464                 config = new Roo.menu.CheckItem(config);
34465             }else{
34466                 config = new Roo.menu.Item(config);
34467             }
34468         }
34469         return this.addItem(config);
34470     },
34471
34472     /**
34473      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34474      * @param {String} text The text to display in the menu item
34475      * @return {Roo.menu.Item} The menu item that was added
34476      */
34477     addText : function(text){
34478         return this.addItem(new Roo.menu.TextItem({ text : text }));
34479     },
34480
34481     /**
34482      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34483      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34484      * @param {Roo.menu.Item} item The menu item to add
34485      * @return {Roo.menu.Item} The menu item that was added
34486      */
34487     insert : function(index, item){
34488         this.items.insert(index, item);
34489         if(this.ul){
34490             var li = document.createElement("li");
34491             li.className = "x-menu-list-item";
34492             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34493             item.render(li, this);
34494             this.delayAutoWidth();
34495         }
34496         return item;
34497     },
34498
34499     /**
34500      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34501      * @param {Roo.menu.Item} item The menu item to remove
34502      */
34503     remove : function(item){
34504         this.items.removeKey(item.id);
34505         item.destroy();
34506     },
34507
34508     /**
34509      * Removes and destroys all items in the menu
34510      */
34511     removeAll : function(){
34512         var f;
34513         while(f = this.items.first()){
34514             this.remove(f);
34515         }
34516     }
34517 });
34518
34519 // MenuNav is a private utility class used internally by the Menu
34520 Roo.menu.MenuNav = function(menu){
34521     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34522     this.scope = this.menu = menu;
34523 };
34524
34525 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34526     doRelay : function(e, h){
34527         var k = e.getKey();
34528         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34529             this.menu.tryActivate(0, 1);
34530             return false;
34531         }
34532         return h.call(this.scope || this, e, this.menu);
34533     },
34534
34535     up : function(e, m){
34536         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34537             m.tryActivate(m.items.length-1, -1);
34538         }
34539     },
34540
34541     down : function(e, m){
34542         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34543             m.tryActivate(0, 1);
34544         }
34545     },
34546
34547     right : function(e, m){
34548         if(m.activeItem){
34549             m.activeItem.expandMenu(true);
34550         }
34551     },
34552
34553     left : function(e, m){
34554         m.hide();
34555         if(m.parentMenu && m.parentMenu.activeItem){
34556             m.parentMenu.activeItem.activate();
34557         }
34558     },
34559
34560     enter : function(e, m){
34561         if(m.activeItem){
34562             e.stopPropagation();
34563             m.activeItem.onClick(e);
34564             m.fireEvent("click", this, m.activeItem);
34565             return true;
34566         }
34567     }
34568 });/*
34569  * Based on:
34570  * Ext JS Library 1.1.1
34571  * Copyright(c) 2006-2007, Ext JS, LLC.
34572  *
34573  * Originally Released Under LGPL - original licence link has changed is not relivant.
34574  *
34575  * Fork - LGPL
34576  * <script type="text/javascript">
34577  */
34578  
34579 /**
34580  * @class Roo.menu.MenuMgr
34581  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34582  * @singleton
34583  */
34584 Roo.menu.MenuMgr = function(){
34585    var menus, active, groups = {}, attached = false, lastShow = new Date();
34586
34587    // private - called when first menu is created
34588    function init(){
34589        menus = {};
34590        active = new Roo.util.MixedCollection();
34591        Roo.get(document).addKeyListener(27, function(){
34592            if(active.length > 0){
34593                hideAll();
34594            }
34595        });
34596    }
34597
34598    // private
34599    function hideAll(){
34600        if(active && active.length > 0){
34601            var c = active.clone();
34602            c.each(function(m){
34603                m.hide();
34604            });
34605        }
34606    }
34607
34608    // private
34609    function onHide(m){
34610        active.remove(m);
34611        if(active.length < 1){
34612            Roo.get(document).un("mousedown", onMouseDown);
34613            attached = false;
34614        }
34615    }
34616
34617    // private
34618    function onShow(m){
34619        var last = active.last();
34620        lastShow = new Date();
34621        active.add(m);
34622        if(!attached){
34623            Roo.get(document).on("mousedown", onMouseDown);
34624            attached = true;
34625        }
34626        if(m.parentMenu){
34627           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34628           m.parentMenu.activeChild = m;
34629        }else if(last && last.isVisible()){
34630           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34631        }
34632    }
34633
34634    // private
34635    function onBeforeHide(m){
34636        if(m.activeChild){
34637            m.activeChild.hide();
34638        }
34639        if(m.autoHideTimer){
34640            clearTimeout(m.autoHideTimer);
34641            delete m.autoHideTimer;
34642        }
34643    }
34644
34645    // private
34646    function onBeforeShow(m){
34647        var pm = m.parentMenu;
34648        if(!pm && !m.allowOtherMenus){
34649            hideAll();
34650        }else if(pm && pm.activeChild && active != m){
34651            pm.activeChild.hide();
34652        }
34653    }
34654
34655    // private
34656    function onMouseDown(e){
34657        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34658            hideAll();
34659        }
34660    }
34661
34662    // private
34663    function onBeforeCheck(mi, state){
34664        if(state){
34665            var g = groups[mi.group];
34666            for(var i = 0, l = g.length; i < l; i++){
34667                if(g[i] != mi){
34668                    g[i].setChecked(false);
34669                }
34670            }
34671        }
34672    }
34673
34674    return {
34675
34676        /**
34677         * Hides all menus that are currently visible
34678         */
34679        hideAll : function(){
34680             hideAll();  
34681        },
34682
34683        // private
34684        register : function(menu){
34685            if(!menus){
34686                init();
34687            }
34688            menus[menu.id] = menu;
34689            menu.on("beforehide", onBeforeHide);
34690            menu.on("hide", onHide);
34691            menu.on("beforeshow", onBeforeShow);
34692            menu.on("show", onShow);
34693            var g = menu.group;
34694            if(g && menu.events["checkchange"]){
34695                if(!groups[g]){
34696                    groups[g] = [];
34697                }
34698                groups[g].push(menu);
34699                menu.on("checkchange", onCheck);
34700            }
34701        },
34702
34703         /**
34704          * Returns a {@link Roo.menu.Menu} object
34705          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34706          * be used to generate and return a new Menu instance.
34707          */
34708        get : function(menu){
34709            if(typeof menu == "string"){ // menu id
34710                return menus[menu];
34711            }else if(menu.events){  // menu instance
34712                return menu;
34713            }else if(typeof menu.length == 'number'){ // array of menu items?
34714                return new Roo.menu.Menu({items:menu});
34715            }else{ // otherwise, must be a config
34716                return new Roo.menu.Menu(menu);
34717            }
34718        },
34719
34720        // private
34721        unregister : function(menu){
34722            delete menus[menu.id];
34723            menu.un("beforehide", onBeforeHide);
34724            menu.un("hide", onHide);
34725            menu.un("beforeshow", onBeforeShow);
34726            menu.un("show", onShow);
34727            var g = menu.group;
34728            if(g && menu.events["checkchange"]){
34729                groups[g].remove(menu);
34730                menu.un("checkchange", onCheck);
34731            }
34732        },
34733
34734        // private
34735        registerCheckable : function(menuItem){
34736            var g = menuItem.group;
34737            if(g){
34738                if(!groups[g]){
34739                    groups[g] = [];
34740                }
34741                groups[g].push(menuItem);
34742                menuItem.on("beforecheckchange", onBeforeCheck);
34743            }
34744        },
34745
34746        // private
34747        unregisterCheckable : function(menuItem){
34748            var g = menuItem.group;
34749            if(g){
34750                groups[g].remove(menuItem);
34751                menuItem.un("beforecheckchange", onBeforeCheck);
34752            }
34753        }
34754    };
34755 }();/*
34756  * Based on:
34757  * Ext JS Library 1.1.1
34758  * Copyright(c) 2006-2007, Ext JS, LLC.
34759  *
34760  * Originally Released Under LGPL - original licence link has changed is not relivant.
34761  *
34762  * Fork - LGPL
34763  * <script type="text/javascript">
34764  */
34765  
34766
34767 /**
34768  * @class Roo.menu.BaseItem
34769  * @extends Roo.Component
34770  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34771  * management and base configuration options shared by all menu components.
34772  * @constructor
34773  * Creates a new BaseItem
34774  * @param {Object} config Configuration options
34775  */
34776 Roo.menu.BaseItem = function(config){
34777     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34778
34779     this.addEvents({
34780         /**
34781          * @event click
34782          * Fires when this item is clicked
34783          * @param {Roo.menu.BaseItem} this
34784          * @param {Roo.EventObject} e
34785          */
34786         click: true,
34787         /**
34788          * @event activate
34789          * Fires when this item is activated
34790          * @param {Roo.menu.BaseItem} this
34791          */
34792         activate : true,
34793         /**
34794          * @event deactivate
34795          * Fires when this item is deactivated
34796          * @param {Roo.menu.BaseItem} this
34797          */
34798         deactivate : true
34799     });
34800
34801     if(this.handler){
34802         this.on("click", this.handler, this.scope, true);
34803     }
34804 };
34805
34806 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34807     /**
34808      * @cfg {Function} handler
34809      * A function that will handle the click event of this menu item (defaults to undefined)
34810      */
34811     /**
34812      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34813      */
34814     canActivate : false,
34815     /**
34816      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34817      */
34818     activeClass : "x-menu-item-active",
34819     /**
34820      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34821      */
34822     hideOnClick : true,
34823     /**
34824      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34825      */
34826     hideDelay : 100,
34827
34828     // private
34829     ctype: "Roo.menu.BaseItem",
34830
34831     // private
34832     actionMode : "container",
34833
34834     // private
34835     render : function(container, parentMenu){
34836         this.parentMenu = parentMenu;
34837         Roo.menu.BaseItem.superclass.render.call(this, container);
34838         this.container.menuItemId = this.id;
34839     },
34840
34841     // private
34842     onRender : function(container, position){
34843         this.el = Roo.get(this.el);
34844         container.dom.appendChild(this.el.dom);
34845     },
34846
34847     // private
34848     onClick : function(e){
34849         if(!this.disabled && this.fireEvent("click", this, e) !== false
34850                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34851             this.handleClick(e);
34852         }else{
34853             e.stopEvent();
34854         }
34855     },
34856
34857     // private
34858     activate : function(){
34859         if(this.disabled){
34860             return false;
34861         }
34862         var li = this.container;
34863         li.addClass(this.activeClass);
34864         this.region = li.getRegion().adjust(2, 2, -2, -2);
34865         this.fireEvent("activate", this);
34866         return true;
34867     },
34868
34869     // private
34870     deactivate : function(){
34871         this.container.removeClass(this.activeClass);
34872         this.fireEvent("deactivate", this);
34873     },
34874
34875     // private
34876     shouldDeactivate : function(e){
34877         return !this.region || !this.region.contains(e.getPoint());
34878     },
34879
34880     // private
34881     handleClick : function(e){
34882         if(this.hideOnClick){
34883             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34884         }
34885     },
34886
34887     // private
34888     expandMenu : function(autoActivate){
34889         // do nothing
34890     },
34891
34892     // private
34893     hideMenu : function(){
34894         // do nothing
34895     }
34896 });/*
34897  * Based on:
34898  * Ext JS Library 1.1.1
34899  * Copyright(c) 2006-2007, Ext JS, LLC.
34900  *
34901  * Originally Released Under LGPL - original licence link has changed is not relivant.
34902  *
34903  * Fork - LGPL
34904  * <script type="text/javascript">
34905  */
34906  
34907 /**
34908  * @class Roo.menu.Adapter
34909  * @extends Roo.menu.BaseItem
34910  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34911  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34912  * @constructor
34913  * Creates a new Adapter
34914  * @param {Object} config Configuration options
34915  */
34916 Roo.menu.Adapter = function(component, config){
34917     Roo.menu.Adapter.superclass.constructor.call(this, config);
34918     this.component = component;
34919 };
34920 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34921     // private
34922     canActivate : true,
34923
34924     // private
34925     onRender : function(container, position){
34926         this.component.render(container);
34927         this.el = this.component.getEl();
34928     },
34929
34930     // private
34931     activate : function(){
34932         if(this.disabled){
34933             return false;
34934         }
34935         this.component.focus();
34936         this.fireEvent("activate", this);
34937         return true;
34938     },
34939
34940     // private
34941     deactivate : function(){
34942         this.fireEvent("deactivate", this);
34943     },
34944
34945     // private
34946     disable : function(){
34947         this.component.disable();
34948         Roo.menu.Adapter.superclass.disable.call(this);
34949     },
34950
34951     // private
34952     enable : function(){
34953         this.component.enable();
34954         Roo.menu.Adapter.superclass.enable.call(this);
34955     }
34956 });/*
34957  * Based on:
34958  * Ext JS Library 1.1.1
34959  * Copyright(c) 2006-2007, Ext JS, LLC.
34960  *
34961  * Originally Released Under LGPL - original licence link has changed is not relivant.
34962  *
34963  * Fork - LGPL
34964  * <script type="text/javascript">
34965  */
34966
34967 /**
34968  * @class Roo.menu.TextItem
34969  * @extends Roo.menu.BaseItem
34970  * Adds a static text string to a menu, usually used as either a heading or group separator.
34971  * Note: old style constructor with text is still supported.
34972  * 
34973  * @constructor
34974  * Creates a new TextItem
34975  * @param {Object} cfg Configuration
34976  */
34977 Roo.menu.TextItem = function(cfg){
34978     if (typeof(cfg) == 'string') {
34979         this.text = cfg;
34980     } else {
34981         Roo.apply(this,cfg);
34982     }
34983     
34984     Roo.menu.TextItem.superclass.constructor.call(this);
34985 };
34986
34987 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34988     /**
34989      * @cfg {Boolean} text Text to show on item.
34990      */
34991     text : '',
34992     
34993     /**
34994      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34995      */
34996     hideOnClick : false,
34997     /**
34998      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34999      */
35000     itemCls : "x-menu-text",
35001
35002     // private
35003     onRender : function(){
35004         var s = document.createElement("span");
35005         s.className = this.itemCls;
35006         s.innerHTML = this.text;
35007         this.el = s;
35008         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35009     }
35010 });/*
35011  * Based on:
35012  * Ext JS Library 1.1.1
35013  * Copyright(c) 2006-2007, Ext JS, LLC.
35014  *
35015  * Originally Released Under LGPL - original licence link has changed is not relivant.
35016  *
35017  * Fork - LGPL
35018  * <script type="text/javascript">
35019  */
35020
35021 /**
35022  * @class Roo.menu.Separator
35023  * @extends Roo.menu.BaseItem
35024  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35025  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35026  * @constructor
35027  * @param {Object} config Configuration options
35028  */
35029 Roo.menu.Separator = function(config){
35030     Roo.menu.Separator.superclass.constructor.call(this, config);
35031 };
35032
35033 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35034     /**
35035      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35036      */
35037     itemCls : "x-menu-sep",
35038     /**
35039      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35040      */
35041     hideOnClick : false,
35042
35043     // private
35044     onRender : function(li){
35045         var s = document.createElement("span");
35046         s.className = this.itemCls;
35047         s.innerHTML = "&#160;";
35048         this.el = s;
35049         li.addClass("x-menu-sep-li");
35050         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35051     }
35052 });/*
35053  * Based on:
35054  * Ext JS Library 1.1.1
35055  * Copyright(c) 2006-2007, Ext JS, LLC.
35056  *
35057  * Originally Released Under LGPL - original licence link has changed is not relivant.
35058  *
35059  * Fork - LGPL
35060  * <script type="text/javascript">
35061  */
35062 /**
35063  * @class Roo.menu.Item
35064  * @extends Roo.menu.BaseItem
35065  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35066  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35067  * activation and click handling.
35068  * @constructor
35069  * Creates a new Item
35070  * @param {Object} config Configuration options
35071  */
35072 Roo.menu.Item = function(config){
35073     Roo.menu.Item.superclass.constructor.call(this, config);
35074     if(this.menu){
35075         this.menu = Roo.menu.MenuMgr.get(this.menu);
35076     }
35077 };
35078 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35079     
35080     /**
35081      * @cfg {String} text
35082      * The text to show on the menu item.
35083      */
35084     text: '',
35085      /**
35086      * @cfg {String} HTML to render in menu
35087      * The text to show on the menu item (HTML version).
35088      */
35089     html: '',
35090     /**
35091      * @cfg {String} icon
35092      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35093      */
35094     icon: undefined,
35095     /**
35096      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35097      */
35098     itemCls : "x-menu-item",
35099     /**
35100      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35101      */
35102     canActivate : true,
35103     /**
35104      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35105      */
35106     showDelay: 200,
35107     // doc'd in BaseItem
35108     hideDelay: 200,
35109
35110     // private
35111     ctype: "Roo.menu.Item",
35112     
35113     // private
35114     onRender : function(container, position){
35115         var el = document.createElement("a");
35116         el.hideFocus = true;
35117         el.unselectable = "on";
35118         el.href = this.href || "#";
35119         if(this.hrefTarget){
35120             el.target = this.hrefTarget;
35121         }
35122         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35123         
35124         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35125         
35126         el.innerHTML = String.format(
35127                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35128                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35129         this.el = el;
35130         Roo.menu.Item.superclass.onRender.call(this, container, position);
35131     },
35132
35133     /**
35134      * Sets the text to display in this menu item
35135      * @param {String} text The text to display
35136      * @param {Boolean} isHTML true to indicate text is pure html.
35137      */
35138     setText : function(text, isHTML){
35139         if (isHTML) {
35140             this.html = text;
35141         } else {
35142             this.text = text;
35143             this.html = '';
35144         }
35145         if(this.rendered){
35146             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35147      
35148             this.el.update(String.format(
35149                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35150                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35151             this.parentMenu.autoWidth();
35152         }
35153     },
35154
35155     // private
35156     handleClick : function(e){
35157         if(!this.href){ // if no link defined, stop the event automatically
35158             e.stopEvent();
35159         }
35160         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35161     },
35162
35163     // private
35164     activate : function(autoExpand){
35165         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35166             this.focus();
35167             if(autoExpand){
35168                 this.expandMenu();
35169             }
35170         }
35171         return true;
35172     },
35173
35174     // private
35175     shouldDeactivate : function(e){
35176         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35177             if(this.menu && this.menu.isVisible()){
35178                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35179             }
35180             return true;
35181         }
35182         return false;
35183     },
35184
35185     // private
35186     deactivate : function(){
35187         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35188         this.hideMenu();
35189     },
35190
35191     // private
35192     expandMenu : function(autoActivate){
35193         if(!this.disabled && this.menu){
35194             clearTimeout(this.hideTimer);
35195             delete this.hideTimer;
35196             if(!this.menu.isVisible() && !this.showTimer){
35197                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35198             }else if (this.menu.isVisible() && autoActivate){
35199                 this.menu.tryActivate(0, 1);
35200             }
35201         }
35202     },
35203
35204     // private
35205     deferExpand : function(autoActivate){
35206         delete this.showTimer;
35207         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35208         if(autoActivate){
35209             this.menu.tryActivate(0, 1);
35210         }
35211     },
35212
35213     // private
35214     hideMenu : function(){
35215         clearTimeout(this.showTimer);
35216         delete this.showTimer;
35217         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35218             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35219         }
35220     },
35221
35222     // private
35223     deferHide : function(){
35224         delete this.hideTimer;
35225         this.menu.hide();
35226     }
35227 });/*
35228  * Based on:
35229  * Ext JS Library 1.1.1
35230  * Copyright(c) 2006-2007, Ext JS, LLC.
35231  *
35232  * Originally Released Under LGPL - original licence link has changed is not relivant.
35233  *
35234  * Fork - LGPL
35235  * <script type="text/javascript">
35236  */
35237  
35238 /**
35239  * @class Roo.menu.CheckItem
35240  * @extends Roo.menu.Item
35241  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35242  * @constructor
35243  * Creates a new CheckItem
35244  * @param {Object} config Configuration options
35245  */
35246 Roo.menu.CheckItem = function(config){
35247     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35248     this.addEvents({
35249         /**
35250          * @event beforecheckchange
35251          * Fires before the checked value is set, providing an opportunity to cancel if needed
35252          * @param {Roo.menu.CheckItem} this
35253          * @param {Boolean} checked The new checked value that will be set
35254          */
35255         "beforecheckchange" : true,
35256         /**
35257          * @event checkchange
35258          * Fires after the checked value has been set
35259          * @param {Roo.menu.CheckItem} this
35260          * @param {Boolean} checked The checked value that was set
35261          */
35262         "checkchange" : true
35263     });
35264     if(this.checkHandler){
35265         this.on('checkchange', this.checkHandler, this.scope);
35266     }
35267 };
35268 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35269     /**
35270      * @cfg {String} group
35271      * All check items with the same group name will automatically be grouped into a single-select
35272      * radio button group (defaults to '')
35273      */
35274     /**
35275      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35276      */
35277     itemCls : "x-menu-item x-menu-check-item",
35278     /**
35279      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35280      */
35281     groupClass : "x-menu-group-item",
35282
35283     /**
35284      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35285      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35286      * initialized with checked = true will be rendered as checked.
35287      */
35288     checked: false,
35289
35290     // private
35291     ctype: "Roo.menu.CheckItem",
35292
35293     // private
35294     onRender : function(c){
35295         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35296         if(this.group){
35297             this.el.addClass(this.groupClass);
35298         }
35299         Roo.menu.MenuMgr.registerCheckable(this);
35300         if(this.checked){
35301             this.checked = false;
35302             this.setChecked(true, true);
35303         }
35304     },
35305
35306     // private
35307     destroy : function(){
35308         if(this.rendered){
35309             Roo.menu.MenuMgr.unregisterCheckable(this);
35310         }
35311         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35312     },
35313
35314     /**
35315      * Set the checked state of this item
35316      * @param {Boolean} checked The new checked value
35317      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35318      */
35319     setChecked : function(state, suppressEvent){
35320         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35321             if(this.container){
35322                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35323             }
35324             this.checked = state;
35325             if(suppressEvent !== true){
35326                 this.fireEvent("checkchange", this, state);
35327             }
35328         }
35329     },
35330
35331     // private
35332     handleClick : function(e){
35333        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35334            this.setChecked(!this.checked);
35335        }
35336        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35337     }
35338 });/*
35339  * Based on:
35340  * Ext JS Library 1.1.1
35341  * Copyright(c) 2006-2007, Ext JS, LLC.
35342  *
35343  * Originally Released Under LGPL - original licence link has changed is not relivant.
35344  *
35345  * Fork - LGPL
35346  * <script type="text/javascript">
35347  */
35348  
35349 /**
35350  * @class Roo.menu.DateItem
35351  * @extends Roo.menu.Adapter
35352  * A menu item that wraps the {@link Roo.DatPicker} component.
35353  * @constructor
35354  * Creates a new DateItem
35355  * @param {Object} config Configuration options
35356  */
35357 Roo.menu.DateItem = function(config){
35358     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35359     /** The Roo.DatePicker object @type Roo.DatePicker */
35360     this.picker = this.component;
35361     this.addEvents({select: true});
35362     
35363     this.picker.on("render", function(picker){
35364         picker.getEl().swallowEvent("click");
35365         picker.container.addClass("x-menu-date-item");
35366     });
35367
35368     this.picker.on("select", this.onSelect, this);
35369 };
35370
35371 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35372     // private
35373     onSelect : function(picker, date){
35374         this.fireEvent("select", this, date, picker);
35375         Roo.menu.DateItem.superclass.handleClick.call(this);
35376     }
35377 });/*
35378  * Based on:
35379  * Ext JS Library 1.1.1
35380  * Copyright(c) 2006-2007, Ext JS, LLC.
35381  *
35382  * Originally Released Under LGPL - original licence link has changed is not relivant.
35383  *
35384  * Fork - LGPL
35385  * <script type="text/javascript">
35386  */
35387  
35388 /**
35389  * @class Roo.menu.ColorItem
35390  * @extends Roo.menu.Adapter
35391  * A menu item that wraps the {@link Roo.ColorPalette} component.
35392  * @constructor
35393  * Creates a new ColorItem
35394  * @param {Object} config Configuration options
35395  */
35396 Roo.menu.ColorItem = function(config){
35397     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35398     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35399     this.palette = this.component;
35400     this.relayEvents(this.palette, ["select"]);
35401     if(this.selectHandler){
35402         this.on('select', this.selectHandler, this.scope);
35403     }
35404 };
35405 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35406  * Based on:
35407  * Ext JS Library 1.1.1
35408  * Copyright(c) 2006-2007, Ext JS, LLC.
35409  *
35410  * Originally Released Under LGPL - original licence link has changed is not relivant.
35411  *
35412  * Fork - LGPL
35413  * <script type="text/javascript">
35414  */
35415  
35416
35417 /**
35418  * @class Roo.menu.DateMenu
35419  * @extends Roo.menu.Menu
35420  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35421  * @constructor
35422  * Creates a new DateMenu
35423  * @param {Object} config Configuration options
35424  */
35425 Roo.menu.DateMenu = function(config){
35426     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35427     this.plain = true;
35428     var di = new Roo.menu.DateItem(config);
35429     this.add(di);
35430     /**
35431      * The {@link Roo.DatePicker} instance for this DateMenu
35432      * @type DatePicker
35433      */
35434     this.picker = di.picker;
35435     /**
35436      * @event select
35437      * @param {DatePicker} picker
35438      * @param {Date} date
35439      */
35440     this.relayEvents(di, ["select"]);
35441
35442     this.on('beforeshow', function(){
35443         if(this.picker){
35444             this.picker.hideMonthPicker(true);
35445         }
35446     }, this);
35447 };
35448 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35449     cls:'x-date-menu'
35450 });/*
35451  * Based on:
35452  * Ext JS Library 1.1.1
35453  * Copyright(c) 2006-2007, Ext JS, LLC.
35454  *
35455  * Originally Released Under LGPL - original licence link has changed is not relivant.
35456  *
35457  * Fork - LGPL
35458  * <script type="text/javascript">
35459  */
35460  
35461
35462 /**
35463  * @class Roo.menu.ColorMenu
35464  * @extends Roo.menu.Menu
35465  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35466  * @constructor
35467  * Creates a new ColorMenu
35468  * @param {Object} config Configuration options
35469  */
35470 Roo.menu.ColorMenu = function(config){
35471     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35472     this.plain = true;
35473     var ci = new Roo.menu.ColorItem(config);
35474     this.add(ci);
35475     /**
35476      * The {@link Roo.ColorPalette} instance for this ColorMenu
35477      * @type ColorPalette
35478      */
35479     this.palette = ci.palette;
35480     /**
35481      * @event select
35482      * @param {ColorPalette} palette
35483      * @param {String} color
35484      */
35485     this.relayEvents(ci, ["select"]);
35486 };
35487 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35488  * Based on:
35489  * Ext JS Library 1.1.1
35490  * Copyright(c) 2006-2007, Ext JS, LLC.
35491  *
35492  * Originally Released Under LGPL - original licence link has changed is not relivant.
35493  *
35494  * Fork - LGPL
35495  * <script type="text/javascript">
35496  */
35497  
35498 /**
35499  * @class Roo.form.Field
35500  * @extends Roo.BoxComponent
35501  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35502  * @constructor
35503  * Creates a new Field
35504  * @param {Object} config Configuration options
35505  */
35506 Roo.form.Field = function(config){
35507     Roo.form.Field.superclass.constructor.call(this, config);
35508 };
35509
35510 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35511     /**
35512      * @cfg {String} fieldLabel Label to use when rendering a form.
35513      */
35514        /**
35515      * @cfg {String} qtip Mouse over tip
35516      */
35517      
35518     /**
35519      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35520      */
35521     invalidClass : "x-form-invalid",
35522     /**
35523      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35524      */
35525     invalidText : "The value in this field is invalid",
35526     /**
35527      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35528      */
35529     focusClass : "x-form-focus",
35530     /**
35531      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35532       automatic validation (defaults to "keyup").
35533      */
35534     validationEvent : "keyup",
35535     /**
35536      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35537      */
35538     validateOnBlur : true,
35539     /**
35540      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35541      */
35542     validationDelay : 250,
35543     /**
35544      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35545      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35546      */
35547     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35548     /**
35549      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35550      */
35551     fieldClass : "x-form-field",
35552     /**
35553      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35554      *<pre>
35555 Value         Description
35556 -----------   ----------------------------------------------------------------------
35557 qtip          Display a quick tip when the user hovers over the field
35558 title         Display a default browser title attribute popup
35559 under         Add a block div beneath the field containing the error text
35560 side          Add an error icon to the right of the field with a popup on hover
35561 [element id]  Add the error text directly to the innerHTML of the specified element
35562 </pre>
35563      */
35564     msgTarget : 'qtip',
35565     /**
35566      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35567      */
35568     msgFx : 'normal',
35569
35570     /**
35571      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35572      */
35573     readOnly : false,
35574
35575     /**
35576      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35577      */
35578     disabled : false,
35579
35580     /**
35581      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35582      */
35583     inputType : undefined,
35584     
35585     /**
35586      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35587          */
35588         tabIndex : undefined,
35589         
35590     // private
35591     isFormField : true,
35592
35593     // private
35594     hasFocus : false,
35595     /**
35596      * @property {Roo.Element} fieldEl
35597      * Element Containing the rendered Field (with label etc.)
35598      */
35599     /**
35600      * @cfg {Mixed} value A value to initialize this field with.
35601      */
35602     value : undefined,
35603
35604     /**
35605      * @cfg {String} name The field's HTML name attribute.
35606      */
35607     /**
35608      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35609      */
35610
35611         // private ??
35612         initComponent : function(){
35613         Roo.form.Field.superclass.initComponent.call(this);
35614         this.addEvents({
35615             /**
35616              * @event focus
35617              * Fires when this field receives input focus.
35618              * @param {Roo.form.Field} this
35619              */
35620             focus : true,
35621             /**
35622              * @event blur
35623              * Fires when this field loses input focus.
35624              * @param {Roo.form.Field} this
35625              */
35626             blur : true,
35627             /**
35628              * @event specialkey
35629              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35630              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35631              * @param {Roo.form.Field} this
35632              * @param {Roo.EventObject} e The event object
35633              */
35634             specialkey : true,
35635             /**
35636              * @event change
35637              * Fires just before the field blurs if the field value has changed.
35638              * @param {Roo.form.Field} this
35639              * @param {Mixed} newValue The new value
35640              * @param {Mixed} oldValue The original value
35641              */
35642             change : true,
35643             /**
35644              * @event invalid
35645              * Fires after the field has been marked as invalid.
35646              * @param {Roo.form.Field} this
35647              * @param {String} msg The validation message
35648              */
35649             invalid : true,
35650             /**
35651              * @event valid
35652              * Fires after the field has been validated with no errors.
35653              * @param {Roo.form.Field} this
35654              */
35655             valid : true,
35656              /**
35657              * @event keyup
35658              * Fires after the key up
35659              * @param {Roo.form.Field} this
35660              * @param {Roo.EventObject}  e The event Object
35661              */
35662             keyup : true
35663         });
35664     },
35665
35666     /**
35667      * Returns the name attribute of the field if available
35668      * @return {String} name The field name
35669      */
35670     getName: function(){
35671          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35672     },
35673
35674     // private
35675     onRender : function(ct, position){
35676         Roo.form.Field.superclass.onRender.call(this, ct, position);
35677         if(!this.el){
35678             var cfg = this.getAutoCreate();
35679             if(!cfg.name){
35680                 cfg.name = this.name || this.id;
35681             }
35682             if(this.inputType){
35683                 cfg.type = this.inputType;
35684             }
35685             this.el = ct.createChild(cfg, position);
35686         }
35687         var type = this.el.dom.type;
35688         if(type){
35689             if(type == 'password'){
35690                 type = 'text';
35691             }
35692             this.el.addClass('x-form-'+type);
35693         }
35694         if(this.readOnly){
35695             this.el.dom.readOnly = true;
35696         }
35697         if(this.tabIndex !== undefined){
35698             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35699         }
35700
35701         this.el.addClass([this.fieldClass, this.cls]);
35702         this.initValue();
35703     },
35704
35705     /**
35706      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35707      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35708      * @return {Roo.form.Field} this
35709      */
35710     applyTo : function(target){
35711         this.allowDomMove = false;
35712         this.el = Roo.get(target);
35713         this.render(this.el.dom.parentNode);
35714         return this;
35715     },
35716
35717     // private
35718     initValue : function(){
35719         if(this.value !== undefined){
35720             this.setValue(this.value);
35721         }else if(this.el.dom.value.length > 0){
35722             this.setValue(this.el.dom.value);
35723         }
35724     },
35725
35726     /**
35727      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35728      */
35729     isDirty : function() {
35730         if(this.disabled) {
35731             return false;
35732         }
35733         return String(this.getValue()) !== String(this.originalValue);
35734     },
35735
35736     // private
35737     afterRender : function(){
35738         Roo.form.Field.superclass.afterRender.call(this);
35739         this.initEvents();
35740     },
35741
35742     // private
35743     fireKey : function(e){
35744         //Roo.log('field ' + e.getKey());
35745         if(e.isNavKeyPress()){
35746             this.fireEvent("specialkey", this, e);
35747         }
35748     },
35749
35750     /**
35751      * Resets the current field value to the originally loaded value and clears any validation messages
35752      */
35753     reset : function(){
35754         this.setValue(this.originalValue);
35755         this.clearInvalid();
35756     },
35757
35758     // private
35759     initEvents : function(){
35760         // safari killled keypress - so keydown is now used..
35761         this.el.on("keydown" , this.fireKey,  this);
35762         this.el.on("focus", this.onFocus,  this);
35763         this.el.on("blur", this.onBlur,  this);
35764         this.el.relayEvent('keyup', this);
35765
35766         // reference to original value for reset
35767         this.originalValue = this.getValue();
35768     },
35769
35770     // private
35771     onFocus : function(){
35772         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35773             this.el.addClass(this.focusClass);
35774         }
35775         if(!this.hasFocus){
35776             this.hasFocus = true;
35777             this.startValue = this.getValue();
35778             this.fireEvent("focus", this);
35779         }
35780     },
35781
35782     beforeBlur : Roo.emptyFn,
35783
35784     // private
35785     onBlur : function(){
35786         this.beforeBlur();
35787         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35788             this.el.removeClass(this.focusClass);
35789         }
35790         this.hasFocus = false;
35791         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35792             this.validate();
35793         }
35794         var v = this.getValue();
35795         if(String(v) !== String(this.startValue)){
35796             this.fireEvent('change', this, v, this.startValue);
35797         }
35798         this.fireEvent("blur", this);
35799     },
35800
35801     /**
35802      * Returns whether or not the field value is currently valid
35803      * @param {Boolean} preventMark True to disable marking the field invalid
35804      * @return {Boolean} True if the value is valid, else false
35805      */
35806     isValid : function(preventMark){
35807         if(this.disabled){
35808             return true;
35809         }
35810         var restore = this.preventMark;
35811         this.preventMark = preventMark === true;
35812         var v = this.validateValue(this.processValue(this.getRawValue()));
35813         this.preventMark = restore;
35814         return v;
35815     },
35816
35817     /**
35818      * Validates the field value
35819      * @return {Boolean} True if the value is valid, else false
35820      */
35821     validate : function(){
35822         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35823             this.clearInvalid();
35824             return true;
35825         }
35826         return false;
35827     },
35828
35829     processValue : function(value){
35830         return value;
35831     },
35832
35833     // private
35834     // Subclasses should provide the validation implementation by overriding this
35835     validateValue : function(value){
35836         return true;
35837     },
35838
35839     /**
35840      * Mark this field as invalid
35841      * @param {String} msg The validation message
35842      */
35843     markInvalid : function(msg){
35844         if(!this.rendered || this.preventMark){ // not rendered
35845             return;
35846         }
35847         this.el.addClass(this.invalidClass);
35848         msg = msg || this.invalidText;
35849         switch(this.msgTarget){
35850             case 'qtip':
35851                 this.el.dom.qtip = msg;
35852                 this.el.dom.qclass = 'x-form-invalid-tip';
35853                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35854                     Roo.QuickTips.enable();
35855                 }
35856                 break;
35857             case 'title':
35858                 this.el.dom.title = msg;
35859                 break;
35860             case 'under':
35861                 if(!this.errorEl){
35862                     var elp = this.el.findParent('.x-form-element', 5, true);
35863                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35864                     this.errorEl.setWidth(elp.getWidth(true)-20);
35865                 }
35866                 this.errorEl.update(msg);
35867                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35868                 break;
35869             case 'side':
35870                 if(!this.errorIcon){
35871                     var elp = this.el.findParent('.x-form-element', 5, true);
35872                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35873                 }
35874                 this.alignErrorIcon();
35875                 this.errorIcon.dom.qtip = msg;
35876                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35877                 this.errorIcon.show();
35878                 this.on('resize', this.alignErrorIcon, this);
35879                 break;
35880             default:
35881                 var t = Roo.getDom(this.msgTarget);
35882                 t.innerHTML = msg;
35883                 t.style.display = this.msgDisplay;
35884                 break;
35885         }
35886         this.fireEvent('invalid', this, msg);
35887     },
35888
35889     // private
35890     alignErrorIcon : function(){
35891         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35892     },
35893
35894     /**
35895      * Clear any invalid styles/messages for this field
35896      */
35897     clearInvalid : function(){
35898         if(!this.rendered || this.preventMark){ // not rendered
35899             return;
35900         }
35901         this.el.removeClass(this.invalidClass);
35902         switch(this.msgTarget){
35903             case 'qtip':
35904                 this.el.dom.qtip = '';
35905                 break;
35906             case 'title':
35907                 this.el.dom.title = '';
35908                 break;
35909             case 'under':
35910                 if(this.errorEl){
35911                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35912                 }
35913                 break;
35914             case 'side':
35915                 if(this.errorIcon){
35916                     this.errorIcon.dom.qtip = '';
35917                     this.errorIcon.hide();
35918                     this.un('resize', this.alignErrorIcon, this);
35919                 }
35920                 break;
35921             default:
35922                 var t = Roo.getDom(this.msgTarget);
35923                 t.innerHTML = '';
35924                 t.style.display = 'none';
35925                 break;
35926         }
35927         this.fireEvent('valid', this);
35928     },
35929
35930     /**
35931      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35932      * @return {Mixed} value The field value
35933      */
35934     getRawValue : function(){
35935         var v = this.el.getValue();
35936         if(v === this.emptyText){
35937             v = '';
35938         }
35939         return v;
35940     },
35941
35942     /**
35943      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35944      * @return {Mixed} value The field value
35945      */
35946     getValue : function(){
35947         var v = this.el.getValue();
35948         if(v === this.emptyText || v === undefined){
35949             v = '';
35950         }
35951         return v;
35952     },
35953
35954     /**
35955      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35956      * @param {Mixed} value The value to set
35957      */
35958     setRawValue : function(v){
35959         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35960     },
35961
35962     /**
35963      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35964      * @param {Mixed} value The value to set
35965      */
35966     setValue : function(v){
35967         this.value = v;
35968         if(this.rendered){
35969             this.el.dom.value = (v === null || v === undefined ? '' : v);
35970              this.validate();
35971         }
35972     },
35973
35974     adjustSize : function(w, h){
35975         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35976         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35977         return s;
35978     },
35979
35980     adjustWidth : function(tag, w){
35981         tag = tag.toLowerCase();
35982         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35983             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35984                 if(tag == 'input'){
35985                     return w + 2;
35986                 }
35987                 if(tag = 'textarea'){
35988                     return w-2;
35989                 }
35990             }else if(Roo.isOpera){
35991                 if(tag == 'input'){
35992                     return w + 2;
35993                 }
35994                 if(tag = 'textarea'){
35995                     return w-2;
35996                 }
35997             }
35998         }
35999         return w;
36000     }
36001 });
36002
36003
36004 // anything other than normal should be considered experimental
36005 Roo.form.Field.msgFx = {
36006     normal : {
36007         show: function(msgEl, f){
36008             msgEl.setDisplayed('block');
36009         },
36010
36011         hide : function(msgEl, f){
36012             msgEl.setDisplayed(false).update('');
36013         }
36014     },
36015
36016     slide : {
36017         show: function(msgEl, f){
36018             msgEl.slideIn('t', {stopFx:true});
36019         },
36020
36021         hide : function(msgEl, f){
36022             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36023         }
36024     },
36025
36026     slideRight : {
36027         show: function(msgEl, f){
36028             msgEl.fixDisplay();
36029             msgEl.alignTo(f.el, 'tl-tr');
36030             msgEl.slideIn('l', {stopFx:true});
36031         },
36032
36033         hide : function(msgEl, f){
36034             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36035         }
36036     }
36037 };/*
36038  * Based on:
36039  * Ext JS Library 1.1.1
36040  * Copyright(c) 2006-2007, Ext JS, LLC.
36041  *
36042  * Originally Released Under LGPL - original licence link has changed is not relivant.
36043  *
36044  * Fork - LGPL
36045  * <script type="text/javascript">
36046  */
36047  
36048
36049 /**
36050  * @class Roo.form.TextField
36051  * @extends Roo.form.Field
36052  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36053  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36054  * @constructor
36055  * Creates a new TextField
36056  * @param {Object} config Configuration options
36057  */
36058 Roo.form.TextField = function(config){
36059     Roo.form.TextField.superclass.constructor.call(this, config);
36060     this.addEvents({
36061         /**
36062          * @event autosize
36063          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36064          * according to the default logic, but this event provides a hook for the developer to apply additional
36065          * logic at runtime to resize the field if needed.
36066              * @param {Roo.form.Field} this This text field
36067              * @param {Number} width The new field width
36068              */
36069         autosize : true
36070     });
36071 };
36072
36073 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36074     /**
36075      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36076      */
36077     grow : false,
36078     /**
36079      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36080      */
36081     growMin : 30,
36082     /**
36083      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36084      */
36085     growMax : 800,
36086     /**
36087      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36088      */
36089     vtype : null,
36090     /**
36091      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36092      */
36093     maskRe : null,
36094     /**
36095      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36096      */
36097     disableKeyFilter : false,
36098     /**
36099      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36100      */
36101     allowBlank : true,
36102     /**
36103      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36104      */
36105     minLength : 0,
36106     /**
36107      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36108      */
36109     maxLength : Number.MAX_VALUE,
36110     /**
36111      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36112      */
36113     minLengthText : "The minimum length for this field is {0}",
36114     /**
36115      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36116      */
36117     maxLengthText : "The maximum length for this field is {0}",
36118     /**
36119      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36120      */
36121     selectOnFocus : false,
36122     /**
36123      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36124      */
36125     blankText : "This field is required",
36126     /**
36127      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36128      * If available, this function will be called only after the basic validators all return true, and will be passed the
36129      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36130      */
36131     validator : null,
36132     /**
36133      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36134      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36135      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36136      */
36137     regex : null,
36138     /**
36139      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36140      */
36141     regexText : "",
36142     /**
36143      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36144      */
36145     emptyText : null,
36146     /**
36147      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36148      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36149      */
36150     emptyClass : 'x-form-empty-field',
36151
36152     // private
36153     initEvents : function(){
36154         Roo.form.TextField.superclass.initEvents.call(this);
36155         if(this.validationEvent == 'keyup'){
36156             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36157             this.el.on('keyup', this.filterValidation, this);
36158         }
36159         else if(this.validationEvent !== false){
36160             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36161         }
36162         if(this.selectOnFocus || this.emptyText){
36163             this.on("focus", this.preFocus, this);
36164             if(this.emptyText){
36165                 this.on('blur', this.postBlur, this);
36166                 this.applyEmptyText();
36167             }
36168         }
36169         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36170             this.el.on("keypress", this.filterKeys, this);
36171         }
36172         if(this.grow){
36173             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36174             this.el.on("click", this.autoSize,  this);
36175         }
36176     },
36177
36178     processValue : function(value){
36179         if(this.stripCharsRe){
36180             var newValue = value.replace(this.stripCharsRe, '');
36181             if(newValue !== value){
36182                 this.setRawValue(newValue);
36183                 return newValue;
36184             }
36185         }
36186         return value;
36187     },
36188
36189     filterValidation : function(e){
36190         if(!e.isNavKeyPress()){
36191             this.validationTask.delay(this.validationDelay);
36192         }
36193     },
36194
36195     // private
36196     onKeyUp : function(e){
36197         if(!e.isNavKeyPress()){
36198             this.autoSize();
36199         }
36200     },
36201
36202     /**
36203      * Resets the current field value to the originally-loaded value and clears any validation messages.
36204      * Also adds emptyText and emptyClass if the original value was blank.
36205      */
36206     reset : function(){
36207         Roo.form.TextField.superclass.reset.call(this);
36208         this.applyEmptyText();
36209     },
36210
36211     applyEmptyText : function(){
36212         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36213             this.setRawValue(this.emptyText);
36214             this.el.addClass(this.emptyClass);
36215         }
36216     },
36217
36218     // private
36219     preFocus : function(){
36220         if(this.emptyText){
36221             if(this.el.dom.value == this.emptyText){
36222                 this.setRawValue('');
36223             }
36224             this.el.removeClass(this.emptyClass);
36225         }
36226         if(this.selectOnFocus){
36227             this.el.dom.select();
36228         }
36229     },
36230
36231     // private
36232     postBlur : function(){
36233         this.applyEmptyText();
36234     },
36235
36236     // private
36237     filterKeys : function(e){
36238         var k = e.getKey();
36239         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36240             return;
36241         }
36242         var c = e.getCharCode(), cc = String.fromCharCode(c);
36243         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36244             return;
36245         }
36246         if(!this.maskRe.test(cc)){
36247             e.stopEvent();
36248         }
36249     },
36250
36251     setValue : function(v){
36252         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36253             this.el.removeClass(this.emptyClass);
36254         }
36255         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36256         this.applyEmptyText();
36257         this.autoSize();
36258     },
36259
36260     /**
36261      * Validates a value according to the field's validation rules and marks the field as invalid
36262      * if the validation fails
36263      * @param {Mixed} value The value to validate
36264      * @return {Boolean} True if the value is valid, else false
36265      */
36266     validateValue : function(value){
36267         if(value.length < 1 || value === this.emptyText){ // if it's blank
36268              if(this.allowBlank){
36269                 this.clearInvalid();
36270                 return true;
36271              }else{
36272                 this.markInvalid(this.blankText);
36273                 return false;
36274              }
36275         }
36276         if(value.length < this.minLength){
36277             this.markInvalid(String.format(this.minLengthText, this.minLength));
36278             return false;
36279         }
36280         if(value.length > this.maxLength){
36281             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36282             return false;
36283         }
36284         if(this.vtype){
36285             var vt = Roo.form.VTypes;
36286             if(!vt[this.vtype](value, this)){
36287                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36288                 return false;
36289             }
36290         }
36291         if(typeof this.validator == "function"){
36292             var msg = this.validator(value);
36293             if(msg !== true){
36294                 this.markInvalid(msg);
36295                 return false;
36296             }
36297         }
36298         if(this.regex && !this.regex.test(value)){
36299             this.markInvalid(this.regexText);
36300             return false;
36301         }
36302         return true;
36303     },
36304
36305     /**
36306      * Selects text in this field
36307      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36308      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36309      */
36310     selectText : function(start, end){
36311         var v = this.getRawValue();
36312         if(v.length > 0){
36313             start = start === undefined ? 0 : start;
36314             end = end === undefined ? v.length : end;
36315             var d = this.el.dom;
36316             if(d.setSelectionRange){
36317                 d.setSelectionRange(start, end);
36318             }else if(d.createTextRange){
36319                 var range = d.createTextRange();
36320                 range.moveStart("character", start);
36321                 range.moveEnd("character", v.length-end);
36322                 range.select();
36323             }
36324         }
36325     },
36326
36327     /**
36328      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36329      * This only takes effect if grow = true, and fires the autosize event.
36330      */
36331     autoSize : function(){
36332         if(!this.grow || !this.rendered){
36333             return;
36334         }
36335         if(!this.metrics){
36336             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36337         }
36338         var el = this.el;
36339         var v = el.dom.value;
36340         var d = document.createElement('div');
36341         d.appendChild(document.createTextNode(v));
36342         v = d.innerHTML;
36343         d = null;
36344         v += "&#160;";
36345         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36346         this.el.setWidth(w);
36347         this.fireEvent("autosize", this, w);
36348     }
36349 });/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359  
36360 /**
36361  * @class Roo.form.Hidden
36362  * @extends Roo.form.TextField
36363  * Simple Hidden element used on forms 
36364  * 
36365  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36366  * 
36367  * @constructor
36368  * Creates a new Hidden form element.
36369  * @param {Object} config Configuration options
36370  */
36371
36372
36373
36374 // easy hidden field...
36375 Roo.form.Hidden = function(config){
36376     Roo.form.Hidden.superclass.constructor.call(this, config);
36377 };
36378   
36379 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36380     fieldLabel:      '',
36381     inputType:      'hidden',
36382     width:          50,
36383     allowBlank:     true,
36384     labelSeparator: '',
36385     hidden:         true,
36386     itemCls :       'x-form-item-display-none'
36387
36388
36389 });
36390
36391
36392 /*
36393  * Based on:
36394  * Ext JS Library 1.1.1
36395  * Copyright(c) 2006-2007, Ext JS, LLC.
36396  *
36397  * Originally Released Under LGPL - original licence link has changed is not relivant.
36398  *
36399  * Fork - LGPL
36400  * <script type="text/javascript">
36401  */
36402  
36403 /**
36404  * @class Roo.form.TriggerField
36405  * @extends Roo.form.TextField
36406  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36407  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36408  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36409  * for which you can provide a custom implementation.  For example:
36410  * <pre><code>
36411 var trigger = new Roo.form.TriggerField();
36412 trigger.onTriggerClick = myTriggerFn;
36413 trigger.applyTo('my-field');
36414 </code></pre>
36415  *
36416  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36417  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36418  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36419  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36420  * @constructor
36421  * Create a new TriggerField.
36422  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36423  * to the base TextField)
36424  */
36425 Roo.form.TriggerField = function(config){
36426     this.mimicing = false;
36427     Roo.form.TriggerField.superclass.constructor.call(this, config);
36428 };
36429
36430 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36431     /**
36432      * @cfg {String} triggerClass A CSS class to apply to the trigger
36433      */
36434     /**
36435      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36436      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36437      */
36438     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36439     /**
36440      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36441      */
36442     hideTrigger:false,
36443
36444     /** @cfg {Boolean} grow @hide */
36445     /** @cfg {Number} growMin @hide */
36446     /** @cfg {Number} growMax @hide */
36447
36448     /**
36449      * @hide 
36450      * @method
36451      */
36452     autoSize: Roo.emptyFn,
36453     // private
36454     monitorTab : true,
36455     // private
36456     deferHeight : true,
36457
36458     
36459     actionMode : 'wrap',
36460     // private
36461     onResize : function(w, h){
36462         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36463         if(typeof w == 'number'){
36464             var x = w - this.trigger.getWidth();
36465             this.el.setWidth(this.adjustWidth('input', x));
36466             this.trigger.setStyle('left', x+'px');
36467         }
36468     },
36469
36470     // private
36471     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36472
36473     // private
36474     getResizeEl : function(){
36475         return this.wrap;
36476     },
36477
36478     // private
36479     getPositionEl : function(){
36480         return this.wrap;
36481     },
36482
36483     // private
36484     alignErrorIcon : function(){
36485         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36486     },
36487
36488     // private
36489     onRender : function(ct, position){
36490         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36491         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36492         this.trigger = this.wrap.createChild(this.triggerConfig ||
36493                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36494         if(this.hideTrigger){
36495             this.trigger.setDisplayed(false);
36496         }
36497         this.initTrigger();
36498         if(!this.width){
36499             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36500         }
36501     },
36502
36503     // private
36504     initTrigger : function(){
36505         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36506         this.trigger.addClassOnOver('x-form-trigger-over');
36507         this.trigger.addClassOnClick('x-form-trigger-click');
36508     },
36509
36510     // private
36511     onDestroy : function(){
36512         if(this.trigger){
36513             this.trigger.removeAllListeners();
36514             this.trigger.remove();
36515         }
36516         if(this.wrap){
36517             this.wrap.remove();
36518         }
36519         Roo.form.TriggerField.superclass.onDestroy.call(this);
36520     },
36521
36522     // private
36523     onFocus : function(){
36524         Roo.form.TriggerField.superclass.onFocus.call(this);
36525         if(!this.mimicing){
36526             this.wrap.addClass('x-trigger-wrap-focus');
36527             this.mimicing = true;
36528             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36529             if(this.monitorTab){
36530                 this.el.on("keydown", this.checkTab, this);
36531             }
36532         }
36533     },
36534
36535     // private
36536     checkTab : function(e){
36537         if(e.getKey() == e.TAB){
36538             this.triggerBlur();
36539         }
36540     },
36541
36542     // private
36543     onBlur : function(){
36544         // do nothing
36545     },
36546
36547     // private
36548     mimicBlur : function(e, t){
36549         if(!this.wrap.contains(t) && this.validateBlur()){
36550             this.triggerBlur();
36551         }
36552     },
36553
36554     // private
36555     triggerBlur : function(){
36556         this.mimicing = false;
36557         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36558         if(this.monitorTab){
36559             this.el.un("keydown", this.checkTab, this);
36560         }
36561         this.wrap.removeClass('x-trigger-wrap-focus');
36562         Roo.form.TriggerField.superclass.onBlur.call(this);
36563     },
36564
36565     // private
36566     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36567     validateBlur : function(e, t){
36568         return true;
36569     },
36570
36571     // private
36572     onDisable : function(){
36573         Roo.form.TriggerField.superclass.onDisable.call(this);
36574         if(this.wrap){
36575             this.wrap.addClass('x-item-disabled');
36576         }
36577     },
36578
36579     // private
36580     onEnable : function(){
36581         Roo.form.TriggerField.superclass.onEnable.call(this);
36582         if(this.wrap){
36583             this.wrap.removeClass('x-item-disabled');
36584         }
36585     },
36586
36587     // private
36588     onShow : function(){
36589         var ae = this.getActionEl();
36590         
36591         if(ae){
36592             ae.dom.style.display = '';
36593             ae.dom.style.visibility = 'visible';
36594         }
36595     },
36596
36597     // private
36598     
36599     onHide : function(){
36600         var ae = this.getActionEl();
36601         ae.dom.style.display = 'none';
36602     },
36603
36604     /**
36605      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36606      * by an implementing function.
36607      * @method
36608      * @param {EventObject} e
36609      */
36610     onTriggerClick : Roo.emptyFn
36611 });
36612
36613 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36614 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36615 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36616 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36617     initComponent : function(){
36618         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36619
36620         this.triggerConfig = {
36621             tag:'span', cls:'x-form-twin-triggers', cn:[
36622             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36623             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36624         ]};
36625     },
36626
36627     getTrigger : function(index){
36628         return this.triggers[index];
36629     },
36630
36631     initTrigger : function(){
36632         var ts = this.trigger.select('.x-form-trigger', true);
36633         this.wrap.setStyle('overflow', 'hidden');
36634         var triggerField = this;
36635         ts.each(function(t, all, index){
36636             t.hide = function(){
36637                 var w = triggerField.wrap.getWidth();
36638                 this.dom.style.display = 'none';
36639                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36640             };
36641             t.show = function(){
36642                 var w = triggerField.wrap.getWidth();
36643                 this.dom.style.display = '';
36644                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36645             };
36646             var triggerIndex = 'Trigger'+(index+1);
36647
36648             if(this['hide'+triggerIndex]){
36649                 t.dom.style.display = 'none';
36650             }
36651             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36652             t.addClassOnOver('x-form-trigger-over');
36653             t.addClassOnClick('x-form-trigger-click');
36654         }, this);
36655         this.triggers = ts.elements;
36656     },
36657
36658     onTrigger1Click : Roo.emptyFn,
36659     onTrigger2Click : Roo.emptyFn
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670  
36671 /**
36672  * @class Roo.form.TextArea
36673  * @extends Roo.form.TextField
36674  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36675  * support for auto-sizing.
36676  * @constructor
36677  * Creates a new TextArea
36678  * @param {Object} config Configuration options
36679  */
36680 Roo.form.TextArea = function(config){
36681     Roo.form.TextArea.superclass.constructor.call(this, config);
36682     // these are provided exchanges for backwards compat
36683     // minHeight/maxHeight were replaced by growMin/growMax to be
36684     // compatible with TextField growing config values
36685     if(this.minHeight !== undefined){
36686         this.growMin = this.minHeight;
36687     }
36688     if(this.maxHeight !== undefined){
36689         this.growMax = this.maxHeight;
36690     }
36691 };
36692
36693 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36694     /**
36695      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36696      */
36697     growMin : 60,
36698     /**
36699      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36700      */
36701     growMax: 1000,
36702     /**
36703      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36704      * in the field (equivalent to setting overflow: hidden, defaults to false)
36705      */
36706     preventScrollbars: false,
36707     /**
36708      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36709      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36710      */
36711
36712     // private
36713     onRender : function(ct, position){
36714         if(!this.el){
36715             this.defaultAutoCreate = {
36716                 tag: "textarea",
36717                 style:"width:300px;height:60px;",
36718                 autocomplete: "off"
36719             };
36720         }
36721         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36722         if(this.grow){
36723             this.textSizeEl = Roo.DomHelper.append(document.body, {
36724                 tag: "pre", cls: "x-form-grow-sizer"
36725             });
36726             if(this.preventScrollbars){
36727                 this.el.setStyle("overflow", "hidden");
36728             }
36729             this.el.setHeight(this.growMin);
36730         }
36731     },
36732
36733     onDestroy : function(){
36734         if(this.textSizeEl){
36735             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36736         }
36737         Roo.form.TextArea.superclass.onDestroy.call(this);
36738     },
36739
36740     // private
36741     onKeyUp : function(e){
36742         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36743             this.autoSize();
36744         }
36745     },
36746
36747     /**
36748      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36749      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36750      */
36751     autoSize : function(){
36752         if(!this.grow || !this.textSizeEl){
36753             return;
36754         }
36755         var el = this.el;
36756         var v = el.dom.value;
36757         var ts = this.textSizeEl;
36758
36759         ts.innerHTML = '';
36760         ts.appendChild(document.createTextNode(v));
36761         v = ts.innerHTML;
36762
36763         Roo.fly(ts).setWidth(this.el.getWidth());
36764         if(v.length < 1){
36765             v = "&#160;&#160;";
36766         }else{
36767             if(Roo.isIE){
36768                 v = v.replace(/\n/g, '<p>&#160;</p>');
36769             }
36770             v += "&#160;\n&#160;";
36771         }
36772         ts.innerHTML = v;
36773         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36774         if(h != this.lastHeight){
36775             this.lastHeight = h;
36776             this.el.setHeight(h);
36777             this.fireEvent("autosize", this, h);
36778         }
36779     }
36780 });/*
36781  * Based on:
36782  * Ext JS Library 1.1.1
36783  * Copyright(c) 2006-2007, Ext JS, LLC.
36784  *
36785  * Originally Released Under LGPL - original licence link has changed is not relivant.
36786  *
36787  * Fork - LGPL
36788  * <script type="text/javascript">
36789  */
36790  
36791
36792 /**
36793  * @class Roo.form.NumberField
36794  * @extends Roo.form.TextField
36795  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36796  * @constructor
36797  * Creates a new NumberField
36798  * @param {Object} config Configuration options
36799  */
36800 Roo.form.NumberField = function(config){
36801     Roo.form.NumberField.superclass.constructor.call(this, config);
36802 };
36803
36804 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36805     /**
36806      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36807      */
36808     fieldClass: "x-form-field x-form-num-field",
36809     /**
36810      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36811      */
36812     allowDecimals : true,
36813     /**
36814      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36815      */
36816     decimalSeparator : ".",
36817     /**
36818      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36819      */
36820     decimalPrecision : 2,
36821     /**
36822      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36823      */
36824     allowNegative : true,
36825     /**
36826      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36827      */
36828     minValue : Number.NEGATIVE_INFINITY,
36829     /**
36830      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36831      */
36832     maxValue : Number.MAX_VALUE,
36833     /**
36834      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36835      */
36836     minText : "The minimum value for this field is {0}",
36837     /**
36838      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36839      */
36840     maxText : "The maximum value for this field is {0}",
36841     /**
36842      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36843      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36844      */
36845     nanText : "{0} is not a valid number",
36846
36847     // private
36848     initEvents : function(){
36849         Roo.form.NumberField.superclass.initEvents.call(this);
36850         var allowed = "0123456789";
36851         if(this.allowDecimals){
36852             allowed += this.decimalSeparator;
36853         }
36854         if(this.allowNegative){
36855             allowed += "-";
36856         }
36857         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36858         var keyPress = function(e){
36859             var k = e.getKey();
36860             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36861                 return;
36862             }
36863             var c = e.getCharCode();
36864             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36865                 e.stopEvent();
36866             }
36867         };
36868         this.el.on("keypress", keyPress, this);
36869     },
36870
36871     // private
36872     validateValue : function(value){
36873         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36874             return false;
36875         }
36876         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36877              return true;
36878         }
36879         var num = this.parseValue(value);
36880         if(isNaN(num)){
36881             this.markInvalid(String.format(this.nanText, value));
36882             return false;
36883         }
36884         if(num < this.minValue){
36885             this.markInvalid(String.format(this.minText, this.minValue));
36886             return false;
36887         }
36888         if(num > this.maxValue){
36889             this.markInvalid(String.format(this.maxText, this.maxValue));
36890             return false;
36891         }
36892         return true;
36893     },
36894
36895     getValue : function(){
36896         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36897     },
36898
36899     // private
36900     parseValue : function(value){
36901         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36902         return isNaN(value) ? '' : value;
36903     },
36904
36905     // private
36906     fixPrecision : function(value){
36907         var nan = isNaN(value);
36908         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36909             return nan ? '' : value;
36910         }
36911         return parseFloat(value).toFixed(this.decimalPrecision);
36912     },
36913
36914     setValue : function(v){
36915         v = this.fixPrecision(v);
36916         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36917     },
36918
36919     // private
36920     decimalPrecisionFcn : function(v){
36921         return Math.floor(v);
36922     },
36923
36924     beforeBlur : function(){
36925         var v = this.parseValue(this.getRawValue());
36926         if(v){
36927             this.setValue(v);
36928         }
36929     }
36930 });/*
36931  * Based on:
36932  * Ext JS Library 1.1.1
36933  * Copyright(c) 2006-2007, Ext JS, LLC.
36934  *
36935  * Originally Released Under LGPL - original licence link has changed is not relivant.
36936  *
36937  * Fork - LGPL
36938  * <script type="text/javascript">
36939  */
36940  
36941 /**
36942  * @class Roo.form.DateField
36943  * @extends Roo.form.TriggerField
36944  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36945 * @constructor
36946 * Create a new DateField
36947 * @param {Object} config
36948  */
36949 Roo.form.DateField = function(config){
36950     Roo.form.DateField.superclass.constructor.call(this, config);
36951     
36952       this.addEvents({
36953          
36954         /**
36955          * @event select
36956          * Fires when a date is selected
36957              * @param {Roo.form.DateField} combo This combo box
36958              * @param {Date} date The date selected
36959              */
36960         'select' : true
36961          
36962     });
36963     
36964     
36965     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36966     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36967     this.ddMatch = null;
36968     if(this.disabledDates){
36969         var dd = this.disabledDates;
36970         var re = "(?:";
36971         for(var i = 0; i < dd.length; i++){
36972             re += dd[i];
36973             if(i != dd.length-1) re += "|";
36974         }
36975         this.ddMatch = new RegExp(re + ")");
36976     }
36977 };
36978
36979 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36980     /**
36981      * @cfg {String} format
36982      * The default date format string which can be overriden for localization support.  The format must be
36983      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36984      */
36985     format : "m/d/y",
36986     /**
36987      * @cfg {String} altFormats
36988      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36989      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36990      */
36991     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36992     /**
36993      * @cfg {Array} disabledDays
36994      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36995      */
36996     disabledDays : null,
36997     /**
36998      * @cfg {String} disabledDaysText
36999      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37000      */
37001     disabledDaysText : "Disabled",
37002     /**
37003      * @cfg {Array} disabledDates
37004      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37005      * expression so they are very powerful. Some examples:
37006      * <ul>
37007      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37008      * <li>["03/08", "09/16"] would disable those days for every year</li>
37009      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37010      * <li>["03/../2006"] would disable every day in March 2006</li>
37011      * <li>["^03"] would disable every day in every March</li>
37012      * </ul>
37013      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37014      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37015      */
37016     disabledDates : null,
37017     /**
37018      * @cfg {String} disabledDatesText
37019      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37020      */
37021     disabledDatesText : "Disabled",
37022     /**
37023      * @cfg {Date/String} minValue
37024      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37025      * valid format (defaults to null).
37026      */
37027     minValue : null,
37028     /**
37029      * @cfg {Date/String} maxValue
37030      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37031      * valid format (defaults to null).
37032      */
37033     maxValue : null,
37034     /**
37035      * @cfg {String} minText
37036      * The error text to display when the date in the cell is before minValue (defaults to
37037      * 'The date in this field must be after {minValue}').
37038      */
37039     minText : "The date in this field must be equal to or after {0}",
37040     /**
37041      * @cfg {String} maxText
37042      * The error text to display when the date in the cell is after maxValue (defaults to
37043      * 'The date in this field must be before {maxValue}').
37044      */
37045     maxText : "The date in this field must be equal to or before {0}",
37046     /**
37047      * @cfg {String} invalidText
37048      * The error text to display when the date in the field is invalid (defaults to
37049      * '{value} is not a valid date - it must be in the format {format}').
37050      */
37051     invalidText : "{0} is not a valid date - it must be in the format {1}",
37052     /**
37053      * @cfg {String} triggerClass
37054      * An additional CSS class used to style the trigger button.  The trigger will always get the
37055      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37056      * which displays a calendar icon).
37057      */
37058     triggerClass : 'x-form-date-trigger',
37059     
37060
37061     /**
37062      * @cfg {bool} useIso
37063      * if enabled, then the date field will use a hidden field to store the 
37064      * real value as iso formated date. default (false)
37065      */ 
37066     useIso : false,
37067     /**
37068      * @cfg {String/Object} autoCreate
37069      * A DomHelper element spec, or true for a default element spec (defaults to
37070      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37071      */ 
37072     // private
37073     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37074     
37075     // private
37076     hiddenField: false,
37077     
37078     onRender : function(ct, position)
37079     {
37080         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37081         if (this.useIso) {
37082             this.el.dom.removeAttribute('name'); 
37083             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37084                     'before', true);
37085             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37086             // prevent input submission
37087             this.hiddenName = this.name;
37088         }
37089             
37090             
37091     },
37092     
37093     // private
37094     validateValue : function(value)
37095     {
37096         value = this.formatDate(value);
37097         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37098             return false;
37099         }
37100         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37101              return true;
37102         }
37103         var svalue = value;
37104         value = this.parseDate(value);
37105         if(!value){
37106             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37107             return false;
37108         }
37109         var time = value.getTime();
37110         if(this.minValue && time < this.minValue.getTime()){
37111             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37112             return false;
37113         }
37114         if(this.maxValue && time > this.maxValue.getTime()){
37115             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37116             return false;
37117         }
37118         if(this.disabledDays){
37119             var day = value.getDay();
37120             for(var i = 0; i < this.disabledDays.length; i++) {
37121                 if(day === this.disabledDays[i]){
37122                     this.markInvalid(this.disabledDaysText);
37123                     return false;
37124                 }
37125             }
37126         }
37127         var fvalue = this.formatDate(value);
37128         if(this.ddMatch && this.ddMatch.test(fvalue)){
37129             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37130             return false;
37131         }
37132         return true;
37133     },
37134
37135     // private
37136     // Provides logic to override the default TriggerField.validateBlur which just returns true
37137     validateBlur : function(){
37138         return !this.menu || !this.menu.isVisible();
37139     },
37140
37141     /**
37142      * Returns the current date value of the date field.
37143      * @return {Date} The date value
37144      */
37145     getValue : function(){
37146         
37147         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37148     },
37149
37150     /**
37151      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37152      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37153      * (the default format used is "m/d/y").
37154      * <br />Usage:
37155      * <pre><code>
37156 //All of these calls set the same date value (May 4, 2006)
37157
37158 //Pass a date object:
37159 var dt = new Date('5/4/06');
37160 dateField.setValue(dt);
37161
37162 //Pass a date string (default format):
37163 dateField.setValue('5/4/06');
37164
37165 //Pass a date string (custom format):
37166 dateField.format = 'Y-m-d';
37167 dateField.setValue('2006-5-4');
37168 </code></pre>
37169      * @param {String/Date} date The date or valid date string
37170      */
37171     setValue : function(date){
37172         if (this.hiddenField) {
37173             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37174         }
37175         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37176     },
37177
37178     // private
37179     parseDate : function(value){
37180         if(!value || value instanceof Date){
37181             return value;
37182         }
37183         var v = Date.parseDate(value, this.format);
37184         if(!v && this.altFormats){
37185             if(!this.altFormatsArray){
37186                 this.altFormatsArray = this.altFormats.split("|");
37187             }
37188             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37189                 v = Date.parseDate(value, this.altFormatsArray[i]);
37190             }
37191         }
37192         return v;
37193     },
37194
37195     // private
37196     formatDate : function(date, fmt){
37197         return (!date || !(date instanceof Date)) ?
37198                date : date.dateFormat(fmt || this.format);
37199     },
37200
37201     // private
37202     menuListeners : {
37203         select: function(m, d){
37204             this.setValue(d);
37205             this.fireEvent('select', this, d);
37206         },
37207         show : function(){ // retain focus styling
37208             this.onFocus();
37209         },
37210         hide : function(){
37211             this.focus.defer(10, this);
37212             var ml = this.menuListeners;
37213             this.menu.un("select", ml.select,  this);
37214             this.menu.un("show", ml.show,  this);
37215             this.menu.un("hide", ml.hide,  this);
37216         }
37217     },
37218
37219     // private
37220     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37221     onTriggerClick : function(){
37222         if(this.disabled){
37223             return;
37224         }
37225         if(this.menu == null){
37226             this.menu = new Roo.menu.DateMenu();
37227         }
37228         Roo.apply(this.menu.picker,  {
37229             showClear: this.allowBlank,
37230             minDate : this.minValue,
37231             maxDate : this.maxValue,
37232             disabledDatesRE : this.ddMatch,
37233             disabledDatesText : this.disabledDatesText,
37234             disabledDays : this.disabledDays,
37235             disabledDaysText : this.disabledDaysText,
37236             format : this.format,
37237             minText : String.format(this.minText, this.formatDate(this.minValue)),
37238             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37239         });
37240         this.menu.on(Roo.apply({}, this.menuListeners, {
37241             scope:this
37242         }));
37243         this.menu.picker.setValue(this.getValue() || new Date());
37244         this.menu.show(this.el, "tl-bl?");
37245     },
37246
37247     beforeBlur : function(){
37248         var v = this.parseDate(this.getRawValue());
37249         if(v){
37250             this.setValue(v);
37251         }
37252     }
37253
37254     /** @cfg {Boolean} grow @hide */
37255     /** @cfg {Number} growMin @hide */
37256     /** @cfg {Number} growMax @hide */
37257     /**
37258      * @hide
37259      * @method autoSize
37260      */
37261 });/*
37262  * Based on:
37263  * Ext JS Library 1.1.1
37264  * Copyright(c) 2006-2007, Ext JS, LLC.
37265  *
37266  * Originally Released Under LGPL - original licence link has changed is not relivant.
37267  *
37268  * Fork - LGPL
37269  * <script type="text/javascript">
37270  */
37271  
37272
37273 /**
37274  * @class Roo.form.ComboBox
37275  * @extends Roo.form.TriggerField
37276  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37277  * @constructor
37278  * Create a new ComboBox.
37279  * @param {Object} config Configuration options
37280  */
37281 Roo.form.ComboBox = function(config){
37282     Roo.form.ComboBox.superclass.constructor.call(this, config);
37283     this.addEvents({
37284         /**
37285          * @event expand
37286          * Fires when the dropdown list is expanded
37287              * @param {Roo.form.ComboBox} combo This combo box
37288              */
37289         'expand' : true,
37290         /**
37291          * @event collapse
37292          * Fires when the dropdown list is collapsed
37293              * @param {Roo.form.ComboBox} combo This combo box
37294              */
37295         'collapse' : true,
37296         /**
37297          * @event beforeselect
37298          * Fires before a list item is selected. Return false to cancel the selection.
37299              * @param {Roo.form.ComboBox} combo This combo box
37300              * @param {Roo.data.Record} record The data record returned from the underlying store
37301              * @param {Number} index The index of the selected item in the dropdown list
37302              */
37303         'beforeselect' : true,
37304         /**
37305          * @event select
37306          * Fires when a list item is selected
37307              * @param {Roo.form.ComboBox} combo This combo box
37308              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37309              * @param {Number} index The index of the selected item in the dropdown list
37310              */
37311         'select' : true,
37312         /**
37313          * @event beforequery
37314          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37315          * The event object passed has these properties:
37316              * @param {Roo.form.ComboBox} combo This combo box
37317              * @param {String} query The query
37318              * @param {Boolean} forceAll true to force "all" query
37319              * @param {Boolean} cancel true to cancel the query
37320              * @param {Object} e The query event object
37321              */
37322         'beforequery': true,
37323          /**
37324          * @event add
37325          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37326              * @param {Roo.form.ComboBox} combo This combo box
37327              */
37328         'add' : true,
37329         /**
37330          * @event edit
37331          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37332              * @param {Roo.form.ComboBox} combo This combo box
37333              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37334              */
37335         'edit' : true
37336         
37337         
37338     });
37339     if(this.transform){
37340         this.allowDomMove = false;
37341         var s = Roo.getDom(this.transform);
37342         if(!this.hiddenName){
37343             this.hiddenName = s.name;
37344         }
37345         if(!this.store){
37346             this.mode = 'local';
37347             var d = [], opts = s.options;
37348             for(var i = 0, len = opts.length;i < len; i++){
37349                 var o = opts[i];
37350                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37351                 if(o.selected) {
37352                     this.value = value;
37353                 }
37354                 d.push([value, o.text]);
37355             }
37356             this.store = new Roo.data.SimpleStore({
37357                 'id': 0,
37358                 fields: ['value', 'text'],
37359                 data : d
37360             });
37361             this.valueField = 'value';
37362             this.displayField = 'text';
37363         }
37364         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37365         if(!this.lazyRender){
37366             this.target = true;
37367             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37368             s.parentNode.removeChild(s); // remove it
37369             this.render(this.el.parentNode);
37370         }else{
37371             s.parentNode.removeChild(s); // remove it
37372         }
37373
37374     }
37375     if (this.store) {
37376         this.store = Roo.factory(this.store, Roo.data);
37377     }
37378     
37379     this.selectedIndex = -1;
37380     if(this.mode == 'local'){
37381         if(config.queryDelay === undefined){
37382             this.queryDelay = 10;
37383         }
37384         if(config.minChars === undefined){
37385             this.minChars = 0;
37386         }
37387     }
37388 };
37389
37390 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37391     /**
37392      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37393      */
37394     /**
37395      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37396      * rendering into an Roo.Editor, defaults to false)
37397      */
37398     /**
37399      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37400      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37401      */
37402     /**
37403      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37404      */
37405     /**
37406      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37407      * the dropdown list (defaults to undefined, with no header element)
37408      */
37409
37410      /**
37411      * @cfg {String/Roo.Template} tpl The template to use to render the output
37412      */
37413      
37414     // private
37415     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37416     /**
37417      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37418      */
37419     listWidth: undefined,
37420     /**
37421      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37422      * mode = 'remote' or 'text' if mode = 'local')
37423      */
37424     displayField: undefined,
37425     /**
37426      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37427      * mode = 'remote' or 'value' if mode = 'local'). 
37428      * Note: use of a valueField requires the user make a selection
37429      * in order for a value to be mapped.
37430      */
37431     valueField: undefined,
37432     
37433     
37434     /**
37435      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37436      * field's data value (defaults to the underlying DOM element's name)
37437      */
37438     hiddenName: undefined,
37439     /**
37440      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37441      */
37442     listClass: '',
37443     /**
37444      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37445      */
37446     selectedClass: 'x-combo-selected',
37447     /**
37448      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37449      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37450      * which displays a downward arrow icon).
37451      */
37452     triggerClass : 'x-form-arrow-trigger',
37453     /**
37454      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37455      */
37456     shadow:'sides',
37457     /**
37458      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37459      * anchor positions (defaults to 'tl-bl')
37460      */
37461     listAlign: 'tl-bl?',
37462     /**
37463      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37464      */
37465     maxHeight: 300,
37466     /**
37467      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37468      * query specified by the allQuery config option (defaults to 'query')
37469      */
37470     triggerAction: 'query',
37471     /**
37472      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37473      * (defaults to 4, does not apply if editable = false)
37474      */
37475     minChars : 4,
37476     /**
37477      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37478      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37479      */
37480     typeAhead: false,
37481     /**
37482      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37483      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37484      */
37485     queryDelay: 500,
37486     /**
37487      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37488      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37489      */
37490     pageSize: 0,
37491     /**
37492      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37493      * when editable = true (defaults to false)
37494      */
37495     selectOnFocus:false,
37496     /**
37497      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37498      */
37499     queryParam: 'query',
37500     /**
37501      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37502      * when mode = 'remote' (defaults to 'Loading...')
37503      */
37504     loadingText: 'Loading...',
37505     /**
37506      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37507      */
37508     resizable: false,
37509     /**
37510      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37511      */
37512     handleHeight : 8,
37513     /**
37514      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37515      * traditional select (defaults to true)
37516      */
37517     editable: true,
37518     /**
37519      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37520      */
37521     allQuery: '',
37522     /**
37523      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37524      */
37525     mode: 'remote',
37526     /**
37527      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37528      * listWidth has a higher value)
37529      */
37530     minListWidth : 70,
37531     /**
37532      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37533      * allow the user to set arbitrary text into the field (defaults to false)
37534      */
37535     forceSelection:false,
37536     /**
37537      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37538      * if typeAhead = true (defaults to 250)
37539      */
37540     typeAheadDelay : 250,
37541     /**
37542      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37543      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37544      */
37545     valueNotFoundText : undefined,
37546     /**
37547      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37548      */
37549     blockFocus : false,
37550     
37551     /**
37552      * @cfg {Boolean} disableClear Disable showing of clear button.
37553      */
37554     disableClear : false,
37555     /**
37556      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37557      */
37558     alwaysQuery : false,
37559     
37560     //private
37561     addicon : false,
37562     editicon: false,
37563     
37564     // element that contains real text value.. (when hidden is used..)
37565      
37566     // private
37567     onRender : function(ct, position){
37568         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37569         if(this.hiddenName){
37570             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37571                     'before', true);
37572             this.hiddenField.value =
37573                 this.hiddenValue !== undefined ? this.hiddenValue :
37574                 this.value !== undefined ? this.value : '';
37575
37576             // prevent input submission
37577             this.el.dom.removeAttribute('name');
37578              
37579              
37580         }
37581         if(Roo.isGecko){
37582             this.el.dom.setAttribute('autocomplete', 'off');
37583         }
37584
37585         var cls = 'x-combo-list';
37586
37587         this.list = new Roo.Layer({
37588             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37589         });
37590
37591         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37592         this.list.setWidth(lw);
37593         this.list.swallowEvent('mousewheel');
37594         this.assetHeight = 0;
37595
37596         if(this.title){
37597             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37598             this.assetHeight += this.header.getHeight();
37599         }
37600
37601         this.innerList = this.list.createChild({cls:cls+'-inner'});
37602         this.innerList.on('mouseover', this.onViewOver, this);
37603         this.innerList.on('mousemove', this.onViewMove, this);
37604         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37605         
37606         if(this.allowBlank && !this.pageSize && !this.disableClear){
37607             this.footer = this.list.createChild({cls:cls+'-ft'});
37608             this.pageTb = new Roo.Toolbar(this.footer);
37609            
37610         }
37611         if(this.pageSize){
37612             this.footer = this.list.createChild({cls:cls+'-ft'});
37613             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37614                     {pageSize: this.pageSize});
37615             
37616         }
37617         
37618         if (this.pageTb && this.allowBlank && !this.disableClear) {
37619             var _this = this;
37620             this.pageTb.add(new Roo.Toolbar.Fill(), {
37621                 cls: 'x-btn-icon x-btn-clear',
37622                 text: '&#160;',
37623                 handler: function()
37624                 {
37625                     _this.collapse();
37626                     _this.clearValue();
37627                     _this.onSelect(false, -1);
37628                 }
37629             });
37630         }
37631         if (this.footer) {
37632             this.assetHeight += this.footer.getHeight();
37633         }
37634         
37635
37636         if(!this.tpl){
37637             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37638         }
37639
37640         this.view = new Roo.View(this.innerList, this.tpl, {
37641             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37642         });
37643
37644         this.view.on('click', this.onViewClick, this);
37645
37646         this.store.on('beforeload', this.onBeforeLoad, this);
37647         this.store.on('load', this.onLoad, this);
37648         this.store.on('loadexception', this.onLoadException, this);
37649
37650         if(this.resizable){
37651             this.resizer = new Roo.Resizable(this.list,  {
37652                pinned:true, handles:'se'
37653             });
37654             this.resizer.on('resize', function(r, w, h){
37655                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37656                 this.listWidth = w;
37657                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37658                 this.restrictHeight();
37659             }, this);
37660             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37661         }
37662         if(!this.editable){
37663             this.editable = true;
37664             this.setEditable(false);
37665         }  
37666         
37667         
37668         if (typeof(this.events.add.listeners) != 'undefined') {
37669             
37670             this.addicon = this.wrap.createChild(
37671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37672        
37673             this.addicon.on('click', function(e) {
37674                 this.fireEvent('add', this);
37675             }, this);
37676         }
37677         if (typeof(this.events.edit.listeners) != 'undefined') {
37678             
37679             this.editicon = this.wrap.createChild(
37680                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37681             if (this.addicon) {
37682                 this.editicon.setStyle('margin-left', '40px');
37683             }
37684             this.editicon.on('click', function(e) {
37685                 
37686                 // we fire even  if inothing is selected..
37687                 this.fireEvent('edit', this, this.lastData );
37688                 
37689             }, this);
37690         }
37691         
37692         
37693         
37694     },
37695
37696     // private
37697     initEvents : function(){
37698         Roo.form.ComboBox.superclass.initEvents.call(this);
37699
37700         this.keyNav = new Roo.KeyNav(this.el, {
37701             "up" : function(e){
37702                 this.inKeyMode = true;
37703                 this.selectPrev();
37704             },
37705
37706             "down" : function(e){
37707                 if(!this.isExpanded()){
37708                     this.onTriggerClick();
37709                 }else{
37710                     this.inKeyMode = true;
37711                     this.selectNext();
37712                 }
37713             },
37714
37715             "enter" : function(e){
37716                 this.onViewClick();
37717                 //return true;
37718             },
37719
37720             "esc" : function(e){
37721                 this.collapse();
37722             },
37723
37724             "tab" : function(e){
37725                 this.onViewClick(false);
37726                 this.fireEvent("specialkey", this, e);
37727                 return true;
37728             },
37729
37730             scope : this,
37731
37732             doRelay : function(foo, bar, hname){
37733                 if(hname == 'down' || this.scope.isExpanded()){
37734                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37735                 }
37736                 return true;
37737             },
37738
37739             forceKeyDown: true
37740         });
37741         this.queryDelay = Math.max(this.queryDelay || 10,
37742                 this.mode == 'local' ? 10 : 250);
37743         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37744         if(this.typeAhead){
37745             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37746         }
37747         if(this.editable !== false){
37748             this.el.on("keyup", this.onKeyUp, this);
37749         }
37750         if(this.forceSelection){
37751             this.on('blur', this.doForce, this);
37752         }
37753     },
37754
37755     onDestroy : function(){
37756         if(this.view){
37757             this.view.setStore(null);
37758             this.view.el.removeAllListeners();
37759             this.view.el.remove();
37760             this.view.purgeListeners();
37761         }
37762         if(this.list){
37763             this.list.destroy();
37764         }
37765         if(this.store){
37766             this.store.un('beforeload', this.onBeforeLoad, this);
37767             this.store.un('load', this.onLoad, this);
37768             this.store.un('loadexception', this.onLoadException, this);
37769         }
37770         Roo.form.ComboBox.superclass.onDestroy.call(this);
37771     },
37772
37773     // private
37774     fireKey : function(e){
37775         if(e.isNavKeyPress() && !this.list.isVisible()){
37776             this.fireEvent("specialkey", this, e);
37777         }
37778     },
37779
37780     // private
37781     onResize: function(w, h){
37782         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37783         
37784         if(typeof w != 'number'){
37785             // we do not handle it!?!?
37786             return;
37787         }
37788         var tw = this.trigger.getWidth();
37789         tw += this.addicon ? this.addicon.getWidth() : 0;
37790         tw += this.editicon ? this.editicon.getWidth() : 0;
37791         var x = w - tw;
37792         this.el.setWidth( this.adjustWidth('input', x));
37793             
37794         this.trigger.setStyle('left', x+'px');
37795         
37796         if(this.list && this.listWidth === undefined){
37797             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37798             this.list.setWidth(lw);
37799             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37800         }
37801         
37802     
37803         
37804     },
37805
37806     /**
37807      * Allow or prevent the user from directly editing the field text.  If false is passed,
37808      * the user will only be able to select from the items defined in the dropdown list.  This method
37809      * is the runtime equivalent of setting the 'editable' config option at config time.
37810      * @param {Boolean} value True to allow the user to directly edit the field text
37811      */
37812     setEditable : function(value){
37813         if(value == this.editable){
37814             return;
37815         }
37816         this.editable = value;
37817         if(!value){
37818             this.el.dom.setAttribute('readOnly', true);
37819             this.el.on('mousedown', this.onTriggerClick,  this);
37820             this.el.addClass('x-combo-noedit');
37821         }else{
37822             this.el.dom.setAttribute('readOnly', false);
37823             this.el.un('mousedown', this.onTriggerClick,  this);
37824             this.el.removeClass('x-combo-noedit');
37825         }
37826     },
37827
37828     // private
37829     onBeforeLoad : function(){
37830         if(!this.hasFocus){
37831             return;
37832         }
37833         this.innerList.update(this.loadingText ?
37834                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37835         this.restrictHeight();
37836         this.selectedIndex = -1;
37837     },
37838
37839     // private
37840     onLoad : function(){
37841         if(!this.hasFocus){
37842             return;
37843         }
37844         if(this.store.getCount() > 0){
37845             this.expand();
37846             this.restrictHeight();
37847             if(this.lastQuery == this.allQuery){
37848                 if(this.editable){
37849                     this.el.dom.select();
37850                 }
37851                 if(!this.selectByValue(this.value, true)){
37852                     this.select(0, true);
37853                 }
37854             }else{
37855                 this.selectNext();
37856                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37857                     this.taTask.delay(this.typeAheadDelay);
37858                 }
37859             }
37860         }else{
37861             this.onEmptyResults();
37862         }
37863         //this.el.focus();
37864     },
37865     // private
37866     onLoadException : function()
37867     {
37868         this.collapse();
37869         Roo.log(this.store.reader.jsonData);
37870         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37871             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37872         }
37873         
37874         
37875     },
37876     // private
37877     onTypeAhead : function(){
37878         if(this.store.getCount() > 0){
37879             var r = this.store.getAt(0);
37880             var newValue = r.data[this.displayField];
37881             var len = newValue.length;
37882             var selStart = this.getRawValue().length;
37883             if(selStart != len){
37884                 this.setRawValue(newValue);
37885                 this.selectText(selStart, newValue.length);
37886             }
37887         }
37888     },
37889
37890     // private
37891     onSelect : function(record, index){
37892         if(this.fireEvent('beforeselect', this, record, index) !== false){
37893             this.setFromData(index > -1 ? record.data : false);
37894             this.collapse();
37895             this.fireEvent('select', this, record, index);
37896         }
37897     },
37898
37899     /**
37900      * Returns the currently selected field value or empty string if no value is set.
37901      * @return {String} value The selected value
37902      */
37903     getValue : function(){
37904         if(this.valueField){
37905             return typeof this.value != 'undefined' ? this.value : '';
37906         }else{
37907             return Roo.form.ComboBox.superclass.getValue.call(this);
37908         }
37909     },
37910
37911     /**
37912      * Clears any text/value currently set in the field
37913      */
37914     clearValue : function(){
37915         if(this.hiddenField){
37916             this.hiddenField.value = '';
37917         }
37918         this.value = '';
37919         this.setRawValue('');
37920         this.lastSelectionText = '';
37921         this.applyEmptyText();
37922     },
37923
37924     /**
37925      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37926      * will be displayed in the field.  If the value does not match the data value of an existing item,
37927      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37928      * Otherwise the field will be blank (although the value will still be set).
37929      * @param {String} value The value to match
37930      */
37931     setValue : function(v){
37932         var text = v;
37933         if(this.valueField){
37934             var r = this.findRecord(this.valueField, v);
37935             if(r){
37936                 text = r.data[this.displayField];
37937             }else if(this.valueNotFoundText !== undefined){
37938                 text = this.valueNotFoundText;
37939             }
37940         }
37941         this.lastSelectionText = text;
37942         if(this.hiddenField){
37943             this.hiddenField.value = v;
37944         }
37945         Roo.form.ComboBox.superclass.setValue.call(this, text);
37946         this.value = v;
37947     },
37948     /**
37949      * @property {Object} the last set data for the element
37950      */
37951     
37952     lastData : false,
37953     /**
37954      * Sets the value of the field based on a object which is related to the record format for the store.
37955      * @param {Object} value the value to set as. or false on reset?
37956      */
37957     setFromData : function(o){
37958         var dv = ''; // display value
37959         var vv = ''; // value value..
37960         this.lastData = o;
37961         if (this.displayField) {
37962             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37963         } else {
37964             // this is an error condition!!!
37965             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37966         }
37967         
37968         if(this.valueField){
37969             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37970         }
37971         if(this.hiddenField){
37972             this.hiddenField.value = vv;
37973             
37974             this.lastSelectionText = dv;
37975             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37976             this.value = vv;
37977             return;
37978         }
37979         // no hidden field.. - we store the value in 'value', but still display
37980         // display field!!!!
37981         this.lastSelectionText = dv;
37982         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37983         this.value = vv;
37984         
37985         
37986     },
37987     // private
37988     reset : function(){
37989         // overridden so that last data is reset..
37990         this.setValue(this.originalValue);
37991         this.clearInvalid();
37992         this.lastData = false;
37993     },
37994     // private
37995     findRecord : function(prop, value){
37996         var record;
37997         if(this.store.getCount() > 0){
37998             this.store.each(function(r){
37999                 if(r.data[prop] == value){
38000                     record = r;
38001                     return false;
38002                 }
38003                 return true;
38004             });
38005         }
38006         return record;
38007     },
38008     
38009     getName: function()
38010     {
38011         // returns hidden if it's set..
38012         if (!this.rendered) {return ''};
38013         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38014         
38015     },
38016     // private
38017     onViewMove : function(e, t){
38018         this.inKeyMode = false;
38019     },
38020
38021     // private
38022     onViewOver : function(e, t){
38023         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38024             return;
38025         }
38026         var item = this.view.findItemFromChild(t);
38027         if(item){
38028             var index = this.view.indexOf(item);
38029             this.select(index, false);
38030         }
38031     },
38032
38033     // private
38034     onViewClick : function(doFocus)
38035     {
38036         var index = this.view.getSelectedIndexes()[0];
38037         var r = this.store.getAt(index);
38038         if(r){
38039             this.onSelect(r, index);
38040         }
38041         if(doFocus !== false && !this.blockFocus){
38042             this.el.focus();
38043         }
38044     },
38045
38046     // private
38047     restrictHeight : function(){
38048         this.innerList.dom.style.height = '';
38049         var inner = this.innerList.dom;
38050         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38051         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38052         this.list.beginUpdate();
38053         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38054         this.list.alignTo(this.el, this.listAlign);
38055         this.list.endUpdate();
38056     },
38057
38058     // private
38059     onEmptyResults : function(){
38060         this.collapse();
38061     },
38062
38063     /**
38064      * Returns true if the dropdown list is expanded, else false.
38065      */
38066     isExpanded : function(){
38067         return this.list.isVisible();
38068     },
38069
38070     /**
38071      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38073      * @param {String} value The data value of the item to select
38074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38075      * selected item if it is not currently in view (defaults to true)
38076      * @return {Boolean} True if the value matched an item in the list, else false
38077      */
38078     selectByValue : function(v, scrollIntoView){
38079         if(v !== undefined && v !== null){
38080             var r = this.findRecord(this.valueField || this.displayField, v);
38081             if(r){
38082                 this.select(this.store.indexOf(r), scrollIntoView);
38083                 return true;
38084             }
38085         }
38086         return false;
38087     },
38088
38089     /**
38090      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38091      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38092      * @param {Number} index The zero-based index of the list item to select
38093      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38094      * selected item if it is not currently in view (defaults to true)
38095      */
38096     select : function(index, scrollIntoView){
38097         this.selectedIndex = index;
38098         this.view.select(index);
38099         if(scrollIntoView !== false){
38100             var el = this.view.getNode(index);
38101             if(el){
38102                 this.innerList.scrollChildIntoView(el, false);
38103             }
38104         }
38105     },
38106
38107     // private
38108     selectNext : function(){
38109         var ct = this.store.getCount();
38110         if(ct > 0){
38111             if(this.selectedIndex == -1){
38112                 this.select(0);
38113             }else if(this.selectedIndex < ct-1){
38114                 this.select(this.selectedIndex+1);
38115             }
38116         }
38117     },
38118
38119     // private
38120     selectPrev : function(){
38121         var ct = this.store.getCount();
38122         if(ct > 0){
38123             if(this.selectedIndex == -1){
38124                 this.select(0);
38125             }else if(this.selectedIndex != 0){
38126                 this.select(this.selectedIndex-1);
38127             }
38128         }
38129     },
38130
38131     // private
38132     onKeyUp : function(e){
38133         if(this.editable !== false && !e.isSpecialKey()){
38134             this.lastKey = e.getKey();
38135             this.dqTask.delay(this.queryDelay);
38136         }
38137     },
38138
38139     // private
38140     validateBlur : function(){
38141         return !this.list || !this.list.isVisible();   
38142     },
38143
38144     // private
38145     initQuery : function(){
38146         this.doQuery(this.getRawValue());
38147     },
38148
38149     // private
38150     doForce : function(){
38151         if(this.el.dom.value.length > 0){
38152             this.el.dom.value =
38153                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38154             this.applyEmptyText();
38155         }
38156     },
38157
38158     /**
38159      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38160      * query allowing the query action to be canceled if needed.
38161      * @param {String} query The SQL query to execute
38162      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38163      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38164      * saved in the current store (defaults to false)
38165      */
38166     doQuery : function(q, forceAll){
38167         if(q === undefined || q === null){
38168             q = '';
38169         }
38170         var qe = {
38171             query: q,
38172             forceAll: forceAll,
38173             combo: this,
38174             cancel:false
38175         };
38176         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38177             return false;
38178         }
38179         q = qe.query;
38180         forceAll = qe.forceAll;
38181         if(forceAll === true || (q.length >= this.minChars)){
38182             if(this.lastQuery != q || this.alwaysQuery){
38183                 this.lastQuery = q;
38184                 if(this.mode == 'local'){
38185                     this.selectedIndex = -1;
38186                     if(forceAll){
38187                         this.store.clearFilter();
38188                     }else{
38189                         this.store.filter(this.displayField, q);
38190                     }
38191                     this.onLoad();
38192                 }else{
38193                     this.store.baseParams[this.queryParam] = q;
38194                     this.store.load({
38195                         params: this.getParams(q)
38196                     });
38197                     this.expand();
38198                 }
38199             }else{
38200                 this.selectedIndex = -1;
38201                 this.onLoad();   
38202             }
38203         }
38204     },
38205
38206     // private
38207     getParams : function(q){
38208         var p = {};
38209         //p[this.queryParam] = q;
38210         if(this.pageSize){
38211             p.start = 0;
38212             p.limit = this.pageSize;
38213         }
38214         return p;
38215     },
38216
38217     /**
38218      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38219      */
38220     collapse : function(){
38221         if(!this.isExpanded()){
38222             return;
38223         }
38224         this.list.hide();
38225         Roo.get(document).un('mousedown', this.collapseIf, this);
38226         Roo.get(document).un('mousewheel', this.collapseIf, this);
38227         if (!this.editable) {
38228             Roo.get(document).un('keydown', this.listKeyPress, this);
38229         }
38230         this.fireEvent('collapse', this);
38231     },
38232
38233     // private
38234     collapseIf : function(e){
38235         if(!e.within(this.wrap) && !e.within(this.list)){
38236             this.collapse();
38237         }
38238     },
38239
38240     /**
38241      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38242      */
38243     expand : function(){
38244         if(this.isExpanded() || !this.hasFocus){
38245             return;
38246         }
38247         this.list.alignTo(this.el, this.listAlign);
38248         this.list.show();
38249         Roo.get(document).on('mousedown', this.collapseIf, this);
38250         Roo.get(document).on('mousewheel', this.collapseIf, this);
38251         if (!this.editable) {
38252             Roo.get(document).on('keydown', this.listKeyPress, this);
38253         }
38254         
38255         this.fireEvent('expand', this);
38256     },
38257
38258     // private
38259     // Implements the default empty TriggerField.onTriggerClick function
38260     onTriggerClick : function(){
38261         if(this.disabled){
38262             return;
38263         }
38264         if(this.isExpanded()){
38265             this.collapse();
38266             if (!this.blockFocus) {
38267                 this.el.focus();
38268             }
38269             
38270         }else {
38271             this.hasFocus = true;
38272             if(this.triggerAction == 'all') {
38273                 this.doQuery(this.allQuery, true);
38274             } else {
38275                 this.doQuery(this.getRawValue());
38276             }
38277             if (!this.blockFocus) {
38278                 this.el.focus();
38279             }
38280         }
38281     },
38282     listKeyPress : function(e)
38283     {
38284         //Roo.log('listkeypress');
38285         // scroll to first matching element based on key pres..
38286         if (e.isSpecialKey()) {
38287             return false;
38288         }
38289         var k = String.fromCharCode(e.getKey()).toUpperCase();
38290         //Roo.log(k);
38291         var match  = false;
38292         var csel = this.view.getSelectedNodes();
38293         var cselitem = false;
38294         if (csel.length) {
38295             var ix = this.view.indexOf(csel[0]);
38296             cselitem  = this.store.getAt(ix);
38297             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38298                 cselitem = false;
38299             }
38300             
38301         }
38302         
38303         this.store.each(function(v) { 
38304             if (cselitem) {
38305                 // start at existing selection.
38306                 if (cselitem.id == v.id) {
38307                     cselitem = false;
38308                 }
38309                 return;
38310             }
38311                 
38312             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38313                 match = this.store.indexOf(v);
38314                 return false;
38315             }
38316         }, this);
38317         
38318         if (match === false) {
38319             return true; // no more action?
38320         }
38321         // scroll to?
38322         this.view.select(match);
38323         var sn = Roo.get(this.view.getSelectedNodes()[0])
38324         sn.scrollIntoView(sn.dom.parentNode, false);
38325     }
38326
38327     /** 
38328     * @cfg {Boolean} grow 
38329     * @hide 
38330     */
38331     /** 
38332     * @cfg {Number} growMin 
38333     * @hide 
38334     */
38335     /** 
38336     * @cfg {Number} growMax 
38337     * @hide 
38338     */
38339     /**
38340      * @hide
38341      * @method autoSize
38342      */
38343 });/*
38344  * Based on:
38345  * Ext JS Library 1.1.1
38346  * Copyright(c) 2006-2007, Ext JS, LLC.
38347  *
38348  * Originally Released Under LGPL - original licence link has changed is not relivant.
38349  *
38350  * Fork - LGPL
38351  * <script type="text/javascript">
38352  */
38353 /**
38354  * @class Roo.form.Checkbox
38355  * @extends Roo.form.Field
38356  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38357  * @constructor
38358  * Creates a new Checkbox
38359  * @param {Object} config Configuration options
38360  */
38361 Roo.form.Checkbox = function(config){
38362     Roo.form.Checkbox.superclass.constructor.call(this, config);
38363     this.addEvents({
38364         /**
38365          * @event check
38366          * Fires when the checkbox is checked or unchecked.
38367              * @param {Roo.form.Checkbox} this This checkbox
38368              * @param {Boolean} checked The new checked value
38369              */
38370         check : true
38371     });
38372 };
38373
38374 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38375     /**
38376      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38377      */
38378     focusClass : undefined,
38379     /**
38380      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38381      */
38382     fieldClass: "x-form-field",
38383     /**
38384      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38385      */
38386     checked: false,
38387     /**
38388      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38389      * {tag: "input", type: "checkbox", autocomplete: "off"})
38390      */
38391     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38392     /**
38393      * @cfg {String} boxLabel The text that appears beside the checkbox
38394      */
38395     boxLabel : "",
38396     /**
38397      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38398      */  
38399     inputValue : '1',
38400     /**
38401      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38402      */
38403      valueOff: '0', // value when not checked..
38404
38405     actionMode : 'viewEl', 
38406     //
38407     // private
38408     itemCls : 'x-menu-check-item x-form-item',
38409     groupClass : 'x-menu-group-item',
38410     inputType : 'hidden',
38411     
38412     
38413     inSetChecked: false, // check that we are not calling self...
38414     
38415     inputElement: false, // real input element?
38416     basedOn: false, // ????
38417     
38418     isFormField: true, // not sure where this is needed!!!!
38419
38420     onResize : function(){
38421         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38422         if(!this.boxLabel){
38423             this.el.alignTo(this.wrap, 'c-c');
38424         }
38425     },
38426
38427     initEvents : function(){
38428         Roo.form.Checkbox.superclass.initEvents.call(this);
38429         this.el.on("click", this.onClick,  this);
38430         this.el.on("change", this.onClick,  this);
38431     },
38432
38433
38434     getResizeEl : function(){
38435         return this.wrap;
38436     },
38437
38438     getPositionEl : function(){
38439         return this.wrap;
38440     },
38441
38442     // private
38443     onRender : function(ct, position){
38444         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38445         /*
38446         if(this.inputValue !== undefined){
38447             this.el.dom.value = this.inputValue;
38448         }
38449         */
38450         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38451         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38452         var viewEl = this.wrap.createChild({ 
38453             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38454         this.viewEl = viewEl;   
38455         this.wrap.on('click', this.onClick,  this); 
38456         
38457         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38458         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38459         
38460         
38461         
38462         if(this.boxLabel){
38463             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38464         //    viewEl.on('click', this.onClick,  this); 
38465         }
38466         //if(this.checked){
38467             this.setChecked(this.checked);
38468         //}else{
38469             //this.checked = this.el.dom;
38470         //}
38471
38472     },
38473
38474     // private
38475     initValue : Roo.emptyFn,
38476
38477     /**
38478      * Returns the checked state of the checkbox.
38479      * @return {Boolean} True if checked, else false
38480      */
38481     getValue : function(){
38482         if(this.el){
38483             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38484         }
38485         return this.valueOff;
38486         
38487     },
38488
38489         // private
38490     onClick : function(){ 
38491         this.setChecked(!this.checked);
38492
38493         //if(this.el.dom.checked != this.checked){
38494         //    this.setValue(this.el.dom.checked);
38495        // }
38496     },
38497
38498     /**
38499      * Sets the checked state of the checkbox.
38500      * On is always based on a string comparison between inputValue and the param.
38501      * @param {Boolean/String} value - the value to set 
38502      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38503      */
38504     setValue : function(v,suppressEvent){
38505         
38506         
38507         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38508         //if(this.el && this.el.dom){
38509         //    this.el.dom.checked = this.checked;
38510         //    this.el.dom.defaultChecked = this.checked;
38511         //}
38512         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38513         //this.fireEvent("check", this, this.checked);
38514     },
38515     // private..
38516     setChecked : function(state,suppressEvent)
38517     {
38518         if (this.inSetChecked) {
38519             this.checked = state;
38520             return;
38521         }
38522         
38523     
38524         if(this.wrap){
38525             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38526         }
38527         this.checked = state;
38528         if(suppressEvent !== true){
38529             this.fireEvent('check', this, state);
38530         }
38531         this.inSetChecked = true;
38532         this.el.dom.value = state ? this.inputValue : this.valueOff;
38533         this.inSetChecked = false;
38534         
38535     },
38536     // handle setting of hidden value by some other method!!?!?
38537     setFromHidden: function()
38538     {
38539         if(!this.el){
38540             return;
38541         }
38542         //console.log("SET FROM HIDDEN");
38543         //alert('setFrom hidden');
38544         this.setValue(this.el.dom.value);
38545     },
38546     
38547     onDestroy : function()
38548     {
38549         if(this.viewEl){
38550             Roo.get(this.viewEl).remove();
38551         }
38552          
38553         Roo.form.Checkbox.superclass.onDestroy.call(this);
38554     }
38555
38556 });/*
38557  * Based on:
38558  * Ext JS Library 1.1.1
38559  * Copyright(c) 2006-2007, Ext JS, LLC.
38560  *
38561  * Originally Released Under LGPL - original licence link has changed is not relivant.
38562  *
38563  * Fork - LGPL
38564  * <script type="text/javascript">
38565  */
38566  
38567 /**
38568  * @class Roo.form.Radio
38569  * @extends Roo.form.Checkbox
38570  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38571  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38572  * @constructor
38573  * Creates a new Radio
38574  * @param {Object} config Configuration options
38575  */
38576 Roo.form.Radio = function(){
38577     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38578 };
38579 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38580     inputType: 'radio',
38581
38582     /**
38583      * If this radio is part of a group, it will return the selected value
38584      * @return {String}
38585      */
38586     getGroupValue : function(){
38587         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38588     }
38589 });//<script type="text/javascript">
38590
38591 /*
38592  * Ext JS Library 1.1.1
38593  * Copyright(c) 2006-2007, Ext JS, LLC.
38594  * licensing@extjs.com
38595  * 
38596  * http://www.extjs.com/license
38597  */
38598  
38599  /*
38600   * 
38601   * Known bugs:
38602   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38603   * - IE ? - no idea how much works there.
38604   * 
38605   * 
38606   * 
38607   */
38608  
38609
38610 /**
38611  * @class Ext.form.HtmlEditor
38612  * @extends Ext.form.Field
38613  * Provides a lightweight HTML Editor component.
38614  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38615  * 
38616  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38617  * supported by this editor.</b><br/><br/>
38618  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38619  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38620  */
38621 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38622       /**
38623      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38624      */
38625     toolbars : false,
38626     /**
38627      * @cfg {String} createLinkText The default text for the create link prompt
38628      */
38629     createLinkText : 'Please enter the URL for the link:',
38630     /**
38631      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38632      */
38633     defaultLinkValue : 'http:/'+'/',
38634    
38635      /**
38636      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38637      *                        Roo.resizable.
38638      */
38639     resizable : false,
38640      /**
38641      * @cfg {Number} height (in pixels)
38642      */   
38643     height: 300,
38644    /**
38645      * @cfg {Number} width (in pixels)
38646      */   
38647     width: 500,
38648     
38649     /**
38650      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38651      * 
38652      */
38653     stylesheets: false,
38654     
38655     // id of frame..
38656     frameId: false,
38657     
38658     // private properties
38659     validationEvent : false,
38660     deferHeight: true,
38661     initialized : false,
38662     activated : false,
38663     sourceEditMode : false,
38664     onFocus : Roo.emptyFn,
38665     iframePad:3,
38666     hideMode:'offsets',
38667     
38668     defaultAutoCreate : { // modified by initCompnoent..
38669         tag: "textarea",
38670         style:"width:500px;height:300px;",
38671         autocomplete: "off"
38672     },
38673
38674     // private
38675     initComponent : function(){
38676         this.addEvents({
38677             /**
38678              * @event initialize
38679              * Fires when the editor is fully initialized (including the iframe)
38680              * @param {HtmlEditor} this
38681              */
38682             initialize: true,
38683             /**
38684              * @event activate
38685              * Fires when the editor is first receives the focus. Any insertion must wait
38686              * until after this event.
38687              * @param {HtmlEditor} this
38688              */
38689             activate: true,
38690              /**
38691              * @event beforesync
38692              * Fires before the textarea is updated with content from the editor iframe. Return false
38693              * to cancel the sync.
38694              * @param {HtmlEditor} this
38695              * @param {String} html
38696              */
38697             beforesync: true,
38698              /**
38699              * @event beforepush
38700              * Fires before the iframe editor is updated with content from the textarea. Return false
38701              * to cancel the push.
38702              * @param {HtmlEditor} this
38703              * @param {String} html
38704              */
38705             beforepush: true,
38706              /**
38707              * @event sync
38708              * Fires when the textarea is updated with content from the editor iframe.
38709              * @param {HtmlEditor} this
38710              * @param {String} html
38711              */
38712             sync: true,
38713              /**
38714              * @event push
38715              * Fires when the iframe editor is updated with content from the textarea.
38716              * @param {HtmlEditor} this
38717              * @param {String} html
38718              */
38719             push: true,
38720              /**
38721              * @event editmodechange
38722              * Fires when the editor switches edit modes
38723              * @param {HtmlEditor} this
38724              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38725              */
38726             editmodechange: true,
38727             /**
38728              * @event editorevent
38729              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38730              * @param {HtmlEditor} this
38731              */
38732             editorevent: true
38733         });
38734         this.defaultAutoCreate =  {
38735             tag: "textarea",
38736             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38737             autocomplete: "off"
38738         };
38739     },
38740
38741     /**
38742      * Protected method that will not generally be called directly. It
38743      * is called when the editor creates its toolbar. Override this method if you need to
38744      * add custom toolbar buttons.
38745      * @param {HtmlEditor} editor
38746      */
38747     createToolbar : function(editor){
38748         if (!editor.toolbars || !editor.toolbars.length) {
38749             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38750         }
38751         
38752         for (var i =0 ; i < editor.toolbars.length;i++) {
38753             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38754             editor.toolbars[i].init(editor);
38755         }
38756          
38757         
38758     },
38759
38760     /**
38761      * Protected method that will not generally be called directly. It
38762      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38763      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38764      */
38765     getDocMarkup : function(){
38766         // body styles..
38767         var st = '';
38768         if (this.stylesheets === false) {
38769             
38770             Roo.get(document.head).select('style').each(function(node) {
38771                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38772             });
38773             
38774             Roo.get(document.head).select('link').each(function(node) { 
38775                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38776             });
38777             
38778         } else if (!this.stylesheets.length) {
38779                 // simple..
38780                 st = '<style type="text/css">' +
38781                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38782                    '</style>';
38783         } else {
38784             Roo.each(this.stylesheets, function(s) {
38785                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38786             });
38787             
38788         }
38789         
38790         return '<html><head>' + st  +
38791             //<style type="text/css">' +
38792             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38793             //'</style>' +
38794             ' </head><body></body></html>';
38795     },
38796
38797     // private
38798     onRender : function(ct, position)
38799     {
38800         var _t = this;
38801         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38802         this.el.dom.style.border = '0 none';
38803         this.el.dom.setAttribute('tabIndex', -1);
38804         this.el.addClass('x-hidden');
38805         if(Roo.isIE){ // fix IE 1px bogus margin
38806             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38807         }
38808         this.wrap = this.el.wrap({
38809             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38810         });
38811         
38812         if (this.resizable) {
38813             this.resizeEl = new Roo.Resizable(this.wrap, {
38814                 pinned : true,
38815                 wrap: true,
38816                 dynamic : true,
38817                 minHeight : this.height,
38818                 height: this.height,
38819                 handles : this.resizable,
38820                 width: this.width,
38821                 listeners : {
38822                     resize : function(r, w, h) {
38823                         _t.onResize(w,h); // -something
38824                     }
38825                 }
38826             });
38827             
38828         }
38829
38830         this.frameId = Roo.id();
38831         
38832         this.createToolbar(this);
38833         
38834       
38835         
38836         var iframe = this.wrap.createChild({
38837             tag: 'iframe',
38838             id: this.frameId,
38839             name: this.frameId,
38840             frameBorder : 'no',
38841             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38842         }, this.el
38843         );
38844         
38845        // console.log(iframe);
38846         //this.wrap.dom.appendChild(iframe);
38847
38848         this.iframe = iframe.dom;
38849
38850          this.assignDocWin();
38851         
38852         this.doc.designMode = 'on';
38853        
38854         this.doc.open();
38855         this.doc.write(this.getDocMarkup());
38856         this.doc.close();
38857
38858         
38859         var task = { // must defer to wait for browser to be ready
38860             run : function(){
38861                 //console.log("run task?" + this.doc.readyState);
38862                 this.assignDocWin();
38863                 if(this.doc.body || this.doc.readyState == 'complete'){
38864                     try {
38865                         this.doc.designMode="on";
38866                     } catch (e) {
38867                         return;
38868                     }
38869                     Roo.TaskMgr.stop(task);
38870                     this.initEditor.defer(10, this);
38871                 }
38872             },
38873             interval : 10,
38874             duration:10000,
38875             scope: this
38876         };
38877         Roo.TaskMgr.start(task);
38878
38879         if(!this.width){
38880             this.setSize(this.wrap.getSize());
38881         }
38882         if (this.resizeEl) {
38883             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38884             // should trigger onReize..
38885         }
38886     },
38887
38888     // private
38889     onResize : function(w, h)
38890     {
38891         //Roo.log('resize: ' +w + ',' + h );
38892         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38893         if(this.el && this.iframe){
38894             if(typeof w == 'number'){
38895                 var aw = w - this.wrap.getFrameWidth('lr');
38896                 this.el.setWidth(this.adjustWidth('textarea', aw));
38897                 this.iframe.style.width = aw + 'px';
38898             }
38899             if(typeof h == 'number'){
38900                 var tbh = 0;
38901                 for (var i =0; i < this.toolbars.length;i++) {
38902                     // fixme - ask toolbars for heights?
38903                     tbh += this.toolbars[i].tb.el.getHeight();
38904                     if (this.toolbars[i].footer) {
38905                         tbh += this.toolbars[i].footer.el.getHeight();
38906                     }
38907                 }
38908                 
38909                 
38910                 
38911                 
38912                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38913                 ah -= 5; // knock a few pixes off for look..
38914                 this.el.setHeight(this.adjustWidth('textarea', ah));
38915                 this.iframe.style.height = ah + 'px';
38916                 if(this.doc){
38917                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38918                 }
38919             }
38920         }
38921     },
38922
38923     /**
38924      * Toggles the editor between standard and source edit mode.
38925      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38926      */
38927     toggleSourceEdit : function(sourceEditMode){
38928         
38929         this.sourceEditMode = sourceEditMode === true;
38930         
38931         if(this.sourceEditMode){
38932           
38933             this.syncValue();
38934             this.iframe.className = 'x-hidden';
38935             this.el.removeClass('x-hidden');
38936             this.el.dom.removeAttribute('tabIndex');
38937             this.el.focus();
38938         }else{
38939              
38940             this.pushValue();
38941             this.iframe.className = '';
38942             this.el.addClass('x-hidden');
38943             this.el.dom.setAttribute('tabIndex', -1);
38944             this.deferFocus();
38945         }
38946         this.setSize(this.wrap.getSize());
38947         this.fireEvent('editmodechange', this, this.sourceEditMode);
38948     },
38949
38950     // private used internally
38951     createLink : function(){
38952         var url = prompt(this.createLinkText, this.defaultLinkValue);
38953         if(url && url != 'http:/'+'/'){
38954             this.relayCmd('createlink', url);
38955         }
38956     },
38957
38958     // private (for BoxComponent)
38959     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38960
38961     // private (for BoxComponent)
38962     getResizeEl : function(){
38963         return this.wrap;
38964     },
38965
38966     // private (for BoxComponent)
38967     getPositionEl : function(){
38968         return this.wrap;
38969     },
38970
38971     // private
38972     initEvents : function(){
38973         this.originalValue = this.getValue();
38974     },
38975
38976     /**
38977      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38978      * @method
38979      */
38980     markInvalid : Roo.emptyFn,
38981     /**
38982      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38983      * @method
38984      */
38985     clearInvalid : Roo.emptyFn,
38986
38987     setValue : function(v){
38988         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38989         this.pushValue();
38990     },
38991
38992     /**
38993      * Protected method that will not generally be called directly. If you need/want
38994      * custom HTML cleanup, this is the method you should override.
38995      * @param {String} html The HTML to be cleaned
38996      * return {String} The cleaned HTML
38997      */
38998     cleanHtml : function(html){
38999         html = String(html);
39000         if(html.length > 5){
39001             if(Roo.isSafari){ // strip safari nonsense
39002                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39003             }
39004         }
39005         if(html == '&nbsp;'){
39006             html = '';
39007         }
39008         return html;
39009     },
39010
39011     /**
39012      * Protected method that will not generally be called directly. Syncs the contents
39013      * of the editor iframe with the textarea.
39014      */
39015     syncValue : function(){
39016         if(this.initialized){
39017             var bd = (this.doc.body || this.doc.documentElement);
39018             //this.cleanUpPaste();
39019             var html = bd.innerHTML;
39020             if(Roo.isSafari){
39021                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39022                 var m = bs.match(/text-align:(.*?);/i);
39023                 if(m && m[1]){
39024                     html = '<div style="'+m[0]+'">' + html + '</div>';
39025                 }
39026             }
39027             html = this.cleanHtml(html);
39028             // fix up the special chars..
39029             html.replace(/([\x80-\uffff])/g, function (a, b) {
39030                 return "&#"+b.charCodeAt()+";" 
39031             });
39032             if(this.fireEvent('beforesync', this, html) !== false){
39033                 this.el.dom.value = html;
39034                 this.fireEvent('sync', this, html);
39035             }
39036         }
39037     },
39038
39039     /**
39040      * Protected method that will not generally be called directly. Pushes the value of the textarea
39041      * into the iframe editor.
39042      */
39043     pushValue : function(){
39044         if(this.initialized){
39045             var v = this.el.dom.value;
39046             if(v.length < 1){
39047                 v = '&#160;';
39048             }
39049             
39050             if(this.fireEvent('beforepush', this, v) !== false){
39051                 var d = (this.doc.body || this.doc.documentElement);
39052                 d.innerHTML = v;
39053                 this.cleanUpPaste();
39054                 this.el.dom.value = d.innerHTML;
39055                 this.fireEvent('push', this, v);
39056             }
39057         }
39058     },
39059
39060     // private
39061     deferFocus : function(){
39062         this.focus.defer(10, this);
39063     },
39064
39065     // doc'ed in Field
39066     focus : function(){
39067         if(this.win && !this.sourceEditMode){
39068             this.win.focus();
39069         }else{
39070             this.el.focus();
39071         }
39072     },
39073     
39074     assignDocWin: function()
39075     {
39076         var iframe = this.iframe;
39077         
39078          if(Roo.isIE){
39079             this.doc = iframe.contentWindow.document;
39080             this.win = iframe.contentWindow;
39081         } else {
39082             if (!Roo.get(this.frameId)) {
39083                 return;
39084             }
39085             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39086             this.win = Roo.get(this.frameId).dom.contentWindow;
39087         }
39088     },
39089     
39090     // private
39091     initEditor : function(){
39092         //console.log("INIT EDITOR");
39093         this.assignDocWin();
39094         
39095         
39096         
39097         this.doc.designMode="on";
39098         this.doc.open();
39099         this.doc.write(this.getDocMarkup());
39100         this.doc.close();
39101         
39102         var dbody = (this.doc.body || this.doc.documentElement);
39103         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39104         // this copies styles from the containing element into thsi one..
39105         // not sure why we need all of this..
39106         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39107         ss['background-attachment'] = 'fixed'; // w3c
39108         dbody.bgProperties = 'fixed'; // ie
39109         Roo.DomHelper.applyStyles(dbody, ss);
39110         Roo.EventManager.on(this.doc, {
39111             //'mousedown': this.onEditorEvent,
39112             'mouseup': this.onEditorEvent,
39113             'dblclick': this.onEditorEvent,
39114             'click': this.onEditorEvent,
39115             'keyup': this.onEditorEvent,
39116             buffer:100,
39117             scope: this
39118         });
39119         if(Roo.isGecko){
39120             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39121         }
39122         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39123             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39124         }
39125         this.initialized = true;
39126
39127         this.fireEvent('initialize', this);
39128         this.pushValue();
39129     },
39130
39131     // private
39132     onDestroy : function(){
39133         
39134         
39135         
39136         if(this.rendered){
39137             
39138             for (var i =0; i < this.toolbars.length;i++) {
39139                 // fixme - ask toolbars for heights?
39140                 this.toolbars[i].onDestroy();
39141             }
39142             
39143             this.wrap.dom.innerHTML = '';
39144             this.wrap.remove();
39145         }
39146     },
39147
39148     // private
39149     onFirstFocus : function(){
39150         
39151         this.assignDocWin();
39152         
39153         
39154         this.activated = true;
39155         for (var i =0; i < this.toolbars.length;i++) {
39156             this.toolbars[i].onFirstFocus();
39157         }
39158        
39159         if(Roo.isGecko){ // prevent silly gecko errors
39160             this.win.focus();
39161             var s = this.win.getSelection();
39162             if(!s.focusNode || s.focusNode.nodeType != 3){
39163                 var r = s.getRangeAt(0);
39164                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39165                 r.collapse(true);
39166                 this.deferFocus();
39167             }
39168             try{
39169                 this.execCmd('useCSS', true);
39170                 this.execCmd('styleWithCSS', false);
39171             }catch(e){}
39172         }
39173         this.fireEvent('activate', this);
39174     },
39175
39176     // private
39177     adjustFont: function(btn){
39178         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39179         //if(Roo.isSafari){ // safari
39180         //    adjust *= 2;
39181        // }
39182         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39183         if(Roo.isSafari){ // safari
39184             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39185             v =  (v < 10) ? 10 : v;
39186             v =  (v > 48) ? 48 : v;
39187             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39188             
39189         }
39190         
39191         
39192         v = Math.max(1, v+adjust);
39193         
39194         this.execCmd('FontSize', v  );
39195     },
39196
39197     onEditorEvent : function(e){
39198         this.fireEvent('editorevent', this, e);
39199       //  this.updateToolbar();
39200         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39201     },
39202
39203     insertTag : function(tg)
39204     {
39205         // could be a bit smarter... -> wrap the current selected tRoo..
39206         
39207         this.execCmd("formatblock",   tg);
39208         
39209     },
39210     
39211     insertText : function(txt)
39212     {
39213         
39214         
39215         range = this.createRange();
39216         range.deleteContents();
39217                //alert(Sender.getAttribute('label'));
39218                
39219         range.insertNode(this.doc.createTextNode(txt));
39220     } ,
39221     
39222     // private
39223     relayBtnCmd : function(btn){
39224         this.relayCmd(btn.cmd);
39225     },
39226
39227     /**
39228      * Executes a Midas editor command on the editor document and performs necessary focus and
39229      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39230      * @param {String} cmd The Midas command
39231      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39232      */
39233     relayCmd : function(cmd, value){
39234         this.win.focus();
39235         this.execCmd(cmd, value);
39236         this.fireEvent('editorevent', this);
39237         //this.updateToolbar();
39238         this.deferFocus();
39239     },
39240
39241     /**
39242      * Executes a Midas editor command directly on the editor document.
39243      * For visual commands, you should use {@link #relayCmd} instead.
39244      * <b>This should only be called after the editor is initialized.</b>
39245      * @param {String} cmd The Midas command
39246      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39247      */
39248     execCmd : function(cmd, value){
39249         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39250         this.syncValue();
39251     },
39252
39253    
39254     /**
39255      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39256      * to insert tRoo.
39257      * @param {String} text | dom node.. 
39258      */
39259     insertAtCursor : function(text)
39260     {
39261         
39262         
39263         
39264         if(!this.activated){
39265             return;
39266         }
39267         /*
39268         if(Roo.isIE){
39269             this.win.focus();
39270             var r = this.doc.selection.createRange();
39271             if(r){
39272                 r.collapse(true);
39273                 r.pasteHTML(text);
39274                 this.syncValue();
39275                 this.deferFocus();
39276             
39277             }
39278             return;
39279         }
39280         */
39281         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39282             this.win.focus();
39283             
39284             
39285             // from jquery ui (MIT licenced)
39286             var range, node;
39287             var win = this.win;
39288             
39289             if (win.getSelection && win.getSelection().getRangeAt) {
39290                 range = win.getSelection().getRangeAt(0);
39291                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39292                 range.insertNode(node);
39293             } else if (win.document.selection && win.document.selection.createRange) {
39294                 // no firefox support
39295                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39296                 win.document.selection.createRange().pasteHTML(txt);
39297             } else {
39298                 // no firefox support
39299                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39300                 this.execCmd('InsertHTML', txt);
39301             } 
39302             
39303             this.syncValue();
39304             
39305             this.deferFocus();
39306         }
39307     },
39308  // private
39309     mozKeyPress : function(e){
39310         if(e.ctrlKey){
39311             var c = e.getCharCode(), cmd;
39312           
39313             if(c > 0){
39314                 c = String.fromCharCode(c).toLowerCase();
39315                 switch(c){
39316                     case 'b':
39317                         cmd = 'bold';
39318                     break;
39319                     case 'i':
39320                         cmd = 'italic';
39321                     break;
39322                     case 'u':
39323                         cmd = 'underline';
39324                         break;
39325                     case 'v':
39326                         this.cleanUpPaste.defer(100, this);
39327                         return;
39328                     break;
39329                 }
39330                 if(cmd){
39331                     this.win.focus();
39332                     this.execCmd(cmd);
39333                     this.deferFocus();
39334                     e.preventDefault();
39335                 }
39336                 
39337             }
39338         }
39339     },
39340
39341     // private
39342     fixKeys : function(){ // load time branching for fastest keydown performance
39343         if(Roo.isIE){
39344             return function(e){
39345                 var k = e.getKey(), r;
39346                 if(k == e.TAB){
39347                     e.stopEvent();
39348                     r = this.doc.selection.createRange();
39349                     if(r){
39350                         r.collapse(true);
39351                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39352                         this.deferFocus();
39353                     }
39354                     return;
39355                 }
39356                 
39357                 if(k == e.ENTER){
39358                     r = this.doc.selection.createRange();
39359                     if(r){
39360                         var target = r.parentElement();
39361                         if(!target || target.tagName.toLowerCase() != 'li'){
39362                             e.stopEvent();
39363                             r.pasteHTML('<br />');
39364                             r.collapse(false);
39365                             r.select();
39366                         }
39367                     }
39368                 }
39369                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39370                     this.cleanUpPaste.defer(100, this);
39371                     return;
39372                 }
39373                 
39374                 
39375             };
39376         }else if(Roo.isOpera){
39377             return function(e){
39378                 var k = e.getKey();
39379                 if(k == e.TAB){
39380                     e.stopEvent();
39381                     this.win.focus();
39382                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39383                     this.deferFocus();
39384                 }
39385                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39386                     this.cleanUpPaste.defer(100, this);
39387                     return;
39388                 }
39389                 
39390             };
39391         }else if(Roo.isSafari){
39392             return function(e){
39393                 var k = e.getKey();
39394                 
39395                 if(k == e.TAB){
39396                     e.stopEvent();
39397                     this.execCmd('InsertText','\t');
39398                     this.deferFocus();
39399                     return;
39400                 }
39401                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39402                     this.cleanUpPaste.defer(100, this);
39403                     return;
39404                 }
39405                 
39406              };
39407         }
39408     }(),
39409     
39410     getAllAncestors: function()
39411     {
39412         var p = this.getSelectedNode();
39413         var a = [];
39414         if (!p) {
39415             a.push(p); // push blank onto stack..
39416             p = this.getParentElement();
39417         }
39418         
39419         
39420         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39421             a.push(p);
39422             p = p.parentNode;
39423         }
39424         a.push(this.doc.body);
39425         return a;
39426     },
39427     lastSel : false,
39428     lastSelNode : false,
39429     
39430     
39431     getSelection : function() 
39432     {
39433         this.assignDocWin();
39434         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39435     },
39436     
39437     getSelectedNode: function() 
39438     {
39439         // this may only work on Gecko!!!
39440         
39441         // should we cache this!!!!
39442         
39443         
39444         
39445          
39446         var range = this.createRange(this.getSelection()).cloneRange();
39447         
39448         if (Roo.isIE) {
39449             var parent = range.parentElement();
39450             while (true) {
39451                 var testRange = range.duplicate();
39452                 testRange.moveToElementText(parent);
39453                 if (testRange.inRange(range)) {
39454                     break;
39455                 }
39456                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39457                     break;
39458                 }
39459                 parent = parent.parentElement;
39460             }
39461             return parent;
39462         }
39463         
39464         // is ancestor a text element.
39465         var ac =  range.commonAncestorContainer;
39466         if (ac.nodeType == 3) {
39467             ac = ac.parentNode;
39468         }
39469         
39470         var ar = ac.childNodes;
39471          
39472         var nodes = [];
39473         var other_nodes = [];
39474         var has_other_nodes = false;
39475         for (var i=0;i<ar.length;i++) {
39476             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39477                 continue;
39478             }
39479             // fullly contained node.
39480             
39481             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39482                 nodes.push(ar[i]);
39483                 continue;
39484             }
39485             
39486             // probably selected..
39487             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39488                 other_nodes.push(ar[i]);
39489                 continue;
39490             }
39491             // outer..
39492             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39493                 continue;
39494             }
39495             
39496             
39497             has_other_nodes = true;
39498         }
39499         if (!nodes.length && other_nodes.length) {
39500             nodes= other_nodes;
39501         }
39502         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39503             return false;
39504         }
39505         
39506         return nodes[0];
39507     },
39508     createRange: function(sel)
39509     {
39510         // this has strange effects when using with 
39511         // top toolbar - not sure if it's a great idea.
39512         //this.editor.contentWindow.focus();
39513         if (typeof sel != "undefined") {
39514             try {
39515                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39516             } catch(e) {
39517                 return this.doc.createRange();
39518             }
39519         } else {
39520             return this.doc.createRange();
39521         }
39522     },
39523     getParentElement: function()
39524     {
39525         
39526         this.assignDocWin();
39527         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39528         
39529         var range = this.createRange(sel);
39530          
39531         try {
39532             var p = range.commonAncestorContainer;
39533             while (p.nodeType == 3) { // text node
39534                 p = p.parentNode;
39535             }
39536             return p;
39537         } catch (e) {
39538             return null;
39539         }
39540     
39541     },
39542     /***
39543      *
39544      * Range intersection.. the hard stuff...
39545      *  '-1' = before
39546      *  '0' = hits..
39547      *  '1' = after.
39548      *         [ -- selected range --- ]
39549      *   [fail]                        [fail]
39550      *
39551      *    basically..
39552      *      if end is before start or  hits it. fail.
39553      *      if start is after end or hits it fail.
39554      *
39555      *   if either hits (but other is outside. - then it's not 
39556      *   
39557      *    
39558      **/
39559     
39560     
39561     // @see http://www.thismuchiknow.co.uk/?p=64.
39562     rangeIntersectsNode : function(range, node)
39563     {
39564         var nodeRange = node.ownerDocument.createRange();
39565         try {
39566             nodeRange.selectNode(node);
39567         } catch (e) {
39568             nodeRange.selectNodeContents(node);
39569         }
39570     
39571         var rangeStartRange = range.cloneRange();
39572         rangeStartRange.collapse(true);
39573     
39574         var rangeEndRange = range.cloneRange();
39575         rangeEndRange.collapse(false);
39576     
39577         var nodeStartRange = nodeRange.cloneRange();
39578         nodeStartRange.collapse(true);
39579     
39580         var nodeEndRange = nodeRange.cloneRange();
39581         nodeEndRange.collapse(false);
39582     
39583         return rangeStartRange.compareBoundaryPoints(
39584                  Range.START_TO_START, nodeEndRange) == -1 &&
39585                rangeEndRange.compareBoundaryPoints(
39586                  Range.START_TO_START, nodeStartRange) == 1;
39587         
39588          
39589     },
39590     rangeCompareNode : function(range, node)
39591     {
39592         var nodeRange = node.ownerDocument.createRange();
39593         try {
39594             nodeRange.selectNode(node);
39595         } catch (e) {
39596             nodeRange.selectNodeContents(node);
39597         }
39598         
39599         
39600         range.collapse(true);
39601     
39602         nodeRange.collapse(true);
39603      
39604         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39605         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39606          
39607         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39608         
39609         var nodeIsBefore   =  ss == 1;
39610         var nodeIsAfter    = ee == -1;
39611         
39612         if (nodeIsBefore && nodeIsAfter)
39613             return 0; // outer
39614         if (!nodeIsBefore && nodeIsAfter)
39615             return 1; //right trailed.
39616         
39617         if (nodeIsBefore && !nodeIsAfter)
39618             return 2;  // left trailed.
39619         // fully contined.
39620         return 3;
39621     },
39622
39623     // private? - in a new class?
39624     cleanUpPaste :  function()
39625     {
39626         // cleans up the whole document..
39627          Roo.log('cleanuppaste');
39628         this.cleanUpChildren(this.doc.body);
39629         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39630         if (clean != this.doc.body.innerHTML) {
39631             this.doc.body.innerHTML = clean;
39632         }
39633         
39634     },
39635     
39636     cleanWordChars : function(input) {
39637         var he = Roo.form.HtmlEditor;
39638     
39639         var output = input;
39640         Roo.each(he.swapCodes, function(sw) { 
39641         
39642             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39643             output = output.replace(swapper, sw[1]);
39644         });
39645         return output;
39646     },
39647     
39648     
39649     cleanUpChildren : function (n)
39650     {
39651         if (!n.childNodes.length) {
39652             return;
39653         }
39654         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39655            this.cleanUpChild(n.childNodes[i]);
39656         }
39657     },
39658     
39659     
39660         
39661     
39662     cleanUpChild : function (node)
39663     {
39664         //console.log(node);
39665         if (node.nodeName == "#text") {
39666             // clean up silly Windows -- stuff?
39667             return; 
39668         }
39669         if (node.nodeName == "#comment") {
39670             node.parentNode.removeChild(node);
39671             // clean up silly Windows -- stuff?
39672             return; 
39673         }
39674         
39675         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39676             // remove node.
39677             node.parentNode.removeChild(node);
39678             return;
39679             
39680         }
39681         
39682         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39683         
39684         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39685         
39686         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39687             remove_keep_children = true;
39688         }
39689         
39690         if (remove_keep_children) {
39691             this.cleanUpChildren(node);
39692             // inserts everything just before this node...
39693             while (node.childNodes.length) {
39694                 var cn = node.childNodes[0];
39695                 node.removeChild(cn);
39696                 node.parentNode.insertBefore(cn, node);
39697             }
39698             node.parentNode.removeChild(node);
39699             return;
39700         }
39701         
39702         if (!node.attributes || !node.attributes.length) {
39703             this.cleanUpChildren(node);
39704             return;
39705         }
39706         
39707         function cleanAttr(n,v)
39708         {
39709             
39710             if (v.match(/^\./) || v.match(/^\//)) {
39711                 return;
39712             }
39713             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39714                 return;
39715             }
39716             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39717             node.removeAttribute(n);
39718             
39719         }
39720         
39721         function cleanStyle(n,v)
39722         {
39723             if (v.match(/expression/)) { //XSS?? should we even bother..
39724                 node.removeAttribute(n);
39725                 return;
39726             }
39727             
39728             
39729             var parts = v.split(/;/);
39730             Roo.each(parts, function(p) {
39731                 p = p.replace(/\s+/g,'');
39732                 if (!p.length) {
39733                     return true;
39734                 }
39735                 var l = p.split(':').shift().replace(/\s+/g,'');
39736                 
39737                 // only allow 'c whitelisted system attributes'
39738                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39739                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39740                     node.removeAttribute(n);
39741                     return false;
39742                 }
39743                 return true;
39744             });
39745             
39746             
39747         }
39748         
39749         
39750         for (var i = node.attributes.length-1; i > -1 ; i--) {
39751             var a = node.attributes[i];
39752             //console.log(a);
39753             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39754                 node.removeAttribute(a.name);
39755                 return;
39756             }
39757             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39758                 cleanAttr(a.name,a.value); // fixme..
39759                 return;
39760             }
39761             if (a.name == 'style') {
39762                 cleanStyle(a.name,a.value);
39763             }
39764             /// clean up MS crap..
39765             // tecnically this should be a list of valid class'es..
39766             
39767             
39768             if (a.name == 'class') {
39769                 if (a.value.match(/^Mso/)) {
39770                     node.className = '';
39771                 }
39772                 
39773                 if (a.value.match(/body/)) {
39774                     node.className = '';
39775                 }
39776             }
39777             
39778             // style cleanup!?
39779             // class cleanup?
39780             
39781         }
39782         
39783         
39784         this.cleanUpChildren(node);
39785         
39786         
39787     }
39788     
39789     
39790     // hide stuff that is not compatible
39791     /**
39792      * @event blur
39793      * @hide
39794      */
39795     /**
39796      * @event change
39797      * @hide
39798      */
39799     /**
39800      * @event focus
39801      * @hide
39802      */
39803     /**
39804      * @event specialkey
39805      * @hide
39806      */
39807     /**
39808      * @cfg {String} fieldClass @hide
39809      */
39810     /**
39811      * @cfg {String} focusClass @hide
39812      */
39813     /**
39814      * @cfg {String} autoCreate @hide
39815      */
39816     /**
39817      * @cfg {String} inputType @hide
39818      */
39819     /**
39820      * @cfg {String} invalidClass @hide
39821      */
39822     /**
39823      * @cfg {String} invalidText @hide
39824      */
39825     /**
39826      * @cfg {String} msgFx @hide
39827      */
39828     /**
39829      * @cfg {String} validateOnBlur @hide
39830      */
39831 });
39832
39833 Roo.form.HtmlEditor.white = [
39834         'area', 'br', 'img', 'input', 'hr', 'wbr',
39835         
39836        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39837        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39838        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39839        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39840        'table',   'ul',         'xmp', 
39841        
39842        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39843       'thead',   'tr', 
39844      
39845       'dir', 'menu', 'ol', 'ul', 'dl',
39846        
39847       'embed',  'object'
39848 ];
39849
39850
39851 Roo.form.HtmlEditor.black = [
39852     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39853         'applet', // 
39854         'base',   'basefont', 'bgsound', 'blink',  'body', 
39855         'frame',  'frameset', 'head',    'html',   'ilayer', 
39856         'iframe', 'layer',  'link',     'meta',    'object',   
39857         'script', 'style' ,'title',  'xml' // clean later..
39858 ];
39859 Roo.form.HtmlEditor.clean = [
39860     'script', 'style', 'title', 'xml'
39861 ];
39862 Roo.form.HtmlEditor.remove = [
39863     'font'
39864 ];
39865 // attributes..
39866
39867 Roo.form.HtmlEditor.ablack = [
39868     'on'
39869 ];
39870     
39871 Roo.form.HtmlEditor.aclean = [ 
39872     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39873 ];
39874
39875 // protocols..
39876 Roo.form.HtmlEditor.pwhite= [
39877         'http',  'https',  'mailto'
39878 ];
39879
39880 // white listed style attributes.
39881 Roo.form.HtmlEditor.cwhite= [
39882         'text-align',
39883         'font-size'
39884 ];
39885
39886
39887 Roo.form.HtmlEditor.swapCodes   =[ 
39888     [    8211, "--" ], 
39889     [    8212, "--" ], 
39890     [    8216,  "'" ],  
39891     [    8217, "'" ],  
39892     [    8220, '"' ],  
39893     [    8221, '"' ],  
39894     [    8226, "*" ],  
39895     [    8230, "..." ]
39896 ]; 
39897
39898     // <script type="text/javascript">
39899 /*
39900  * Based on
39901  * Ext JS Library 1.1.1
39902  * Copyright(c) 2006-2007, Ext JS, LLC.
39903  *  
39904  
39905  */
39906
39907 /**
39908  * @class Roo.form.HtmlEditorToolbar1
39909  * Basic Toolbar
39910  * 
39911  * Usage:
39912  *
39913  new Roo.form.HtmlEditor({
39914     ....
39915     toolbars : [
39916         new Roo.form.HtmlEditorToolbar1({
39917             disable : { fonts: 1 , format: 1, ..., ... , ...],
39918             btns : [ .... ]
39919         })
39920     }
39921      
39922  * 
39923  * @cfg {Object} disable List of elements to disable..
39924  * @cfg {Array} btns List of additional buttons.
39925  * 
39926  * 
39927  * NEEDS Extra CSS? 
39928  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39929  */
39930  
39931 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39932 {
39933     
39934     Roo.apply(this, config);
39935     
39936     // default disabled, based on 'good practice'..
39937     this.disable = this.disable || {};
39938     Roo.applyIf(this.disable, {
39939         fontSize : true,
39940         colors : true,
39941         specialElements : true
39942     });
39943     
39944     
39945     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39946     // dont call parent... till later.
39947 }
39948
39949 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39950     
39951     tb: false,
39952     
39953     rendered: false,
39954     
39955     editor : false,
39956     /**
39957      * @cfg {Object} disable  List of toolbar elements to disable
39958          
39959      */
39960     disable : false,
39961       /**
39962      * @cfg {Array} fontFamilies An array of available font families
39963      */
39964     fontFamilies : [
39965         'Arial',
39966         'Courier New',
39967         'Tahoma',
39968         'Times New Roman',
39969         'Verdana'
39970     ],
39971     
39972     specialChars : [
39973            "&#169;",
39974           "&#174;",     
39975           "&#8482;",    
39976           "&#163;" ,    
39977          // "&#8212;",    
39978           "&#8230;",    
39979           "&#247;" ,    
39980         //  "&#225;" ,     ?? a acute?
39981            "&#8364;"    , //Euro
39982        //   "&#8220;"    ,
39983         //  "&#8221;"    ,
39984         //  "&#8226;"    ,
39985           "&#176;"  //   , // degrees
39986
39987          // "&#233;"     , // e ecute
39988          // "&#250;"     , // u ecute?
39989     ],
39990     
39991     specialElements : [
39992         {
39993             text: "Insert Table",
39994             xtype: 'MenuItem',
39995             xns : Roo.Menu,
39996             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39997                 
39998         },
39999         {    
40000             text: "Insert Image",
40001             xtype: 'MenuItem',
40002             xns : Roo.Menu,
40003             ihtml : '<img src="about:blank"/>'
40004             
40005         }
40006         
40007          
40008     ],
40009     
40010     
40011     inputElements : [ 
40012             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40013             "input:submit", "input:button", "select", "textarea", "label" ],
40014     formats : [
40015         ["p"] ,  
40016         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40017         ["pre"],[ "code"], 
40018         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40019     ],
40020      /**
40021      * @cfg {String} defaultFont default font to use.
40022      */
40023     defaultFont: 'tahoma',
40024    
40025     fontSelect : false,
40026     
40027     
40028     formatCombo : false,
40029     
40030     init : function(editor)
40031     {
40032         this.editor = editor;
40033         
40034         
40035         var fid = editor.frameId;
40036         var etb = this;
40037         function btn(id, toggle, handler){
40038             var xid = fid + '-'+ id ;
40039             return {
40040                 id : xid,
40041                 cmd : id,
40042                 cls : 'x-btn-icon x-edit-'+id,
40043                 enableToggle:toggle !== false,
40044                 scope: editor, // was editor...
40045                 handler:handler||editor.relayBtnCmd,
40046                 clickEvent:'mousedown',
40047                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40048                 tabIndex:-1
40049             };
40050         }
40051         
40052         
40053         
40054         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40055         this.tb = tb;
40056          // stop form submits
40057         tb.el.on('click', function(e){
40058             e.preventDefault(); // what does this do?
40059         });
40060
40061         if(!this.disable.font && !Roo.isSafari){
40062             /* why no safari for fonts
40063             editor.fontSelect = tb.el.createChild({
40064                 tag:'select',
40065                 tabIndex: -1,
40066                 cls:'x-font-select',
40067                 html: editor.createFontOptions()
40068             });
40069             editor.fontSelect.on('change', function(){
40070                 var font = editor.fontSelect.dom.value;
40071                 editor.relayCmd('fontname', font);
40072                 editor.deferFocus();
40073             }, editor);
40074             tb.add(
40075                 editor.fontSelect.dom,
40076                 '-'
40077             );
40078             */
40079         };
40080         if(!this.disable.formats){
40081             this.formatCombo = new Roo.form.ComboBox({
40082                 store: new Roo.data.SimpleStore({
40083                     id : 'tag',
40084                     fields: ['tag'],
40085                     data : this.formats // from states.js
40086                 }),
40087                 blockFocus : true,
40088                 //autoCreate : {tag: "div",  size: "20"},
40089                 displayField:'tag',
40090                 typeAhead: false,
40091                 mode: 'local',
40092                 editable : false,
40093                 triggerAction: 'all',
40094                 emptyText:'Add tag',
40095                 selectOnFocus:true,
40096                 width:135,
40097                 listeners : {
40098                     'select': function(c, r, i) {
40099                         editor.insertTag(r.get('tag'));
40100                         editor.focus();
40101                     }
40102                 }
40103
40104             });
40105             tb.addField(this.formatCombo);
40106             
40107         }
40108         
40109         if(!this.disable.format){
40110             tb.add(
40111                 btn('bold'),
40112                 btn('italic'),
40113                 btn('underline')
40114             );
40115         };
40116         if(!this.disable.fontSize){
40117             tb.add(
40118                 '-',
40119                 
40120                 
40121                 btn('increasefontsize', false, editor.adjustFont),
40122                 btn('decreasefontsize', false, editor.adjustFont)
40123             );
40124         };
40125         
40126         
40127         if(!this.disable.colors){
40128             tb.add(
40129                 '-', {
40130                     id:editor.frameId +'-forecolor',
40131                     cls:'x-btn-icon x-edit-forecolor',
40132                     clickEvent:'mousedown',
40133                     tooltip: this.buttonTips['forecolor'] || undefined,
40134                     tabIndex:-1,
40135                     menu : new Roo.menu.ColorMenu({
40136                         allowReselect: true,
40137                         focus: Roo.emptyFn,
40138                         value:'000000',
40139                         plain:true,
40140                         selectHandler: function(cp, color){
40141                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40142                             editor.deferFocus();
40143                         },
40144                         scope: editor,
40145                         clickEvent:'mousedown'
40146                     })
40147                 }, {
40148                     id:editor.frameId +'backcolor',
40149                     cls:'x-btn-icon x-edit-backcolor',
40150                     clickEvent:'mousedown',
40151                     tooltip: this.buttonTips['backcolor'] || undefined,
40152                     tabIndex:-1,
40153                     menu : new Roo.menu.ColorMenu({
40154                         focus: Roo.emptyFn,
40155                         value:'FFFFFF',
40156                         plain:true,
40157                         allowReselect: true,
40158                         selectHandler: function(cp, color){
40159                             if(Roo.isGecko){
40160                                 editor.execCmd('useCSS', false);
40161                                 editor.execCmd('hilitecolor', color);
40162                                 editor.execCmd('useCSS', true);
40163                                 editor.deferFocus();
40164                             }else{
40165                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40166                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40167                                 editor.deferFocus();
40168                             }
40169                         },
40170                         scope:editor,
40171                         clickEvent:'mousedown'
40172                     })
40173                 }
40174             );
40175         };
40176         // now add all the items...
40177         
40178
40179         if(!this.disable.alignments){
40180             tb.add(
40181                 '-',
40182                 btn('justifyleft'),
40183                 btn('justifycenter'),
40184                 btn('justifyright')
40185             );
40186         };
40187
40188         //if(!Roo.isSafari){
40189             if(!this.disable.links){
40190                 tb.add(
40191                     '-',
40192                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40193                 );
40194             };
40195
40196             if(!this.disable.lists){
40197                 tb.add(
40198                     '-',
40199                     btn('insertorderedlist'),
40200                     btn('insertunorderedlist')
40201                 );
40202             }
40203             if(!this.disable.sourceEdit){
40204                 tb.add(
40205                     '-',
40206                     btn('sourceedit', true, function(btn){
40207                         this.toggleSourceEdit(btn.pressed);
40208                     })
40209                 );
40210             }
40211         //}
40212         
40213         var smenu = { };
40214         // special menu.. - needs to be tidied up..
40215         if (!this.disable.special) {
40216             smenu = {
40217                 text: "&#169;",
40218                 cls: 'x-edit-none',
40219                 
40220                 menu : {
40221                     items : []
40222                 }
40223             };
40224             for (var i =0; i < this.specialChars.length; i++) {
40225                 smenu.menu.items.push({
40226                     
40227                     html: this.specialChars[i],
40228                     handler: function(a,b) {
40229                         //editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40230                         editor.insertAtCursor(a.html);
40231                         
40232                     },
40233                     tabIndex:-1
40234                 });
40235             }
40236             
40237             
40238             tb.add(smenu);
40239             
40240             
40241         }
40242          
40243         if (!this.disable.specialElements) {
40244             var semenu = {
40245                 text: "Other;",
40246                 cls: 'x-edit-none',
40247                 menu : {
40248                     items : []
40249                 }
40250             };
40251             for (var i =0; i < this.specialElements.length; i++) {
40252                 semenu.menu.items.push(
40253                     Roo.apply({ 
40254                         handler: function(a,b) {
40255                             editor.insertAtCursor(this.ihtml);
40256                         }
40257                     }, this.specialElements[i])
40258                 );
40259                     
40260             }
40261             
40262             tb.add(semenu);
40263             
40264             
40265         }
40266          
40267         
40268         if (this.btns) {
40269             for(var i =0; i< this.btns.length;i++) {
40270                 var b = this.btns[i];
40271                 b.cls =  'x-edit-none';
40272                 b.scope = editor;
40273                 tb.add(b);
40274             }
40275         
40276         }
40277         
40278         
40279         
40280         // disable everything...
40281         
40282         this.tb.items.each(function(item){
40283            if(item.id != editor.frameId+ '-sourceedit'){
40284                 item.disable();
40285             }
40286         });
40287         this.rendered = true;
40288         
40289         // the all the btns;
40290         editor.on('editorevent', this.updateToolbar, this);
40291         // other toolbars need to implement this..
40292         //editor.on('editmodechange', this.updateToolbar, this);
40293     },
40294     
40295     
40296     
40297     /**
40298      * Protected method that will not generally be called directly. It triggers
40299      * a toolbar update by reading the markup state of the current selection in the editor.
40300      */
40301     updateToolbar: function(){
40302
40303         if(!this.editor.activated){
40304             this.editor.onFirstFocus();
40305             return;
40306         }
40307
40308         var btns = this.tb.items.map, 
40309             doc = this.editor.doc,
40310             frameId = this.editor.frameId;
40311
40312         if(!this.disable.font && !Roo.isSafari){
40313             /*
40314             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40315             if(name != this.fontSelect.dom.value){
40316                 this.fontSelect.dom.value = name;
40317             }
40318             */
40319         }
40320         if(!this.disable.format){
40321             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40322             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40323             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40324         }
40325         if(!this.disable.alignments){
40326             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40327             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40328             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40329         }
40330         if(!Roo.isSafari && !this.disable.lists){
40331             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40332             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40333         }
40334         
40335         var ans = this.editor.getAllAncestors();
40336         if (this.formatCombo) {
40337             
40338             
40339             var store = this.formatCombo.store;
40340             this.formatCombo.setValue("");
40341             for (var i =0; i < ans.length;i++) {
40342                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40343                     // select it..
40344                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40345                     break;
40346                 }
40347             }
40348         }
40349         
40350         
40351         
40352         // hides menus... - so this cant be on a menu...
40353         Roo.menu.MenuMgr.hideAll();
40354
40355         //this.editorsyncValue();
40356     },
40357    
40358     
40359     createFontOptions : function(){
40360         var buf = [], fs = this.fontFamilies, ff, lc;
40361         for(var i = 0, len = fs.length; i< len; i++){
40362             ff = fs[i];
40363             lc = ff.toLowerCase();
40364             buf.push(
40365                 '<option value="',lc,'" style="font-family:',ff,';"',
40366                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40367                     ff,
40368                 '</option>'
40369             );
40370         }
40371         return buf.join('');
40372     },
40373     
40374     toggleSourceEdit : function(sourceEditMode){
40375         if(sourceEditMode === undefined){
40376             sourceEditMode = !this.sourceEditMode;
40377         }
40378         this.sourceEditMode = sourceEditMode === true;
40379         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40380         // just toggle the button?
40381         if(btn.pressed !== this.editor.sourceEditMode){
40382             btn.toggle(this.editor.sourceEditMode);
40383             return;
40384         }
40385         
40386         if(this.sourceEditMode){
40387             this.tb.items.each(function(item){
40388                 if(item.cmd != 'sourceedit'){
40389                     item.disable();
40390                 }
40391             });
40392           
40393         }else{
40394             if(this.initialized){
40395                 this.tb.items.each(function(item){
40396                     item.enable();
40397                 });
40398             }
40399             
40400         }
40401         // tell the editor that it's been pressed..
40402         this.editor.toggleSourceEdit(sourceEditMode);
40403        
40404     },
40405      /**
40406      * Object collection of toolbar tooltips for the buttons in the editor. The key
40407      * is the command id associated with that button and the value is a valid QuickTips object.
40408      * For example:
40409 <pre><code>
40410 {
40411     bold : {
40412         title: 'Bold (Ctrl+B)',
40413         text: 'Make the selected text bold.',
40414         cls: 'x-html-editor-tip'
40415     },
40416     italic : {
40417         title: 'Italic (Ctrl+I)',
40418         text: 'Make the selected text italic.',
40419         cls: 'x-html-editor-tip'
40420     },
40421     ...
40422 </code></pre>
40423     * @type Object
40424      */
40425     buttonTips : {
40426         bold : {
40427             title: 'Bold (Ctrl+B)',
40428             text: 'Make the selected text bold.',
40429             cls: 'x-html-editor-tip'
40430         },
40431         italic : {
40432             title: 'Italic (Ctrl+I)',
40433             text: 'Make the selected text italic.',
40434             cls: 'x-html-editor-tip'
40435         },
40436         underline : {
40437             title: 'Underline (Ctrl+U)',
40438             text: 'Underline the selected text.',
40439             cls: 'x-html-editor-tip'
40440         },
40441         increasefontsize : {
40442             title: 'Grow Text',
40443             text: 'Increase the font size.',
40444             cls: 'x-html-editor-tip'
40445         },
40446         decreasefontsize : {
40447             title: 'Shrink Text',
40448             text: 'Decrease the font size.',
40449             cls: 'x-html-editor-tip'
40450         },
40451         backcolor : {
40452             title: 'Text Highlight Color',
40453             text: 'Change the background color of the selected text.',
40454             cls: 'x-html-editor-tip'
40455         },
40456         forecolor : {
40457             title: 'Font Color',
40458             text: 'Change the color of the selected text.',
40459             cls: 'x-html-editor-tip'
40460         },
40461         justifyleft : {
40462             title: 'Align Text Left',
40463             text: 'Align text to the left.',
40464             cls: 'x-html-editor-tip'
40465         },
40466         justifycenter : {
40467             title: 'Center Text',
40468             text: 'Center text in the editor.',
40469             cls: 'x-html-editor-tip'
40470         },
40471         justifyright : {
40472             title: 'Align Text Right',
40473             text: 'Align text to the right.',
40474             cls: 'x-html-editor-tip'
40475         },
40476         insertunorderedlist : {
40477             title: 'Bullet List',
40478             text: 'Start a bulleted list.',
40479             cls: 'x-html-editor-tip'
40480         },
40481         insertorderedlist : {
40482             title: 'Numbered List',
40483             text: 'Start a numbered list.',
40484             cls: 'x-html-editor-tip'
40485         },
40486         createlink : {
40487             title: 'Hyperlink',
40488             text: 'Make the selected text a hyperlink.',
40489             cls: 'x-html-editor-tip'
40490         },
40491         sourceedit : {
40492             title: 'Source Edit',
40493             text: 'Switch to source editing mode.',
40494             cls: 'x-html-editor-tip'
40495         }
40496     },
40497     // private
40498     onDestroy : function(){
40499         if(this.rendered){
40500             
40501             this.tb.items.each(function(item){
40502                 if(item.menu){
40503                     item.menu.removeAll();
40504                     if(item.menu.el){
40505                         item.menu.el.destroy();
40506                     }
40507                 }
40508                 item.destroy();
40509             });
40510              
40511         }
40512     },
40513     onFirstFocus: function() {
40514         this.tb.items.each(function(item){
40515            item.enable();
40516         });
40517     }
40518 });
40519
40520
40521
40522
40523 // <script type="text/javascript">
40524 /*
40525  * Based on
40526  * Ext JS Library 1.1.1
40527  * Copyright(c) 2006-2007, Ext JS, LLC.
40528  *  
40529  
40530  */
40531
40532  
40533 /**
40534  * @class Roo.form.HtmlEditor.ToolbarContext
40535  * Context Toolbar
40536  * 
40537  * Usage:
40538  *
40539  new Roo.form.HtmlEditor({
40540     ....
40541     toolbars : [
40542         { xtype: 'ToolbarStandard', styles : {} }
40543         { xtype: 'ToolbarContext', disable : {} }
40544     ]
40545 })
40546
40547      
40548  * 
40549  * @config : {Object} disable List of elements to disable.. (not done yet.)
40550  * @config : {Object} styles  Map of styles available.
40551  * 
40552  */
40553
40554 Roo.form.HtmlEditor.ToolbarContext = function(config)
40555 {
40556     
40557     Roo.apply(this, config);
40558     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40559     // dont call parent... till later.
40560     this.styles = this.styles || {};
40561 }
40562 Roo.form.HtmlEditor.ToolbarContext.types = {
40563     'IMG' : {
40564         width : {
40565             title: "Width",
40566             width: 40
40567         },
40568         height:  {
40569             title: "Height",
40570             width: 40
40571         },
40572         align: {
40573             title: "Align",
40574             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40575             width : 80
40576             
40577         },
40578         border: {
40579             title: "Border",
40580             width: 40
40581         },
40582         alt: {
40583             title: "Alt",
40584             width: 120
40585         },
40586         src : {
40587             title: "Src",
40588             width: 220
40589         }
40590         
40591     },
40592     'A' : {
40593         name : {
40594             title: "Name",
40595             width: 50
40596         },
40597         href:  {
40598             title: "Href",
40599             width: 220
40600         } // border?
40601         
40602     },
40603     'TABLE' : {
40604         rows : {
40605             title: "Rows",
40606             width: 20
40607         },
40608         cols : {
40609             title: "Cols",
40610             width: 20
40611         },
40612         width : {
40613             title: "Width",
40614             width: 40
40615         },
40616         height : {
40617             title: "Height",
40618             width: 40
40619         },
40620         border : {
40621             title: "Border",
40622             width: 20
40623         }
40624     },
40625     'TD' : {
40626         width : {
40627             title: "Width",
40628             width: 40
40629         },
40630         height : {
40631             title: "Height",
40632             width: 40
40633         },   
40634         align: {
40635             title: "Align",
40636             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40637             width: 80
40638         },
40639         valign: {
40640             title: "Valign",
40641             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40642             width: 80
40643         },
40644         colspan: {
40645             title: "Colspan",
40646             width: 20
40647             
40648         }
40649     },
40650     'INPUT' : {
40651         name : {
40652             title: "name",
40653             width: 120
40654         },
40655         value : {
40656             title: "Value",
40657             width: 120
40658         },
40659         width : {
40660             title: "Width",
40661             width: 40
40662         }
40663     },
40664     'LABEL' : {
40665         'for' : {
40666             title: "For",
40667             width: 120
40668         }
40669     },
40670     'TEXTAREA' : {
40671           name : {
40672             title: "name",
40673             width: 120
40674         },
40675         rows : {
40676             title: "Rows",
40677             width: 20
40678         },
40679         cols : {
40680             title: "Cols",
40681             width: 20
40682         }
40683     },
40684     'SELECT' : {
40685         name : {
40686             title: "name",
40687             width: 120
40688         },
40689         selectoptions : {
40690             title: "Options",
40691             width: 200
40692         }
40693     },
40694     
40695     // should we really allow this??
40696     // should this just be 
40697     'BODY' : {
40698         title : {
40699             title: "title",
40700             width: 200,
40701             disabled : true
40702         }
40703     },
40704     '*' : {
40705         // empty..
40706     }
40707 };
40708
40709
40710
40711 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40712     
40713     tb: false,
40714     
40715     rendered: false,
40716     
40717     editor : false,
40718     /**
40719      * @cfg {Object} disable  List of toolbar elements to disable
40720          
40721      */
40722     disable : false,
40723     /**
40724      * @cfg {Object} styles List of styles 
40725      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40726      *
40727      * These must be defined in the page, so they get rendered correctly..
40728      * .headline { }
40729      * TD.underline { }
40730      * 
40731      */
40732     styles : false,
40733     
40734     
40735     
40736     toolbars : false,
40737     
40738     init : function(editor)
40739     {
40740         this.editor = editor;
40741         
40742         
40743         var fid = editor.frameId;
40744         var etb = this;
40745         function btn(id, toggle, handler){
40746             var xid = fid + '-'+ id ;
40747             return {
40748                 id : xid,
40749                 cmd : id,
40750                 cls : 'x-btn-icon x-edit-'+id,
40751                 enableToggle:toggle !== false,
40752                 scope: editor, // was editor...
40753                 handler:handler||editor.relayBtnCmd,
40754                 clickEvent:'mousedown',
40755                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40756                 tabIndex:-1
40757             };
40758         }
40759         // create a new element.
40760         var wdiv = editor.wrap.createChild({
40761                 tag: 'div'
40762             }, editor.wrap.dom.firstChild.nextSibling, true);
40763         
40764         // can we do this more than once??
40765         
40766          // stop form submits
40767       
40768  
40769         // disable everything...
40770         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40771         this.toolbars = {};
40772            
40773         for (var i in  ty) {
40774           
40775             this.toolbars[i] = this.buildToolbar(ty[i],i);
40776         }
40777         this.tb = this.toolbars.BODY;
40778         this.tb.el.show();
40779         this.buildFooter();
40780         this.footer.show();
40781          
40782         this.rendered = true;
40783         
40784         // the all the btns;
40785         editor.on('editorevent', this.updateToolbar, this);
40786         // other toolbars need to implement this..
40787         //editor.on('editmodechange', this.updateToolbar, this);
40788     },
40789     
40790     
40791     
40792     /**
40793      * Protected method that will not generally be called directly. It triggers
40794      * a toolbar update by reading the markup state of the current selection in the editor.
40795      */
40796     updateToolbar: function(ignore_a,ignore_b,sel){
40797
40798         
40799         if(!this.editor.activated){
40800              this.editor.onFirstFocus();
40801             return;
40802         }
40803         var updateFooter = sel ? false : true;
40804         
40805         
40806         var ans = this.editor.getAllAncestors();
40807         
40808         // pick
40809         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40810         
40811         if (!sel) { 
40812             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40813             sel = sel ? sel : this.editor.doc.body;
40814             sel = sel.tagName.length ? sel : this.editor.doc.body;
40815             
40816         }
40817         // pick a menu that exists..
40818         var tn = sel.tagName.toUpperCase();
40819         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40820         
40821         tn = sel.tagName.toUpperCase();
40822         
40823         var lastSel = this.tb.selectedNode
40824         
40825         this.tb.selectedNode = sel;
40826         
40827         // if current menu does not match..
40828         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40829                 
40830             this.tb.el.hide();
40831             ///console.log("show: " + tn);
40832             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40833             this.tb.el.show();
40834             // update name
40835             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40836             
40837             
40838             // update attributes
40839             if (this.tb.fields) {
40840                 this.tb.fields.each(function(e) {
40841                    e.setValue(sel.getAttribute(e.name));
40842                 });
40843             }
40844             
40845             // update styles
40846             var st = this.tb.fields.item(0);
40847             st.store.removeAll();
40848             var cn = sel.className.split(/\s+/);
40849             
40850             var avs = [];
40851             if (this.styles['*']) {
40852                 
40853                 Roo.each(this.styles['*'], function(v) {
40854                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40855                 });
40856             }
40857             if (this.styles[tn]) { 
40858                 Roo.each(this.styles[tn], function(v) {
40859                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40860                 });
40861             }
40862             
40863             st.store.loadData(avs);
40864             st.collapse();
40865             st.setValue(cn);
40866             
40867             // flag our selected Node.
40868             this.tb.selectedNode = sel;
40869            
40870            
40871             Roo.menu.MenuMgr.hideAll();
40872
40873         }
40874         
40875         if (!updateFooter) {
40876             return;
40877         }
40878         // update the footer
40879         //
40880         var html = '';
40881         
40882         this.footerEls = ans.reverse();
40883         Roo.each(this.footerEls, function(a,i) {
40884             if (!a) { return; }
40885             html += html.length ? ' &gt; '  :  '';
40886             
40887             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40888             
40889         });
40890        
40891         // 
40892         var sz = this.footDisp.up('td').getSize();
40893         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40894         this.footDisp.dom.style.marginLeft = '5px';
40895         
40896         this.footDisp.dom.style.overflow = 'hidden';
40897         
40898         this.footDisp.dom.innerHTML = html;
40899             
40900         //this.editorsyncValue();
40901     },
40902    
40903        
40904     // private
40905     onDestroy : function(){
40906         if(this.rendered){
40907             
40908             this.tb.items.each(function(item){
40909                 if(item.menu){
40910                     item.menu.removeAll();
40911                     if(item.menu.el){
40912                         item.menu.el.destroy();
40913                     }
40914                 }
40915                 item.destroy();
40916             });
40917              
40918         }
40919     },
40920     onFirstFocus: function() {
40921         // need to do this for all the toolbars..
40922         this.tb.items.each(function(item){
40923            item.enable();
40924         });
40925     },
40926     buildToolbar: function(tlist, nm)
40927     {
40928         var editor = this.editor;
40929          // create a new element.
40930         var wdiv = editor.wrap.createChild({
40931                 tag: 'div'
40932             }, editor.wrap.dom.firstChild.nextSibling, true);
40933         
40934        
40935         var tb = new Roo.Toolbar(wdiv);
40936         // add the name..
40937         
40938         tb.add(nm+ ":&nbsp;");
40939         
40940         // styles...
40941         if (this.styles) {
40942             
40943             // this needs a multi-select checkbox...
40944             tb.addField( new Roo.form.ComboBox({
40945                 store: new Roo.data.SimpleStore({
40946                     id : 'val',
40947                     fields: ['val', 'selected'],
40948                     data : [] 
40949                 }),
40950                 name : 'className',
40951                 displayField:'val',
40952                 typeAhead: false,
40953                 mode: 'local',
40954                 editable : false,
40955                 triggerAction: 'all',
40956                 emptyText:'Select Style',
40957                 selectOnFocus:true,
40958                 width: 130,
40959                 listeners : {
40960                     'select': function(c, r, i) {
40961                         // initial support only for on class per el..
40962                         tb.selectedNode.className =  r ? r.get('val') : '';
40963                     }
40964                 }
40965     
40966             }));
40967         }
40968             
40969         
40970         
40971         for (var i in tlist) {
40972             
40973             var item = tlist[i];
40974             tb.add(item.title + ":&nbsp;");
40975             
40976             
40977             
40978             
40979             if (item.opts) {
40980                 // opts == pulldown..
40981                 tb.addField( new Roo.form.ComboBox({
40982                     store: new Roo.data.SimpleStore({
40983                         id : 'val',
40984                         fields: ['val'],
40985                         data : item.opts  
40986                     }),
40987                     name : i,
40988                     displayField:'val',
40989                     typeAhead: false,
40990                     mode: 'local',
40991                     editable : false,
40992                     triggerAction: 'all',
40993                     emptyText:'Select',
40994                     selectOnFocus:true,
40995                     width: item.width ? item.width  : 130,
40996                     listeners : {
40997                         'select': function(c, r, i) {
40998                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40999                         }
41000                     }
41001
41002                 }));
41003                 continue;
41004                     
41005                  
41006                 
41007                 tb.addField( new Roo.form.TextField({
41008                     name: i,
41009                     width: 100,
41010                     //allowBlank:false,
41011                     value: ''
41012                 }));
41013                 continue;
41014             }
41015             tb.addField( new Roo.form.TextField({
41016                 name: i,
41017                 width: item.width,
41018                 //allowBlank:true,
41019                 value: '',
41020                 listeners: {
41021                     'change' : function(f, nv, ov) {
41022                         tb.selectedNode.setAttribute(f.name, nv);
41023                     }
41024                 }
41025             }));
41026              
41027         }
41028         tb.el.on('click', function(e){
41029             e.preventDefault(); // what does this do?
41030         });
41031         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41032         tb.el.hide();
41033         tb.name = nm;
41034         // dont need to disable them... as they will get hidden
41035         return tb;
41036          
41037         
41038     },
41039     buildFooter : function()
41040     {
41041         
41042         var fel = this.editor.wrap.createChild();
41043         this.footer = new Roo.Toolbar(fel);
41044         // toolbar has scrolly on left / right?
41045         var footDisp= new Roo.Toolbar.Fill();
41046         var _t = this;
41047         this.footer.add(
41048             {
41049                 text : '&lt;',
41050                 xtype: 'Button',
41051                 handler : function() {
41052                     _t.footDisp.scrollTo('left',0,true)
41053                 }
41054             }
41055         );
41056         this.footer.add( footDisp );
41057         this.footer.add( 
41058             {
41059                 text : '&gt;',
41060                 xtype: 'Button',
41061                 handler : function() {
41062                     // no animation..
41063                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41064                 }
41065             }
41066         );
41067         var fel = Roo.get(footDisp.el);
41068         fel.addClass('x-editor-context');
41069         this.footDispWrap = fel; 
41070         this.footDispWrap.overflow  = 'hidden';
41071         
41072         this.footDisp = fel.createChild();
41073         this.footDispWrap.on('click', this.onContextClick, this)
41074         
41075         
41076     },
41077     onContextClick : function (ev,dom)
41078     {
41079         ev.preventDefault();
41080         var  cn = dom.className;
41081         Roo.log(cn);
41082         if (!cn.match(/x-ed-loc-/)) {
41083             return;
41084         }
41085         var n = cn.split('-').pop();
41086         var ans = this.footerEls;
41087         var sel = ans[n];
41088         
41089          // pick
41090         var range = this.editor.createRange();
41091         
41092         range.selectNodeContents(sel);
41093         //range.selectNode(sel);
41094         
41095         
41096         var selection = this.editor.getSelection();
41097         selection.removeAllRanges();
41098         selection.addRange(range);
41099         
41100         
41101         
41102         this.updateToolbar(null, null, sel);
41103         
41104         
41105     }
41106     
41107     
41108     
41109     
41110     
41111 });
41112
41113
41114
41115
41116
41117 /*
41118  * Based on:
41119  * Ext JS Library 1.1.1
41120  * Copyright(c) 2006-2007, Ext JS, LLC.
41121  *
41122  * Originally Released Under LGPL - original licence link has changed is not relivant.
41123  *
41124  * Fork - LGPL
41125  * <script type="text/javascript">
41126  */
41127  
41128 /**
41129  * @class Roo.form.BasicForm
41130  * @extends Roo.util.Observable
41131  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41132  * @constructor
41133  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41134  * @param {Object} config Configuration options
41135  */
41136 Roo.form.BasicForm = function(el, config){
41137     this.allItems = [];
41138     this.childForms = [];
41139     Roo.apply(this, config);
41140     /*
41141      * The Roo.form.Field items in this form.
41142      * @type MixedCollection
41143      */
41144      
41145      
41146     this.items = new Roo.util.MixedCollection(false, function(o){
41147         return o.id || (o.id = Roo.id());
41148     });
41149     this.addEvents({
41150         /**
41151          * @event beforeaction
41152          * Fires before any action is performed. Return false to cancel the action.
41153          * @param {Form} this
41154          * @param {Action} action The action to be performed
41155          */
41156         beforeaction: true,
41157         /**
41158          * @event actionfailed
41159          * Fires when an action fails.
41160          * @param {Form} this
41161          * @param {Action} action The action that failed
41162          */
41163         actionfailed : true,
41164         /**
41165          * @event actioncomplete
41166          * Fires when an action is completed.
41167          * @param {Form} this
41168          * @param {Action} action The action that completed
41169          */
41170         actioncomplete : true
41171     });
41172     if(el){
41173         this.initEl(el);
41174     }
41175     Roo.form.BasicForm.superclass.constructor.call(this);
41176 };
41177
41178 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41179     /**
41180      * @cfg {String} method
41181      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41182      */
41183     /**
41184      * @cfg {DataReader} reader
41185      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41186      * This is optional as there is built-in support for processing JSON.
41187      */
41188     /**
41189      * @cfg {DataReader} errorReader
41190      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41191      * This is completely optional as there is built-in support for processing JSON.
41192      */
41193     /**
41194      * @cfg {String} url
41195      * The URL to use for form actions if one isn't supplied in the action options.
41196      */
41197     /**
41198      * @cfg {Boolean} fileUpload
41199      * Set to true if this form is a file upload.
41200      */
41201      
41202     /**
41203      * @cfg {Object} baseParams
41204      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41205      */
41206      /**
41207      
41208     /**
41209      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41210      */
41211     timeout: 30,
41212
41213     // private
41214     activeAction : null,
41215
41216     /**
41217      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41218      * or setValues() data instead of when the form was first created.
41219      */
41220     trackResetOnLoad : false,
41221     
41222     
41223     /**
41224      * childForms - used for multi-tab forms
41225      * @type {Array}
41226      */
41227     childForms : false,
41228     
41229     /**
41230      * allItems - full list of fields.
41231      * @type {Array}
41232      */
41233     allItems : false,
41234     
41235     /**
41236      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41237      * element by passing it or its id or mask the form itself by passing in true.
41238      * @type Mixed
41239      */
41240     waitMsgTarget : false,
41241
41242     // private
41243     initEl : function(el){
41244         this.el = Roo.get(el);
41245         this.id = this.el.id || Roo.id();
41246         this.el.on('submit', this.onSubmit, this);
41247         this.el.addClass('x-form');
41248     },
41249
41250     // private
41251     onSubmit : function(e){
41252         e.stopEvent();
41253     },
41254
41255     /**
41256      * Returns true if client-side validation on the form is successful.
41257      * @return Boolean
41258      */
41259     isValid : function(){
41260         var valid = true;
41261         this.items.each(function(f){
41262            if(!f.validate()){
41263                valid = false;
41264            }
41265         });
41266         return valid;
41267     },
41268
41269     /**
41270      * Returns true if any fields in this form have changed since their original load.
41271      * @return Boolean
41272      */
41273     isDirty : function(){
41274         var dirty = false;
41275         this.items.each(function(f){
41276            if(f.isDirty()){
41277                dirty = true;
41278                return false;
41279            }
41280         });
41281         return dirty;
41282     },
41283
41284     /**
41285      * Performs a predefined action (submit or load) or custom actions you define on this form.
41286      * @param {String} actionName The name of the action type
41287      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41288      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41289      * accept other config options):
41290      * <pre>
41291 Property          Type             Description
41292 ----------------  ---------------  ----------------------------------------------------------------------------------
41293 url               String           The url for the action (defaults to the form's url)
41294 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41295 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41296 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41297                                    validate the form on the client (defaults to false)
41298      * </pre>
41299      * @return {BasicForm} this
41300      */
41301     doAction : function(action, options){
41302         if(typeof action == 'string'){
41303             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41304         }
41305         if(this.fireEvent('beforeaction', this, action) !== false){
41306             this.beforeAction(action);
41307             action.run.defer(100, action);
41308         }
41309         return this;
41310     },
41311
41312     /**
41313      * Shortcut to do a submit action.
41314      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41315      * @return {BasicForm} this
41316      */
41317     submit : function(options){
41318         this.doAction('submit', options);
41319         return this;
41320     },
41321
41322     /**
41323      * Shortcut to do a load action.
41324      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41325      * @return {BasicForm} this
41326      */
41327     load : function(options){
41328         this.doAction('load', options);
41329         return this;
41330     },
41331
41332     /**
41333      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41334      * @param {Record} record The record to edit
41335      * @return {BasicForm} this
41336      */
41337     updateRecord : function(record){
41338         record.beginEdit();
41339         var fs = record.fields;
41340         fs.each(function(f){
41341             var field = this.findField(f.name);
41342             if(field){
41343                 record.set(f.name, field.getValue());
41344             }
41345         }, this);
41346         record.endEdit();
41347         return this;
41348     },
41349
41350     /**
41351      * Loads an Roo.data.Record into this form.
41352      * @param {Record} record The record to load
41353      * @return {BasicForm} this
41354      */
41355     loadRecord : function(record){
41356         this.setValues(record.data);
41357         return this;
41358     },
41359
41360     // private
41361     beforeAction : function(action){
41362         var o = action.options;
41363         
41364        
41365         if(this.waitMsgTarget === true){
41366             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41367         }else if(this.waitMsgTarget){
41368             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41369             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41370         }else {
41371             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41372         }
41373          
41374     },
41375
41376     // private
41377     afterAction : function(action, success){
41378         this.activeAction = null;
41379         var o = action.options;
41380         
41381         if(this.waitMsgTarget === true){
41382             this.el.unmask();
41383         }else if(this.waitMsgTarget){
41384             this.waitMsgTarget.unmask();
41385         }else{
41386             Roo.MessageBox.updateProgress(1);
41387             Roo.MessageBox.hide();
41388         }
41389          
41390         if(success){
41391             if(o.reset){
41392                 this.reset();
41393             }
41394             Roo.callback(o.success, o.scope, [this, action]);
41395             this.fireEvent('actioncomplete', this, action);
41396             
41397         }else{
41398             Roo.callback(o.failure, o.scope, [this, action]);
41399             // show an error message if no failed handler is set..
41400             if (!this.hasListener('actionfailed')) {
41401                 Roo.MessageBox.alert("Error",
41402                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41403                         action.result.errorMsg :
41404                         "Saving Failed, please check your entries"
41405                 );
41406             }
41407             
41408             this.fireEvent('actionfailed', this, action);
41409         }
41410         
41411     },
41412
41413     /**
41414      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41415      * @param {String} id The value to search for
41416      * @return Field
41417      */
41418     findField : function(id){
41419         var field = this.items.get(id);
41420         if(!field){
41421             this.items.each(function(f){
41422                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41423                     field = f;
41424                     return false;
41425                 }
41426             });
41427         }
41428         return field || null;
41429     },
41430
41431     /**
41432      * Add a secondary form to this one, 
41433      * Used to provide tabbed forms. One form is primary, with hidden values 
41434      * which mirror the elements from the other forms.
41435      * 
41436      * @param {Roo.form.Form} form to add.
41437      * 
41438      */
41439     addForm : function(form)
41440     {
41441        
41442         if (this.childForms.indexOf(form) > -1) {
41443             // already added..
41444             return;
41445         }
41446         this.childForms.push(form);
41447         var n = '';
41448         Roo.each(form.allItems, function (fe) {
41449             
41450             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41451             if (this.findField(n)) { // already added..
41452                 return;
41453             }
41454             var add = new Roo.form.Hidden({
41455                 name : n
41456             });
41457             add.render(this.el);
41458             
41459             this.add( add );
41460         }, this);
41461         
41462     },
41463     /**
41464      * Mark fields in this form invalid in bulk.
41465      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41466      * @return {BasicForm} this
41467      */
41468     markInvalid : function(errors){
41469         if(errors instanceof Array){
41470             for(var i = 0, len = errors.length; i < len; i++){
41471                 var fieldError = errors[i];
41472                 var f = this.findField(fieldError.id);
41473                 if(f){
41474                     f.markInvalid(fieldError.msg);
41475                 }
41476             }
41477         }else{
41478             var field, id;
41479             for(id in errors){
41480                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41481                     field.markInvalid(errors[id]);
41482                 }
41483             }
41484         }
41485         Roo.each(this.childForms || [], function (f) {
41486             f.markInvalid(errors);
41487         });
41488         
41489         return this;
41490     },
41491
41492     /**
41493      * Set values for fields in this form in bulk.
41494      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41495      * @return {BasicForm} this
41496      */
41497     setValues : function(values){
41498         if(values instanceof Array){ // array of objects
41499             for(var i = 0, len = values.length; i < len; i++){
41500                 var v = values[i];
41501                 var f = this.findField(v.id);
41502                 if(f){
41503                     f.setValue(v.value);
41504                     if(this.trackResetOnLoad){
41505                         f.originalValue = f.getValue();
41506                     }
41507                 }
41508             }
41509         }else{ // object hash
41510             var field, id;
41511             for(id in values){
41512                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41513                     
41514                     if (field.setFromData && 
41515                         field.valueField && 
41516                         field.displayField &&
41517                         // combos' with local stores can 
41518                         // be queried via setValue()
41519                         // to set their value..
41520                         (field.store && !field.store.isLocal)
41521                         ) {
41522                         // it's a combo
41523                         var sd = { };
41524                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41525                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41526                         field.setFromData(sd);
41527                         
41528                     } else {
41529                         field.setValue(values[id]);
41530                     }
41531                     
41532                     
41533                     if(this.trackResetOnLoad){
41534                         field.originalValue = field.getValue();
41535                     }
41536                 }
41537             }
41538         }
41539          
41540         Roo.each(this.childForms || [], function (f) {
41541             f.setValues(values);
41542         });
41543                 
41544         return this;
41545     },
41546
41547     /**
41548      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41549      * they are returned as an array.
41550      * @param {Boolean} asString
41551      * @return {Object}
41552      */
41553     getValues : function(asString){
41554         if (this.childForms) {
41555             // copy values from the child forms
41556             Roo.each(this.childForms, function (f) {
41557                 this.setValues(f.getValues());
41558             }, this);
41559         }
41560         
41561         
41562         
41563         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41564         if(asString === true){
41565             return fs;
41566         }
41567         return Roo.urlDecode(fs);
41568     },
41569     
41570     /**
41571      * Returns the fields in this form as an object with key/value pairs. 
41572      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41573      * @return {Object}
41574      */
41575     getFieldValues : function(with_hidden)
41576     {
41577         if (this.childForms) {
41578             // copy values from the child forms
41579             // should this call getFieldValues - probably not as we do not currently copy
41580             // hidden fields when we generate..
41581             Roo.each(this.childForms, function (f) {
41582                 this.setValues(f.getValues());
41583             }, this);
41584         }
41585         
41586         var ret = {};
41587         this.items.each(function(f){
41588             if (!f.getName()) {
41589                 return;
41590             }
41591             var v = f.getValue();
41592             // not sure if this supported any more..
41593             if ((typeof(v) == 'object') && f.getRawValue) {
41594                 v = f.getRawValue() ; // dates..
41595             }
41596             // combo boxes where name != hiddenName...
41597             if (f.name != f.getName()) {
41598                 ret[f.name] = f.getRawValue();
41599             }
41600             ret[f.getName()] = v;
41601         });
41602         
41603         return ret;
41604     },
41605
41606     /**
41607      * Clears all invalid messages in this form.
41608      * @return {BasicForm} this
41609      */
41610     clearInvalid : function(){
41611         this.items.each(function(f){
41612            f.clearInvalid();
41613         });
41614         
41615         Roo.each(this.childForms || [], function (f) {
41616             f.clearInvalid();
41617         });
41618         
41619         
41620         return this;
41621     },
41622
41623     /**
41624      * Resets this form.
41625      * @return {BasicForm} this
41626      */
41627     reset : function(){
41628         this.items.each(function(f){
41629             f.reset();
41630         });
41631         
41632         Roo.each(this.childForms || [], function (f) {
41633             f.reset();
41634         });
41635        
41636         
41637         return this;
41638     },
41639
41640     /**
41641      * Add Roo.form components to this form.
41642      * @param {Field} field1
41643      * @param {Field} field2 (optional)
41644      * @param {Field} etc (optional)
41645      * @return {BasicForm} this
41646      */
41647     add : function(){
41648         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41649         return this;
41650     },
41651
41652
41653     /**
41654      * Removes a field from the items collection (does NOT remove its markup).
41655      * @param {Field} field
41656      * @return {BasicForm} this
41657      */
41658     remove : function(field){
41659         this.items.remove(field);
41660         return this;
41661     },
41662
41663     /**
41664      * Looks at the fields in this form, checks them for an id attribute,
41665      * and calls applyTo on the existing dom element with that id.
41666      * @return {BasicForm} this
41667      */
41668     render : function(){
41669         this.items.each(function(f){
41670             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41671                 f.applyTo(f.id);
41672             }
41673         });
41674         return this;
41675     },
41676
41677     /**
41678      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41679      * @param {Object} values
41680      * @return {BasicForm} this
41681      */
41682     applyToFields : function(o){
41683         this.items.each(function(f){
41684            Roo.apply(f, o);
41685         });
41686         return this;
41687     },
41688
41689     /**
41690      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41691      * @param {Object} values
41692      * @return {BasicForm} this
41693      */
41694     applyIfToFields : function(o){
41695         this.items.each(function(f){
41696            Roo.applyIf(f, o);
41697         });
41698         return this;
41699     }
41700 });
41701
41702 // back compat
41703 Roo.BasicForm = Roo.form.BasicForm;/*
41704  * Based on:
41705  * Ext JS Library 1.1.1
41706  * Copyright(c) 2006-2007, Ext JS, LLC.
41707  *
41708  * Originally Released Under LGPL - original licence link has changed is not relivant.
41709  *
41710  * Fork - LGPL
41711  * <script type="text/javascript">
41712  */
41713
41714 /**
41715  * @class Roo.form.Form
41716  * @extends Roo.form.BasicForm
41717  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41718  * @constructor
41719  * @param {Object} config Configuration options
41720  */
41721 Roo.form.Form = function(config){
41722     var xitems =  [];
41723     if (config.items) {
41724         xitems = config.items;
41725         delete config.items;
41726     }
41727    
41728     
41729     Roo.form.Form.superclass.constructor.call(this, null, config);
41730     this.url = this.url || this.action;
41731     if(!this.root){
41732         this.root = new Roo.form.Layout(Roo.applyIf({
41733             id: Roo.id()
41734         }, config));
41735     }
41736     this.active = this.root;
41737     /**
41738      * Array of all the buttons that have been added to this form via {@link addButton}
41739      * @type Array
41740      */
41741     this.buttons = [];
41742     this.allItems = [];
41743     this.addEvents({
41744         /**
41745          * @event clientvalidation
41746          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41747          * @param {Form} this
41748          * @param {Boolean} valid true if the form has passed client-side validation
41749          */
41750         clientvalidation: true,
41751         /**
41752          * @event rendered
41753          * Fires when the form is rendered
41754          * @param {Roo.form.Form} form
41755          */
41756         rendered : true
41757     });
41758     
41759     if (this.progressUrl) {
41760             // push a hidden field onto the list of fields..
41761             this.addxtype( {
41762                     xns: Roo.form, 
41763                     xtype : 'Hidden', 
41764                     name : 'UPLOAD_IDENTIFIER' 
41765             });
41766         }
41767         
41768     
41769     Roo.each(xitems, this.addxtype, this);
41770     
41771     
41772     
41773 };
41774
41775 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41776     /**
41777      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41778      */
41779     /**
41780      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41781      */
41782     /**
41783      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41784      */
41785     buttonAlign:'center',
41786
41787     /**
41788      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41789      */
41790     minButtonWidth:75,
41791
41792     /**
41793      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41794      * This property cascades to child containers if not set.
41795      */
41796     labelAlign:'left',
41797
41798     /**
41799      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41800      * fires a looping event with that state. This is required to bind buttons to the valid
41801      * state using the config value formBind:true on the button.
41802      */
41803     monitorValid : false,
41804
41805     /**
41806      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41807      */
41808     monitorPoll : 200,
41809     
41810     /**
41811      * @cfg {String} progressUrl - Url to return progress data 
41812      */
41813     
41814     progressUrl : false,
41815   
41816     /**
41817      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41818      * fields are added and the column is closed. If no fields are passed the column remains open
41819      * until end() is called.
41820      * @param {Object} config The config to pass to the column
41821      * @param {Field} field1 (optional)
41822      * @param {Field} field2 (optional)
41823      * @param {Field} etc (optional)
41824      * @return Column The column container object
41825      */
41826     column : function(c){
41827         var col = new Roo.form.Column(c);
41828         this.start(col);
41829         if(arguments.length > 1){ // duplicate code required because of Opera
41830             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41831             this.end();
41832         }
41833         return col;
41834     },
41835
41836     /**
41837      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41838      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41839      * until end() is called.
41840      * @param {Object} config The config to pass to the fieldset
41841      * @param {Field} field1 (optional)
41842      * @param {Field} field2 (optional)
41843      * @param {Field} etc (optional)
41844      * @return FieldSet The fieldset container object
41845      */
41846     fieldset : function(c){
41847         var fs = new Roo.form.FieldSet(c);
41848         this.start(fs);
41849         if(arguments.length > 1){ // duplicate code required because of Opera
41850             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41851             this.end();
41852         }
41853         return fs;
41854     },
41855
41856     /**
41857      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41858      * fields are added and the container is closed. If no fields are passed the container remains open
41859      * until end() is called.
41860      * @param {Object} config The config to pass to the Layout
41861      * @param {Field} field1 (optional)
41862      * @param {Field} field2 (optional)
41863      * @param {Field} etc (optional)
41864      * @return Layout The container object
41865      */
41866     container : function(c){
41867         var l = new Roo.form.Layout(c);
41868         this.start(l);
41869         if(arguments.length > 1){ // duplicate code required because of Opera
41870             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41871             this.end();
41872         }
41873         return l;
41874     },
41875
41876     /**
41877      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41878      * @param {Object} container A Roo.form.Layout or subclass of Layout
41879      * @return {Form} this
41880      */
41881     start : function(c){
41882         // cascade label info
41883         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41884         this.active.stack.push(c);
41885         c.ownerCt = this.active;
41886         this.active = c;
41887         return this;
41888     },
41889
41890     /**
41891      * Closes the current open container
41892      * @return {Form} this
41893      */
41894     end : function(){
41895         if(this.active == this.root){
41896             return this;
41897         }
41898         this.active = this.active.ownerCt;
41899         return this;
41900     },
41901
41902     /**
41903      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41904      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41905      * as the label of the field.
41906      * @param {Field} field1
41907      * @param {Field} field2 (optional)
41908      * @param {Field} etc. (optional)
41909      * @return {Form} this
41910      */
41911     add : function(){
41912         this.active.stack.push.apply(this.active.stack, arguments);
41913         this.allItems.push.apply(this.allItems,arguments);
41914         var r = [];
41915         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41916             if(a[i].isFormField){
41917                 r.push(a[i]);
41918             }
41919         }
41920         if(r.length > 0){
41921             Roo.form.Form.superclass.add.apply(this, r);
41922         }
41923         return this;
41924     },
41925     
41926
41927     
41928     
41929     
41930      /**
41931      * Find any element that has been added to a form, using it's ID or name
41932      * This can include framesets, columns etc. along with regular fields..
41933      * @param {String} id - id or name to find.
41934      
41935      * @return {Element} e - or false if nothing found.
41936      */
41937     findbyId : function(id)
41938     {
41939         var ret = false;
41940         if (!id) {
41941             return ret;
41942         }
41943         Roo.each(this.allItems, function(f){
41944             if (f.id == id || f.name == id ){
41945                 ret = f;
41946                 return false;
41947             }
41948         });
41949         return ret;
41950     },
41951
41952     
41953     
41954     /**
41955      * Render this form into the passed container. This should only be called once!
41956      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41957      * @return {Form} this
41958      */
41959     render : function(ct)
41960     {
41961         
41962         
41963         
41964         ct = Roo.get(ct);
41965         var o = this.autoCreate || {
41966             tag: 'form',
41967             method : this.method || 'POST',
41968             id : this.id || Roo.id()
41969         };
41970         this.initEl(ct.createChild(o));
41971
41972         this.root.render(this.el);
41973         
41974        
41975              
41976         this.items.each(function(f){
41977             f.render('x-form-el-'+f.id);
41978         });
41979
41980         if(this.buttons.length > 0){
41981             // tables are required to maintain order and for correct IE layout
41982             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41983                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41984                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41985             }}, null, true);
41986             var tr = tb.getElementsByTagName('tr')[0];
41987             for(var i = 0, len = this.buttons.length; i < len; i++) {
41988                 var b = this.buttons[i];
41989                 var td = document.createElement('td');
41990                 td.className = 'x-form-btn-td';
41991                 b.render(tr.appendChild(td));
41992             }
41993         }
41994         if(this.monitorValid){ // initialize after render
41995             this.startMonitoring();
41996         }
41997         this.fireEvent('rendered', this);
41998         return this;
41999     },
42000
42001     /**
42002      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42003      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42004      * object or a valid Roo.DomHelper element config
42005      * @param {Function} handler The function called when the button is clicked
42006      * @param {Object} scope (optional) The scope of the handler function
42007      * @return {Roo.Button}
42008      */
42009     addButton : function(config, handler, scope){
42010         var bc = {
42011             handler: handler,
42012             scope: scope,
42013             minWidth: this.minButtonWidth,
42014             hideParent:true
42015         };
42016         if(typeof config == "string"){
42017             bc.text = config;
42018         }else{
42019             Roo.apply(bc, config);
42020         }
42021         var btn = new Roo.Button(null, bc);
42022         this.buttons.push(btn);
42023         return btn;
42024     },
42025
42026      /**
42027      * Adds a series of form elements (using the xtype property as the factory method.
42028      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42029      * @param {Object} config 
42030      */
42031     
42032     addxtype : function()
42033     {
42034         var ar = Array.prototype.slice.call(arguments, 0);
42035         var ret = false;
42036         for(var i = 0; i < ar.length; i++) {
42037             if (!ar[i]) {
42038                 continue; // skip -- if this happends something invalid got sent, we 
42039                 // should ignore it, as basically that interface element will not show up
42040                 // and that should be pretty obvious!!
42041             }
42042             
42043             if (Roo.form[ar[i].xtype]) {
42044                 ar[i].form = this;
42045                 var fe = Roo.factory(ar[i], Roo.form);
42046                 if (!ret) {
42047                     ret = fe;
42048                 }
42049                 fe.form = this;
42050                 if (fe.store) {
42051                     fe.store.form = this;
42052                 }
42053                 if (fe.isLayout) {  
42054                          
42055                     this.start(fe);
42056                     this.allItems.push(fe);
42057                     if (fe.items && fe.addxtype) {
42058                         fe.addxtype.apply(fe, fe.items);
42059                         delete fe.items;
42060                     }
42061                      this.end();
42062                     continue;
42063                 }
42064                 
42065                 
42066                  
42067                 this.add(fe);
42068               //  console.log('adding ' + ar[i].xtype);
42069             }
42070             if (ar[i].xtype == 'Button') {  
42071                 //console.log('adding button');
42072                 //console.log(ar[i]);
42073                 this.addButton(ar[i]);
42074                 this.allItems.push(fe);
42075                 continue;
42076             }
42077             
42078             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42079                 alert('end is not supported on xtype any more, use items');
42080             //    this.end();
42081             //    //console.log('adding end');
42082             }
42083             
42084         }
42085         return ret;
42086     },
42087     
42088     /**
42089      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42090      * option "monitorValid"
42091      */
42092     startMonitoring : function(){
42093         if(!this.bound){
42094             this.bound = true;
42095             Roo.TaskMgr.start({
42096                 run : this.bindHandler,
42097                 interval : this.monitorPoll || 200,
42098                 scope: this
42099             });
42100         }
42101     },
42102
42103     /**
42104      * Stops monitoring of the valid state of this form
42105      */
42106     stopMonitoring : function(){
42107         this.bound = false;
42108     },
42109
42110     // private
42111     bindHandler : function(){
42112         if(!this.bound){
42113             return false; // stops binding
42114         }
42115         var valid = true;
42116         this.items.each(function(f){
42117             if(!f.isValid(true)){
42118                 valid = false;
42119                 return false;
42120             }
42121         });
42122         for(var i = 0, len = this.buttons.length; i < len; i++){
42123             var btn = this.buttons[i];
42124             if(btn.formBind === true && btn.disabled === valid){
42125                 btn.setDisabled(!valid);
42126             }
42127         }
42128         this.fireEvent('clientvalidation', this, valid);
42129     }
42130     
42131     
42132     
42133     
42134     
42135     
42136     
42137     
42138 });
42139
42140
42141 // back compat
42142 Roo.Form = Roo.form.Form;
42143 /*
42144  * Based on:
42145  * Ext JS Library 1.1.1
42146  * Copyright(c) 2006-2007, Ext JS, LLC.
42147  *
42148  * Originally Released Under LGPL - original licence link has changed is not relivant.
42149  *
42150  * Fork - LGPL
42151  * <script type="text/javascript">
42152  */
42153  
42154  /**
42155  * @class Roo.form.Action
42156  * Internal Class used to handle form actions
42157  * @constructor
42158  * @param {Roo.form.BasicForm} el The form element or its id
42159  * @param {Object} config Configuration options
42160  */
42161  
42162  
42163 // define the action interface
42164 Roo.form.Action = function(form, options){
42165     this.form = form;
42166     this.options = options || {};
42167 };
42168 /**
42169  * Client Validation Failed
42170  * @const 
42171  */
42172 Roo.form.Action.CLIENT_INVALID = 'client';
42173 /**
42174  * Server Validation Failed
42175  * @const 
42176  */
42177  Roo.form.Action.SERVER_INVALID = 'server';
42178  /**
42179  * Connect to Server Failed
42180  * @const 
42181  */
42182 Roo.form.Action.CONNECT_FAILURE = 'connect';
42183 /**
42184  * Reading Data from Server Failed
42185  * @const 
42186  */
42187 Roo.form.Action.LOAD_FAILURE = 'load';
42188
42189 Roo.form.Action.prototype = {
42190     type : 'default',
42191     failureType : undefined,
42192     response : undefined,
42193     result : undefined,
42194
42195     // interface method
42196     run : function(options){
42197
42198     },
42199
42200     // interface method
42201     success : function(response){
42202
42203     },
42204
42205     // interface method
42206     handleResponse : function(response){
42207
42208     },
42209
42210     // default connection failure
42211     failure : function(response){
42212         
42213         this.response = response;
42214         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42215         this.form.afterAction(this, false);
42216     },
42217
42218     processResponse : function(response){
42219         this.response = response;
42220         if(!response.responseText){
42221             return true;
42222         }
42223         this.result = this.handleResponse(response);
42224         return this.result;
42225     },
42226
42227     // utility functions used internally
42228     getUrl : function(appendParams){
42229         var url = this.options.url || this.form.url || this.form.el.dom.action;
42230         if(appendParams){
42231             var p = this.getParams();
42232             if(p){
42233                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42234             }
42235         }
42236         return url;
42237     },
42238
42239     getMethod : function(){
42240         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42241     },
42242
42243     getParams : function(){
42244         var bp = this.form.baseParams;
42245         var p = this.options.params;
42246         if(p){
42247             if(typeof p == "object"){
42248                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42249             }else if(typeof p == 'string' && bp){
42250                 p += '&' + Roo.urlEncode(bp);
42251             }
42252         }else if(bp){
42253             p = Roo.urlEncode(bp);
42254         }
42255         return p;
42256     },
42257
42258     createCallback : function(){
42259         return {
42260             success: this.success,
42261             failure: this.failure,
42262             scope: this,
42263             timeout: (this.form.timeout*1000),
42264             upload: this.form.fileUpload ? this.success : undefined
42265         };
42266     }
42267 };
42268
42269 Roo.form.Action.Submit = function(form, options){
42270     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42271 };
42272
42273 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42274     type : 'submit',
42275
42276     haveProgress : false,
42277     uploadComplete : false,
42278     
42279     // uploadProgress indicator.
42280     uploadProgress : function()
42281     {
42282         if (!this.form.progressUrl) {
42283             return;
42284         }
42285         
42286         if (!this.haveProgress) {
42287             Roo.MessageBox.progress("Uploading", "Uploading");
42288         }
42289         if (this.uploadComplete) {
42290            Roo.MessageBox.hide();
42291            return;
42292         }
42293         
42294         this.haveProgress = true;
42295    
42296         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42297         
42298         var c = new Roo.data.Connection();
42299         c.request({
42300             url : this.form.progressUrl,
42301             params: {
42302                 id : uid
42303             },
42304             method: 'GET',
42305             success : function(req){
42306                //console.log(data);
42307                 var rdata = false;
42308                 var edata;
42309                 try  {
42310                    rdata = Roo.decode(req.responseText)
42311                 } catch (e) {
42312                     Roo.log("Invalid data from server..");
42313                     Roo.log(edata);
42314                     return;
42315                 }
42316                 if (!rdata || !rdata.success) {
42317                     Roo.log(rdata);
42318                     return;
42319                 }
42320                 var data = rdata.data;
42321                 
42322                 if (this.uploadComplete) {
42323                    Roo.MessageBox.hide();
42324                    return;
42325                 }
42326                    
42327                 if (data){
42328                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42329                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42330                     );
42331                 }
42332                 this.uploadProgress.defer(2000,this);
42333             },
42334        
42335             failure: function(data) {
42336                 Roo.log('progress url failed ');
42337                 Roo.log(data);
42338             },
42339             scope : this
42340         });
42341            
42342     },
42343     
42344     
42345     run : function()
42346     {
42347         // run get Values on the form, so it syncs any secondary forms.
42348         this.form.getValues();
42349         
42350         var o = this.options;
42351         var method = this.getMethod();
42352         var isPost = method == 'POST';
42353         if(o.clientValidation === false || this.form.isValid()){
42354             
42355             if (this.form.progressUrl) {
42356                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42357                     (new Date() * 1) + '' + Math.random());
42358                     
42359             } 
42360             
42361             
42362             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42363                 form:this.form.el.dom,
42364                 url:this.getUrl(!isPost),
42365                 method: method,
42366                 params:isPost ? this.getParams() : null,
42367                 isUpload: this.form.fileUpload
42368             }));
42369             
42370             this.uploadProgress();
42371
42372         }else if (o.clientValidation !== false){ // client validation failed
42373             this.failureType = Roo.form.Action.CLIENT_INVALID;
42374             this.form.afterAction(this, false);
42375         }
42376     },
42377
42378     success : function(response)
42379     {
42380         this.uploadComplete= true;
42381         if (this.haveProgress) {
42382             Roo.MessageBox.hide();
42383         }
42384         
42385         
42386         var result = this.processResponse(response);
42387         if(result === true || result.success){
42388             this.form.afterAction(this, true);
42389             return;
42390         }
42391         if(result.errors){
42392             this.form.markInvalid(result.errors);
42393             this.failureType = Roo.form.Action.SERVER_INVALID;
42394         }
42395         this.form.afterAction(this, false);
42396     },
42397     failure : function(response)
42398     {
42399         this.uploadComplete= true;
42400         if (this.haveProgress) {
42401             Roo.MessageBox.hide();
42402         }
42403         
42404         this.response = response;
42405         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42406         this.form.afterAction(this, false);
42407     },
42408     
42409     handleResponse : function(response){
42410         if(this.form.errorReader){
42411             var rs = this.form.errorReader.read(response);
42412             var errors = [];
42413             if(rs.records){
42414                 for(var i = 0, len = rs.records.length; i < len; i++) {
42415                     var r = rs.records[i];
42416                     errors[i] = r.data;
42417                 }
42418             }
42419             if(errors.length < 1){
42420                 errors = null;
42421             }
42422             return {
42423                 success : rs.success,
42424                 errors : errors
42425             };
42426         }
42427         var ret = false;
42428         try {
42429             ret = Roo.decode(response.responseText);
42430         } catch (e) {
42431             ret = {
42432                 success: false,
42433                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42434                 errors : []
42435             };
42436         }
42437         return ret;
42438         
42439     }
42440 });
42441
42442
42443 Roo.form.Action.Load = function(form, options){
42444     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42445     this.reader = this.form.reader;
42446 };
42447
42448 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42449     type : 'load',
42450
42451     run : function(){
42452         
42453         Roo.Ajax.request(Roo.apply(
42454                 this.createCallback(), {
42455                     method:this.getMethod(),
42456                     url:this.getUrl(false),
42457                     params:this.getParams()
42458         }));
42459     },
42460
42461     success : function(response){
42462         
42463         var result = this.processResponse(response);
42464         if(result === true || !result.success || !result.data){
42465             this.failureType = Roo.form.Action.LOAD_FAILURE;
42466             this.form.afterAction(this, false);
42467             return;
42468         }
42469         this.form.clearInvalid();
42470         this.form.setValues(result.data);
42471         this.form.afterAction(this, true);
42472     },
42473
42474     handleResponse : function(response){
42475         if(this.form.reader){
42476             var rs = this.form.reader.read(response);
42477             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42478             return {
42479                 success : rs.success,
42480                 data : data
42481             };
42482         }
42483         return Roo.decode(response.responseText);
42484     }
42485 });
42486
42487 Roo.form.Action.ACTION_TYPES = {
42488     'load' : Roo.form.Action.Load,
42489     'submit' : Roo.form.Action.Submit
42490 };/*
42491  * Based on:
42492  * Ext JS Library 1.1.1
42493  * Copyright(c) 2006-2007, Ext JS, LLC.
42494  *
42495  * Originally Released Under LGPL - original licence link has changed is not relivant.
42496  *
42497  * Fork - LGPL
42498  * <script type="text/javascript">
42499  */
42500  
42501 /**
42502  * @class Roo.form.Layout
42503  * @extends Roo.Component
42504  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42505  * @constructor
42506  * @param {Object} config Configuration options
42507  */
42508 Roo.form.Layout = function(config){
42509     var xitems = [];
42510     if (config.items) {
42511         xitems = config.items;
42512         delete config.items;
42513     }
42514     Roo.form.Layout.superclass.constructor.call(this, config);
42515     this.stack = [];
42516     Roo.each(xitems, this.addxtype, this);
42517      
42518 };
42519
42520 Roo.extend(Roo.form.Layout, Roo.Component, {
42521     /**
42522      * @cfg {String/Object} autoCreate
42523      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42524      */
42525     /**
42526      * @cfg {String/Object/Function} style
42527      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42528      * a function which returns such a specification.
42529      */
42530     /**
42531      * @cfg {String} labelAlign
42532      * Valid values are "left," "top" and "right" (defaults to "left")
42533      */
42534     /**
42535      * @cfg {Number} labelWidth
42536      * Fixed width in pixels of all field labels (defaults to undefined)
42537      */
42538     /**
42539      * @cfg {Boolean} clear
42540      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42541      */
42542     clear : true,
42543     /**
42544      * @cfg {String} labelSeparator
42545      * The separator to use after field labels (defaults to ':')
42546      */
42547     labelSeparator : ':',
42548     /**
42549      * @cfg {Boolean} hideLabels
42550      * True to suppress the display of field labels in this layout (defaults to false)
42551      */
42552     hideLabels : false,
42553
42554     // private
42555     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42556     
42557     isLayout : true,
42558     
42559     // private
42560     onRender : function(ct, position){
42561         if(this.el){ // from markup
42562             this.el = Roo.get(this.el);
42563         }else {  // generate
42564             var cfg = this.getAutoCreate();
42565             this.el = ct.createChild(cfg, position);
42566         }
42567         if(this.style){
42568             this.el.applyStyles(this.style);
42569         }
42570         if(this.labelAlign){
42571             this.el.addClass('x-form-label-'+this.labelAlign);
42572         }
42573         if(this.hideLabels){
42574             this.labelStyle = "display:none";
42575             this.elementStyle = "padding-left:0;";
42576         }else{
42577             if(typeof this.labelWidth == 'number'){
42578                 this.labelStyle = "width:"+this.labelWidth+"px;";
42579                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42580             }
42581             if(this.labelAlign == 'top'){
42582                 this.labelStyle = "width:auto;";
42583                 this.elementStyle = "padding-left:0;";
42584             }
42585         }
42586         var stack = this.stack;
42587         var slen = stack.length;
42588         if(slen > 0){
42589             if(!this.fieldTpl){
42590                 var t = new Roo.Template(
42591                     '<div class="x-form-item {5}">',
42592                         '<label for="{0}" style="{2}">{1}{4}</label>',
42593                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42594                         '</div>',
42595                     '</div><div class="x-form-clear-left"></div>'
42596                 );
42597                 t.disableFormats = true;
42598                 t.compile();
42599                 Roo.form.Layout.prototype.fieldTpl = t;
42600             }
42601             for(var i = 0; i < slen; i++) {
42602                 if(stack[i].isFormField){
42603                     this.renderField(stack[i]);
42604                 }else{
42605                     this.renderComponent(stack[i]);
42606                 }
42607             }
42608         }
42609         if(this.clear){
42610             this.el.createChild({cls:'x-form-clear'});
42611         }
42612     },
42613
42614     // private
42615     renderField : function(f){
42616         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42617                f.id, //0
42618                f.fieldLabel, //1
42619                f.labelStyle||this.labelStyle||'', //2
42620                this.elementStyle||'', //3
42621                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42622                f.itemCls||this.itemCls||''  //5
42623        ], true).getPrevSibling());
42624     },
42625
42626     // private
42627     renderComponent : function(c){
42628         c.render(c.isLayout ? this.el : this.el.createChild());    
42629     },
42630     /**
42631      * Adds a object form elements (using the xtype property as the factory method.)
42632      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42633      * @param {Object} config 
42634      */
42635     addxtype : function(o)
42636     {
42637         // create the lement.
42638         o.form = this.form;
42639         var fe = Roo.factory(o, Roo.form);
42640         this.form.allItems.push(fe);
42641         this.stack.push(fe);
42642         
42643         if (fe.isFormField) {
42644             this.form.items.add(fe);
42645         }
42646          
42647         return fe;
42648     }
42649 });
42650
42651 /**
42652  * @class Roo.form.Column
42653  * @extends Roo.form.Layout
42654  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42655  * @constructor
42656  * @param {Object} config Configuration options
42657  */
42658 Roo.form.Column = function(config){
42659     Roo.form.Column.superclass.constructor.call(this, config);
42660 };
42661
42662 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42663     /**
42664      * @cfg {Number/String} width
42665      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42666      */
42667     /**
42668      * @cfg {String/Object} autoCreate
42669      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42670      */
42671
42672     // private
42673     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42674
42675     // private
42676     onRender : function(ct, position){
42677         Roo.form.Column.superclass.onRender.call(this, ct, position);
42678         if(this.width){
42679             this.el.setWidth(this.width);
42680         }
42681     }
42682 });
42683
42684
42685 /**
42686  * @class Roo.form.Row
42687  * @extends Roo.form.Layout
42688  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42689  * @constructor
42690  * @param {Object} config Configuration options
42691  */
42692
42693  
42694 Roo.form.Row = function(config){
42695     Roo.form.Row.superclass.constructor.call(this, config);
42696 };
42697  
42698 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42699       /**
42700      * @cfg {Number/String} width
42701      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42702      */
42703     /**
42704      * @cfg {Number/String} height
42705      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42706      */
42707     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42708     
42709     padWidth : 20,
42710     // private
42711     onRender : function(ct, position){
42712         //console.log('row render');
42713         if(!this.rowTpl){
42714             var t = new Roo.Template(
42715                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42716                     '<label for="{0}" style="{2}">{1}{4}</label>',
42717                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42718                     '</div>',
42719                 '</div>'
42720             );
42721             t.disableFormats = true;
42722             t.compile();
42723             Roo.form.Layout.prototype.rowTpl = t;
42724         }
42725         this.fieldTpl = this.rowTpl;
42726         
42727         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42728         var labelWidth = 100;
42729         
42730         if ((this.labelAlign != 'top')) {
42731             if (typeof this.labelWidth == 'number') {
42732                 labelWidth = this.labelWidth
42733             }
42734             this.padWidth =  20 + labelWidth;
42735             
42736         }
42737         
42738         Roo.form.Column.superclass.onRender.call(this, ct, position);
42739         if(this.width){
42740             this.el.setWidth(this.width);
42741         }
42742         if(this.height){
42743             this.el.setHeight(this.height);
42744         }
42745     },
42746     
42747     // private
42748     renderField : function(f){
42749         f.fieldEl = this.fieldTpl.append(this.el, [
42750                f.id, f.fieldLabel,
42751                f.labelStyle||this.labelStyle||'',
42752                this.elementStyle||'',
42753                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42754                f.itemCls||this.itemCls||'',
42755                f.width ? f.width + this.padWidth : 160 + this.padWidth
42756        ],true);
42757     }
42758 });
42759  
42760
42761 /**
42762  * @class Roo.form.FieldSet
42763  * @extends Roo.form.Layout
42764  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42765  * @constructor
42766  * @param {Object} config Configuration options
42767  */
42768 Roo.form.FieldSet = function(config){
42769     Roo.form.FieldSet.superclass.constructor.call(this, config);
42770 };
42771
42772 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42773     /**
42774      * @cfg {String} legend
42775      * The text to display as the legend for the FieldSet (defaults to '')
42776      */
42777     /**
42778      * @cfg {String/Object} autoCreate
42779      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42780      */
42781
42782     // private
42783     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42784
42785     // private
42786     onRender : function(ct, position){
42787         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42788         if(this.legend){
42789             this.setLegend(this.legend);
42790         }
42791     },
42792
42793     // private
42794     setLegend : function(text){
42795         if(this.rendered){
42796             this.el.child('legend').update(text);
42797         }
42798     }
42799 });/*
42800  * Based on:
42801  * Ext JS Library 1.1.1
42802  * Copyright(c) 2006-2007, Ext JS, LLC.
42803  *
42804  * Originally Released Under LGPL - original licence link has changed is not relivant.
42805  *
42806  * Fork - LGPL
42807  * <script type="text/javascript">
42808  */
42809 /**
42810  * @class Roo.form.VTypes
42811  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42812  * @singleton
42813  */
42814 Roo.form.VTypes = function(){
42815     // closure these in so they are only created once.
42816     var alpha = /^[a-zA-Z_]+$/;
42817     var alphanum = /^[a-zA-Z0-9_]+$/;
42818     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42819     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42820
42821     // All these messages and functions are configurable
42822     return {
42823         /**
42824          * The function used to validate email addresses
42825          * @param {String} value The email address
42826          */
42827         'email' : function(v){
42828             return email.test(v);
42829         },
42830         /**
42831          * The error text to display when the email validation function returns false
42832          * @type String
42833          */
42834         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42835         /**
42836          * The keystroke filter mask to be applied on email input
42837          * @type RegExp
42838          */
42839         'emailMask' : /[a-z0-9_\.\-@]/i,
42840
42841         /**
42842          * The function used to validate URLs
42843          * @param {String} value The URL
42844          */
42845         'url' : function(v){
42846             return url.test(v);
42847         },
42848         /**
42849          * The error text to display when the url validation function returns false
42850          * @type String
42851          */
42852         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42853         
42854         /**
42855          * The function used to validate alpha values
42856          * @param {String} value The value
42857          */
42858         'alpha' : function(v){
42859             return alpha.test(v);
42860         },
42861         /**
42862          * The error text to display when the alpha validation function returns false
42863          * @type String
42864          */
42865         'alphaText' : 'This field should only contain letters and _',
42866         /**
42867          * The keystroke filter mask to be applied on alpha input
42868          * @type RegExp
42869          */
42870         'alphaMask' : /[a-z_]/i,
42871
42872         /**
42873          * The function used to validate alphanumeric values
42874          * @param {String} value The value
42875          */
42876         'alphanum' : function(v){
42877             return alphanum.test(v);
42878         },
42879         /**
42880          * The error text to display when the alphanumeric validation function returns false
42881          * @type String
42882          */
42883         'alphanumText' : 'This field should only contain letters, numbers and _',
42884         /**
42885          * The keystroke filter mask to be applied on alphanumeric input
42886          * @type RegExp
42887          */
42888         'alphanumMask' : /[a-z0-9_]/i
42889     };
42890 }();//<script type="text/javascript">
42891
42892 /**
42893  * @class Roo.form.FCKeditor
42894  * @extends Roo.form.TextArea
42895  * Wrapper around the FCKEditor http://www.fckeditor.net
42896  * @constructor
42897  * Creates a new FCKeditor
42898  * @param {Object} config Configuration options
42899  */
42900 Roo.form.FCKeditor = function(config){
42901     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42902     this.addEvents({
42903          /**
42904          * @event editorinit
42905          * Fired when the editor is initialized - you can add extra handlers here..
42906          * @param {FCKeditor} this
42907          * @param {Object} the FCK object.
42908          */
42909         editorinit : true
42910     });
42911     
42912     
42913 };
42914 Roo.form.FCKeditor.editors = { };
42915 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42916 {
42917     //defaultAutoCreate : {
42918     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42919     //},
42920     // private
42921     /**
42922      * @cfg {Object} fck options - see fck manual for details.
42923      */
42924     fckconfig : false,
42925     
42926     /**
42927      * @cfg {Object} fck toolbar set (Basic or Default)
42928      */
42929     toolbarSet : 'Basic',
42930     /**
42931      * @cfg {Object} fck BasePath
42932      */ 
42933     basePath : '/fckeditor/',
42934     
42935     
42936     frame : false,
42937     
42938     value : '',
42939     
42940    
42941     onRender : function(ct, position)
42942     {
42943         if(!this.el){
42944             this.defaultAutoCreate = {
42945                 tag: "textarea",
42946                 style:"width:300px;height:60px;",
42947                 autocomplete: "off"
42948             };
42949         }
42950         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42951         /*
42952         if(this.grow){
42953             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42954             if(this.preventScrollbars){
42955                 this.el.setStyle("overflow", "hidden");
42956             }
42957             this.el.setHeight(this.growMin);
42958         }
42959         */
42960         //console.log('onrender' + this.getId() );
42961         Roo.form.FCKeditor.editors[this.getId()] = this;
42962          
42963
42964         this.replaceTextarea() ;
42965         
42966     },
42967     
42968     getEditor : function() {
42969         return this.fckEditor;
42970     },
42971     /**
42972      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42973      * @param {Mixed} value The value to set
42974      */
42975     
42976     
42977     setValue : function(value)
42978     {
42979         //console.log('setValue: ' + value);
42980         
42981         if(typeof(value) == 'undefined') { // not sure why this is happending...
42982             return;
42983         }
42984         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42985         
42986         //if(!this.el || !this.getEditor()) {
42987         //    this.value = value;
42988             //this.setValue.defer(100,this,[value]);    
42989         //    return;
42990         //} 
42991         
42992         if(!this.getEditor()) {
42993             return;
42994         }
42995         
42996         this.getEditor().SetData(value);
42997         
42998         //
42999
43000     },
43001
43002     /**
43003      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43004      * @return {Mixed} value The field value
43005      */
43006     getValue : function()
43007     {
43008         
43009         if (this.frame && this.frame.dom.style.display == 'none') {
43010             return Roo.form.FCKeditor.superclass.getValue.call(this);
43011         }
43012         
43013         if(!this.el || !this.getEditor()) {
43014            
43015            // this.getValue.defer(100,this); 
43016             return this.value;
43017         }
43018        
43019         
43020         var value=this.getEditor().GetData();
43021         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43022         return Roo.form.FCKeditor.superclass.getValue.call(this);
43023         
43024
43025     },
43026
43027     /**
43028      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43029      * @return {Mixed} value The field value
43030      */
43031     getRawValue : function()
43032     {
43033         if (this.frame && this.frame.dom.style.display == 'none') {
43034             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43035         }
43036         
43037         if(!this.el || !this.getEditor()) {
43038             //this.getRawValue.defer(100,this); 
43039             return this.value;
43040             return;
43041         }
43042         
43043         
43044         
43045         var value=this.getEditor().GetData();
43046         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43047         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43048          
43049     },
43050     
43051     setSize : function(w,h) {
43052         
43053         
43054         
43055         //if (this.frame && this.frame.dom.style.display == 'none') {
43056         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43057         //    return;
43058         //}
43059         //if(!this.el || !this.getEditor()) {
43060         //    this.setSize.defer(100,this, [w,h]); 
43061         //    return;
43062         //}
43063         
43064         
43065         
43066         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43067         
43068         this.frame.dom.setAttribute('width', w);
43069         this.frame.dom.setAttribute('height', h);
43070         this.frame.setSize(w,h);
43071         
43072     },
43073     
43074     toggleSourceEdit : function(value) {
43075         
43076       
43077          
43078         this.el.dom.style.display = value ? '' : 'none';
43079         this.frame.dom.style.display = value ?  'none' : '';
43080         
43081     },
43082     
43083     
43084     focus: function(tag)
43085     {
43086         if (this.frame.dom.style.display == 'none') {
43087             return Roo.form.FCKeditor.superclass.focus.call(this);
43088         }
43089         if(!this.el || !this.getEditor()) {
43090             this.focus.defer(100,this, [tag]); 
43091             return;
43092         }
43093         
43094         
43095         
43096         
43097         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43098         this.getEditor().Focus();
43099         if (tgs.length) {
43100             if (!this.getEditor().Selection.GetSelection()) {
43101                 this.focus.defer(100,this, [tag]); 
43102                 return;
43103             }
43104             
43105             
43106             var r = this.getEditor().EditorDocument.createRange();
43107             r.setStart(tgs[0],0);
43108             r.setEnd(tgs[0],0);
43109             this.getEditor().Selection.GetSelection().removeAllRanges();
43110             this.getEditor().Selection.GetSelection().addRange(r);
43111             this.getEditor().Focus();
43112         }
43113         
43114     },
43115     
43116     
43117     
43118     replaceTextarea : function()
43119     {
43120         if ( document.getElementById( this.getId() + '___Frame' ) )
43121             return ;
43122         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43123         //{
43124             // We must check the elements firstly using the Id and then the name.
43125         var oTextarea = document.getElementById( this.getId() );
43126         
43127         var colElementsByName = document.getElementsByName( this.getId() ) ;
43128          
43129         oTextarea.style.display = 'none' ;
43130
43131         if ( oTextarea.tabIndex ) {            
43132             this.TabIndex = oTextarea.tabIndex ;
43133         }
43134         
43135         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43136         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43137         this.frame = Roo.get(this.getId() + '___Frame')
43138     },
43139     
43140     _getConfigHtml : function()
43141     {
43142         var sConfig = '' ;
43143
43144         for ( var o in this.fckconfig ) {
43145             sConfig += sConfig.length > 0  ? '&amp;' : '';
43146             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43147         }
43148
43149         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43150     },
43151     
43152     
43153     _getIFrameHtml : function()
43154     {
43155         var sFile = 'fckeditor.html' ;
43156         /* no idea what this is about..
43157         try
43158         {
43159             if ( (/fcksource=true/i).test( window.top.location.search ) )
43160                 sFile = 'fckeditor.original.html' ;
43161         }
43162         catch (e) { 
43163         */
43164
43165         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43166         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43167         
43168         
43169         var html = '<iframe id="' + this.getId() +
43170             '___Frame" src="' + sLink +
43171             '" width="' + this.width +
43172             '" height="' + this.height + '"' +
43173             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43174             ' frameborder="0" scrolling="no"></iframe>' ;
43175
43176         return html ;
43177     },
43178     
43179     _insertHtmlBefore : function( html, element )
43180     {
43181         if ( element.insertAdjacentHTML )       {
43182             // IE
43183             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43184         } else { // Gecko
43185             var oRange = document.createRange() ;
43186             oRange.setStartBefore( element ) ;
43187             var oFragment = oRange.createContextualFragment( html );
43188             element.parentNode.insertBefore( oFragment, element ) ;
43189         }
43190     }
43191     
43192     
43193   
43194     
43195     
43196     
43197     
43198
43199 });
43200
43201 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43202
43203 function FCKeditor_OnComplete(editorInstance){
43204     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43205     f.fckEditor = editorInstance;
43206     //console.log("loaded");
43207     f.fireEvent('editorinit', f, editorInstance);
43208
43209   
43210
43211  
43212
43213
43214
43215
43216
43217
43218
43219
43220
43221
43222
43223
43224
43225
43226
43227 //<script type="text/javascript">
43228 /**
43229  * @class Roo.form.GridField
43230  * @extends Roo.form.Field
43231  * Embed a grid (or editable grid into a form)
43232  * STATUS ALPHA
43233  * 
43234  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43235  * it needs 
43236  * xgrid.store = Roo.data.Store
43237  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43238  * xgrid.store.reader = Roo.data.JsonReader 
43239  * 
43240  * 
43241  * @constructor
43242  * Creates a new GridField
43243  * @param {Object} config Configuration options
43244  */
43245 Roo.form.GridField = function(config){
43246     Roo.form.GridField.superclass.constructor.call(this, config);
43247      
43248 };
43249
43250 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43251     /**
43252      * @cfg {Number} width  - used to restrict width of grid..
43253      */
43254     width : 100,
43255     /**
43256      * @cfg {Number} height - used to restrict height of grid..
43257      */
43258     height : 50,
43259      /**
43260      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43261          * 
43262          *}
43263      */
43264     xgrid : false, 
43265     /**
43266      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43267      * {tag: "input", type: "checkbox", autocomplete: "off"})
43268      */
43269    // defaultAutoCreate : { tag: 'div' },
43270     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43271     /**
43272      * @cfg {String} addTitle Text to include for adding a title.
43273      */
43274     addTitle : false,
43275     //
43276     onResize : function(){
43277         Roo.form.Field.superclass.onResize.apply(this, arguments);
43278     },
43279
43280     initEvents : function(){
43281         // Roo.form.Checkbox.superclass.initEvents.call(this);
43282         // has no events...
43283        
43284     },
43285
43286
43287     getResizeEl : function(){
43288         return this.wrap;
43289     },
43290
43291     getPositionEl : function(){
43292         return this.wrap;
43293     },
43294
43295     // private
43296     onRender : function(ct, position){
43297         
43298         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43299         var style = this.style;
43300         delete this.style;
43301         
43302         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43303         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43304         this.viewEl = this.wrap.createChild({ tag: 'div' });
43305         if (style) {
43306             this.viewEl.applyStyles(style);
43307         }
43308         if (this.width) {
43309             this.viewEl.setWidth(this.width);
43310         }
43311         if (this.height) {
43312             this.viewEl.setHeight(this.height);
43313         }
43314         //if(this.inputValue !== undefined){
43315         //this.setValue(this.value);
43316         
43317         
43318         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43319         
43320         
43321         this.grid.render();
43322         this.grid.getDataSource().on('remove', this.refreshValue, this);
43323         this.grid.getDataSource().on('update', this.refreshValue, this);
43324         this.grid.on('afteredit', this.refreshValue, this);
43325  
43326     },
43327      
43328     
43329     /**
43330      * Sets the value of the item. 
43331      * @param {String} either an object  or a string..
43332      */
43333     setValue : function(v){
43334         //this.value = v;
43335         v = v || []; // empty set..
43336         // this does not seem smart - it really only affects memoryproxy grids..
43337         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43338             var ds = this.grid.getDataSource();
43339             // assumes a json reader..
43340             var data = {}
43341             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43342             ds.loadData( data);
43343         }
43344         // clear selection so it does not get stale.
43345         if (this.grid.sm) { 
43346             this.grid.sm.clearSelections();
43347         }
43348         
43349         Roo.form.GridField.superclass.setValue.call(this, v);
43350         this.refreshValue();
43351         // should load data in the grid really....
43352     },
43353     
43354     // private
43355     refreshValue: function() {
43356          var val = [];
43357         this.grid.getDataSource().each(function(r) {
43358             val.push(r.data);
43359         });
43360         this.el.dom.value = Roo.encode(val);
43361     }
43362     
43363      
43364     
43365     
43366 });/*
43367  * Based on:
43368  * Ext JS Library 1.1.1
43369  * Copyright(c) 2006-2007, Ext JS, LLC.
43370  *
43371  * Originally Released Under LGPL - original licence link has changed is not relivant.
43372  *
43373  * Fork - LGPL
43374  * <script type="text/javascript">
43375  */
43376 /**
43377  * @class Roo.form.DisplayField
43378  * @extends Roo.form.Field
43379  * A generic Field to display non-editable data.
43380  * @constructor
43381  * Creates a new Display Field item.
43382  * @param {Object} config Configuration options
43383  */
43384 Roo.form.DisplayField = function(config){
43385     Roo.form.DisplayField.superclass.constructor.call(this, config);
43386     
43387 };
43388
43389 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43390     inputType:      'hidden',
43391     allowBlank:     true,
43392     readOnly:         true,
43393     
43394  
43395     /**
43396      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43397      */
43398     focusClass : undefined,
43399     /**
43400      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43401      */
43402     fieldClass: 'x-form-field',
43403     
43404      /**
43405      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43406      */
43407     valueRenderer: undefined,
43408     
43409     width: 100,
43410     /**
43411      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43412      * {tag: "input", type: "checkbox", autocomplete: "off"})
43413      */
43414      
43415  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43416
43417     onResize : function(){
43418         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43419         
43420     },
43421
43422     initEvents : function(){
43423         // Roo.form.Checkbox.superclass.initEvents.call(this);
43424         // has no events...
43425        
43426     },
43427
43428
43429     getResizeEl : function(){
43430         return this.wrap;
43431     },
43432
43433     getPositionEl : function(){
43434         return this.wrap;
43435     },
43436
43437     // private
43438     onRender : function(ct, position){
43439         
43440         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43441         //if(this.inputValue !== undefined){
43442         this.wrap = this.el.wrap();
43443         
43444         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43445         
43446         if (this.bodyStyle) {
43447             this.viewEl.applyStyles(this.bodyStyle);
43448         }
43449         //this.viewEl.setStyle('padding', '2px');
43450         
43451         this.setValue(this.value);
43452         
43453     },
43454 /*
43455     // private
43456     initValue : Roo.emptyFn,
43457
43458   */
43459
43460         // private
43461     onClick : function(){
43462         
43463     },
43464
43465     /**
43466      * Sets the checked state of the checkbox.
43467      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43468      */
43469     setValue : function(v){
43470         this.value = v;
43471         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43472         // this might be called before we have a dom element..
43473         if (!this.viewEl) {
43474             return;
43475         }
43476         this.viewEl.dom.innerHTML = html;
43477         Roo.form.DisplayField.superclass.setValue.call(this, v);
43478
43479     }
43480 });/*
43481  * 
43482  * Licence- LGPL
43483  * 
43484  */
43485
43486 /**
43487  * @class Roo.form.DayPicker
43488  * @extends Roo.form.Field
43489  * A Day picker show [M] [T] [W] ....
43490  * @constructor
43491  * Creates a new Day Picker
43492  * @param {Object} config Configuration options
43493  */
43494 Roo.form.DayPicker= function(config){
43495     Roo.form.DayPicker.superclass.constructor.call(this, config);
43496      
43497 };
43498
43499 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43500     /**
43501      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43502      */
43503     focusClass : undefined,
43504     /**
43505      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43506      */
43507     fieldClass: "x-form-field",
43508    
43509     /**
43510      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43511      * {tag: "input", type: "checkbox", autocomplete: "off"})
43512      */
43513     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43514     
43515    
43516     actionMode : 'viewEl', 
43517     //
43518     // private
43519  
43520     inputType : 'hidden',
43521     
43522      
43523     inputElement: false, // real input element?
43524     basedOn: false, // ????
43525     
43526     isFormField: true, // not sure where this is needed!!!!
43527
43528     onResize : function(){
43529         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43530         if(!this.boxLabel){
43531             this.el.alignTo(this.wrap, 'c-c');
43532         }
43533     },
43534
43535     initEvents : function(){
43536         Roo.form.Checkbox.superclass.initEvents.call(this);
43537         this.el.on("click", this.onClick,  this);
43538         this.el.on("change", this.onClick,  this);
43539     },
43540
43541
43542     getResizeEl : function(){
43543         return this.wrap;
43544     },
43545
43546     getPositionEl : function(){
43547         return this.wrap;
43548     },
43549
43550     
43551     // private
43552     onRender : function(ct, position){
43553         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43554        
43555         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43556         
43557         var r1 = '<table><tr>';
43558         var r2 = '<tr class="x-form-daypick-icons">';
43559         for (var i=0; i < 7; i++) {
43560             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43561             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43562         }
43563         
43564         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43565         viewEl.select('img').on('click', this.onClick, this);
43566         this.viewEl = viewEl;   
43567         
43568         
43569         // this will not work on Chrome!!!
43570         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43571         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43572         
43573         
43574           
43575
43576     },
43577
43578     // private
43579     initValue : Roo.emptyFn,
43580
43581     /**
43582      * Returns the checked state of the checkbox.
43583      * @return {Boolean} True if checked, else false
43584      */
43585     getValue : function(){
43586         return this.el.dom.value;
43587         
43588     },
43589
43590         // private
43591     onClick : function(e){ 
43592         //this.setChecked(!this.checked);
43593         Roo.get(e.target).toggleClass('x-menu-item-checked');
43594         this.refreshValue();
43595         //if(this.el.dom.checked != this.checked){
43596         //    this.setValue(this.el.dom.checked);
43597        // }
43598     },
43599     
43600     // private
43601     refreshValue : function()
43602     {
43603         var val = '';
43604         this.viewEl.select('img',true).each(function(e,i,n)  {
43605             val += e.is(".x-menu-item-checked") ? String(n) : '';
43606         });
43607         this.setValue(val, true);
43608     },
43609
43610     /**
43611      * Sets the checked state of the checkbox.
43612      * On is always based on a string comparison between inputValue and the param.
43613      * @param {Boolean/String} value - the value to set 
43614      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43615      */
43616     setValue : function(v,suppressEvent){
43617         if (!this.el.dom) {
43618             return;
43619         }
43620         var old = this.el.dom.value ;
43621         this.el.dom.value = v;
43622         if (suppressEvent) {
43623             return ;
43624         }
43625          
43626         // update display..
43627         this.viewEl.select('img',true).each(function(e,i,n)  {
43628             
43629             var on = e.is(".x-menu-item-checked");
43630             var newv = v.indexOf(String(n)) > -1;
43631             if (on != newv) {
43632                 e.toggleClass('x-menu-item-checked');
43633             }
43634             
43635         });
43636         
43637         
43638         this.fireEvent('change', this, v, old);
43639         
43640         
43641     },
43642    
43643     // handle setting of hidden value by some other method!!?!?
43644     setFromHidden: function()
43645     {
43646         if(!this.el){
43647             return;
43648         }
43649         //console.log("SET FROM HIDDEN");
43650         //alert('setFrom hidden');
43651         this.setValue(this.el.dom.value);
43652     },
43653     
43654     onDestroy : function()
43655     {
43656         if(this.viewEl){
43657             Roo.get(this.viewEl).remove();
43658         }
43659          
43660         Roo.form.DayPicker.superclass.onDestroy.call(this);
43661     }
43662
43663 });/*
43664  * RooJS Library 1.1.1
43665  * Copyright(c) 2008-2011  Alan Knowles
43666  *
43667  * License - LGPL
43668  */
43669  
43670
43671 /**
43672  * @class Roo.form.ComboCheck
43673  * @extends Roo.form.ComboBox
43674  * A combobox for multiple select items.
43675  *
43676  * FIXME - could do with a reset button..
43677  * 
43678  * @constructor
43679  * Create a new ComboCheck
43680  * @param {Object} config Configuration options
43681  */
43682 Roo.form.ComboCheck = function(config){
43683     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43684     // should verify some data...
43685     // like
43686     // hiddenName = required..
43687     // displayField = required
43688     // valudField == required
43689     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43690     var _t = this;
43691     Roo.each(req, function(e) {
43692         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43693             throw "Roo.form.ComboCheck : missing value for: " + e;
43694         }
43695     });
43696     
43697     
43698 };
43699
43700 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43701      
43702      
43703     editable : false,
43704      
43705     selectedClass: 'x-menu-item-checked', 
43706     
43707     // private
43708     onRender : function(ct, position){
43709         var _t = this;
43710         
43711         
43712         
43713         if(!this.tpl){
43714             var cls = 'x-combo-list';
43715
43716             
43717             this.tpl =  new Roo.Template({
43718                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43719                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43720                    '<span>{' + this.displayField + '}</span>' +
43721                     '</div>' 
43722                 
43723             });
43724         }
43725  
43726         
43727         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43728         this.view.singleSelect = false;
43729         this.view.multiSelect = true;
43730         this.view.toggleSelect = true;
43731         this.pageTb.add(new Roo.Toolbar.Fill(), {
43732             
43733             text: 'Done',
43734             handler: function()
43735             {
43736                 _t.collapse();
43737             }
43738         });
43739     },
43740     
43741     onViewOver : function(e, t){
43742         // do nothing...
43743         return;
43744         
43745     },
43746     
43747     onViewClick : function(doFocus,index){
43748         return;
43749         
43750     },
43751     select: function () {
43752         //Roo.log("SELECT CALLED");
43753     },
43754      
43755     selectByValue : function(xv, scrollIntoView){
43756         var ar = this.getValueArray();
43757         var sels = [];
43758         
43759         Roo.each(ar, function(v) {
43760             if(v === undefined || v === null){
43761                 return;
43762             }
43763             var r = this.findRecord(this.valueField, v);
43764             if(r){
43765                 sels.push(this.store.indexOf(r))
43766                 
43767             }
43768         },this);
43769         this.view.select(sels);
43770         return false;
43771     },
43772     
43773     
43774     
43775     onSelect : function(record, index){
43776        // Roo.log("onselect Called");
43777        // this is only called by the clear button now..
43778         this.view.clearSelections();
43779         this.setValue('[]');
43780         if (this.value != this.valueBefore) {
43781             this.fireEvent('change', this, this.value, this.valueBefore);
43782         }
43783     },
43784     getValueArray : function()
43785     {
43786         var ar = [] ;
43787         
43788         try {
43789             //Roo.log(this.value);
43790             if (typeof(this.value) == 'undefined') {
43791                 return [];
43792             }
43793             var ar = Roo.decode(this.value);
43794             return  ar instanceof Array ? ar : []; //?? valid?
43795             
43796         } catch(e) {
43797             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43798             return [];
43799         }
43800          
43801     },
43802     expand : function ()
43803     {
43804         Roo.form.ComboCheck.superclass.expand.call(this);
43805         this.valueBefore = this.value;
43806         
43807
43808     },
43809     
43810     collapse : function(){
43811         Roo.form.ComboCheck.superclass.collapse.call(this);
43812         var sl = this.view.getSelectedIndexes();
43813         var st = this.store;
43814         var nv = [];
43815         var tv = [];
43816         var r;
43817         Roo.each(sl, function(i) {
43818             r = st.getAt(i);
43819             nv.push(r.get(this.valueField));
43820         },this);
43821         this.setValue(Roo.encode(nv));
43822         if (this.value != this.valueBefore) {
43823
43824             this.fireEvent('change', this, this.value, this.valueBefore);
43825         }
43826         
43827     },
43828     
43829     setValue : function(v){
43830         // Roo.log(v);
43831         this.value = v;
43832         
43833         var vals = this.getValueArray();
43834         var tv = [];
43835         Roo.each(vals, function(k) {
43836             var r = this.findRecord(this.valueField, k);
43837             if(r){
43838                 tv.push(r.data[this.displayField]);
43839             }else if(this.valueNotFoundText !== undefined){
43840                 tv.push( this.valueNotFoundText );
43841             }
43842         },this);
43843        // Roo.log(tv);
43844         
43845         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43846         this.hiddenField.value = v;
43847         this.value = v;
43848     }
43849     
43850 });//<script type="text/javasscript">
43851  
43852
43853 /**
43854  * @class Roo.DDView
43855  * A DnD enabled version of Roo.View.
43856  * @param {Element/String} container The Element in which to create the View.
43857  * @param {String} tpl The template string used to create the markup for each element of the View
43858  * @param {Object} config The configuration properties. These include all the config options of
43859  * {@link Roo.View} plus some specific to this class.<br>
43860  * <p>
43861  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43862  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43863  * <p>
43864  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43865 .x-view-drag-insert-above {
43866         border-top:1px dotted #3366cc;
43867 }
43868 .x-view-drag-insert-below {
43869         border-bottom:1px dotted #3366cc;
43870 }
43871 </code></pre>
43872  * 
43873  */
43874  
43875 Roo.DDView = function(container, tpl, config) {
43876     Roo.DDView.superclass.constructor.apply(this, arguments);
43877     this.getEl().setStyle("outline", "0px none");
43878     this.getEl().unselectable();
43879     if (this.dragGroup) {
43880                 this.setDraggable(this.dragGroup.split(","));
43881     }
43882     if (this.dropGroup) {
43883                 this.setDroppable(this.dropGroup.split(","));
43884     }
43885     if (this.deletable) {
43886         this.setDeletable();
43887     }
43888     this.isDirtyFlag = false;
43889         this.addEvents({
43890                 "drop" : true
43891         });
43892 };
43893
43894 Roo.extend(Roo.DDView, Roo.View, {
43895 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43896 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43897 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43898 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43899
43900         isFormField: true,
43901
43902         reset: Roo.emptyFn,
43903         
43904         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43905
43906         validate: function() {
43907                 return true;
43908         },
43909         
43910         destroy: function() {
43911                 this.purgeListeners();
43912                 this.getEl.removeAllListeners();
43913                 this.getEl().remove();
43914                 if (this.dragZone) {
43915                         if (this.dragZone.destroy) {
43916                                 this.dragZone.destroy();
43917                         }
43918                 }
43919                 if (this.dropZone) {
43920                         if (this.dropZone.destroy) {
43921                                 this.dropZone.destroy();
43922                         }
43923                 }
43924         },
43925
43926 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43927         getName: function() {
43928                 return this.name;
43929         },
43930
43931 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43932         setValue: function(v) {
43933                 if (!this.store) {
43934                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43935                 }
43936                 var data = {};
43937                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43938                 this.store.proxy = new Roo.data.MemoryProxy(data);
43939                 this.store.load();
43940         },
43941
43942 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43943         getValue: function() {
43944                 var result = '(';
43945                 this.store.each(function(rec) {
43946                         result += rec.id + ',';
43947                 });
43948                 return result.substr(0, result.length - 1) + ')';
43949         },
43950         
43951         getIds: function() {
43952                 var i = 0, result = new Array(this.store.getCount());
43953                 this.store.each(function(rec) {
43954                         result[i++] = rec.id;
43955                 });
43956                 return result;
43957         },
43958         
43959         isDirty: function() {
43960                 return this.isDirtyFlag;
43961         },
43962
43963 /**
43964  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43965  *      whole Element becomes the target, and this causes the drop gesture to append.
43966  */
43967     getTargetFromEvent : function(e) {
43968                 var target = e.getTarget();
43969                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43970                 target = target.parentNode;
43971                 }
43972                 if (!target) {
43973                         target = this.el.dom.lastChild || this.el.dom;
43974                 }
43975                 return target;
43976     },
43977
43978 /**
43979  *      Create the drag data which consists of an object which has the property "ddel" as
43980  *      the drag proxy element. 
43981  */
43982     getDragData : function(e) {
43983         var target = this.findItemFromChild(e.getTarget());
43984                 if(target) {
43985                         this.handleSelection(e);
43986                         var selNodes = this.getSelectedNodes();
43987             var dragData = {
43988                 source: this,
43989                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43990                 nodes: selNodes,
43991                 records: []
43992                         };
43993                         var selectedIndices = this.getSelectedIndexes();
43994                         for (var i = 0; i < selectedIndices.length; i++) {
43995                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43996                         }
43997                         if (selNodes.length == 1) {
43998                                 dragData.ddel = target.cloneNode(true); // the div element
43999                         } else {
44000                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44001                                 div.className = 'multi-proxy';
44002                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44003                                         div.appendChild(selNodes[i].cloneNode(true));
44004                                 }
44005                                 dragData.ddel = div;
44006                         }
44007             //console.log(dragData)
44008             //console.log(dragData.ddel.innerHTML)
44009                         return dragData;
44010                 }
44011         //console.log('nodragData')
44012                 return false;
44013     },
44014     
44015 /**     Specify to which ddGroup items in this DDView may be dragged. */
44016     setDraggable: function(ddGroup) {
44017         if (ddGroup instanceof Array) {
44018                 Roo.each(ddGroup, this.setDraggable, this);
44019                 return;
44020         }
44021         if (this.dragZone) {
44022                 this.dragZone.addToGroup(ddGroup);
44023         } else {
44024                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44025                                 containerScroll: true,
44026                                 ddGroup: ddGroup 
44027
44028                         });
44029 //                      Draggability implies selection. DragZone's mousedown selects the element.
44030                         if (!this.multiSelect) { this.singleSelect = true; }
44031
44032 //                      Wire the DragZone's handlers up to methods in *this*
44033                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44034                 }
44035     },
44036
44037 /**     Specify from which ddGroup this DDView accepts drops. */
44038     setDroppable: function(ddGroup) {
44039         if (ddGroup instanceof Array) {
44040                 Roo.each(ddGroup, this.setDroppable, this);
44041                 return;
44042         }
44043         if (this.dropZone) {
44044                 this.dropZone.addToGroup(ddGroup);
44045         } else {
44046                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44047                                 containerScroll: true,
44048                                 ddGroup: ddGroup
44049                         });
44050
44051 //                      Wire the DropZone's handlers up to methods in *this*
44052                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44053                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44054                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44055                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44056                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44057                 }
44058     },
44059
44060 /**     Decide whether to drop above or below a View node. */
44061     getDropPoint : function(e, n, dd){
44062         if (n == this.el.dom) { return "above"; }
44063                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44064                 var c = t + (b - t) / 2;
44065                 var y = Roo.lib.Event.getPageY(e);
44066                 if(y <= c) {
44067                         return "above";
44068                 }else{
44069                         return "below";
44070                 }
44071     },
44072
44073     onNodeEnter : function(n, dd, e, data){
44074                 return false;
44075     },
44076     
44077     onNodeOver : function(n, dd, e, data){
44078                 var pt = this.getDropPoint(e, n, dd);
44079                 // set the insert point style on the target node
44080                 var dragElClass = this.dropNotAllowed;
44081                 if (pt) {
44082                         var targetElClass;
44083                         if (pt == "above"){
44084                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44085                                 targetElClass = "x-view-drag-insert-above";
44086                         } else {
44087                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44088                                 targetElClass = "x-view-drag-insert-below";
44089                         }
44090                         if (this.lastInsertClass != targetElClass){
44091                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44092                                 this.lastInsertClass = targetElClass;
44093                         }
44094                 }
44095                 return dragElClass;
44096         },
44097
44098     onNodeOut : function(n, dd, e, data){
44099                 this.removeDropIndicators(n);
44100     },
44101
44102     onNodeDrop : function(n, dd, e, data){
44103         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44104                 return false;
44105         }
44106         var pt = this.getDropPoint(e, n, dd);
44107                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44108                 if (pt == "below") { insertAt++; }
44109                 for (var i = 0; i < data.records.length; i++) {
44110                         var r = data.records[i];
44111                         var dup = this.store.getById(r.id);
44112                         if (dup && (dd != this.dragZone)) {
44113                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44114                         } else {
44115                                 if (data.copy) {
44116                                         this.store.insert(insertAt++, r.copy());
44117                                 } else {
44118                                         data.source.isDirtyFlag = true;
44119                                         r.store.remove(r);
44120                                         this.store.insert(insertAt++, r);
44121                                 }
44122                                 this.isDirtyFlag = true;
44123                         }
44124                 }
44125                 this.dragZone.cachedTarget = null;
44126                 return true;
44127     },
44128
44129     removeDropIndicators : function(n){
44130                 if(n){
44131                         Roo.fly(n).removeClass([
44132                                 "x-view-drag-insert-above",
44133                                 "x-view-drag-insert-below"]);
44134                         this.lastInsertClass = "_noclass";
44135                 }
44136     },
44137
44138 /**
44139  *      Utility method. Add a delete option to the DDView's context menu.
44140  *      @param {String} imageUrl The URL of the "delete" icon image.
44141  */
44142         setDeletable: function(imageUrl) {
44143                 if (!this.singleSelect && !this.multiSelect) {
44144                         this.singleSelect = true;
44145                 }
44146                 var c = this.getContextMenu();
44147                 this.contextMenu.on("itemclick", function(item) {
44148                         switch (item.id) {
44149                                 case "delete":
44150                                         this.remove(this.getSelectedIndexes());
44151                                         break;
44152                         }
44153                 }, this);
44154                 this.contextMenu.add({
44155                         icon: imageUrl,
44156                         id: "delete",
44157                         text: 'Delete'
44158                 });
44159         },
44160         
44161 /**     Return the context menu for this DDView. */
44162         getContextMenu: function() {
44163                 if (!this.contextMenu) {
44164 //                      Create the View's context menu
44165                         this.contextMenu = new Roo.menu.Menu({
44166                                 id: this.id + "-contextmenu"
44167                         });
44168                         this.el.on("contextmenu", this.showContextMenu, this);
44169                 }
44170                 return this.contextMenu;
44171         },
44172         
44173         disableContextMenu: function() {
44174                 if (this.contextMenu) {
44175                         this.el.un("contextmenu", this.showContextMenu, this);
44176                 }
44177         },
44178
44179         showContextMenu: function(e, item) {
44180         item = this.findItemFromChild(e.getTarget());
44181                 if (item) {
44182                         e.stopEvent();
44183                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44184                         this.contextMenu.showAt(e.getXY());
44185             }
44186     },
44187
44188 /**
44189  *      Remove {@link Roo.data.Record}s at the specified indices.
44190  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44191  */
44192     remove: function(selectedIndices) {
44193                 selectedIndices = [].concat(selectedIndices);
44194                 for (var i = 0; i < selectedIndices.length; i++) {
44195                         var rec = this.store.getAt(selectedIndices[i]);
44196                         this.store.remove(rec);
44197                 }
44198     },
44199
44200 /**
44201  *      Double click fires the event, but also, if this is draggable, and there is only one other
44202  *      related DropZone, it transfers the selected node.
44203  */
44204     onDblClick : function(e){
44205         var item = this.findItemFromChild(e.getTarget());
44206         if(item){
44207             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44208                 return false;
44209             }
44210             if (this.dragGroup) {
44211                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44212                     while (targets.indexOf(this.dropZone) > -1) {
44213                             targets.remove(this.dropZone);
44214                                 }
44215                     if (targets.length == 1) {
44216                                         this.dragZone.cachedTarget = null;
44217                         var el = Roo.get(targets[0].getEl());
44218                         var box = el.getBox(true);
44219                         targets[0].onNodeDrop(el.dom, {
44220                                 target: el.dom,
44221                                 xy: [box.x, box.y + box.height - 1]
44222                         }, null, this.getDragData(e));
44223                     }
44224                 }
44225         }
44226     },
44227     
44228     handleSelection: function(e) {
44229                 this.dragZone.cachedTarget = null;
44230         var item = this.findItemFromChild(e.getTarget());
44231         if (!item) {
44232                 this.clearSelections(true);
44233                 return;
44234         }
44235                 if (item && (this.multiSelect || this.singleSelect)){
44236                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44237                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44238                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44239                                 this.unselect(item);
44240                         } else {
44241                                 this.select(item, this.multiSelect && e.ctrlKey);
44242                                 this.lastSelection = item;
44243                         }
44244                 }
44245     },
44246
44247     onItemClick : function(item, index, e){
44248                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44249                         return false;
44250                 }
44251                 return true;
44252     },
44253
44254     unselect : function(nodeInfo, suppressEvent){
44255                 var node = this.getNode(nodeInfo);
44256                 if(node && this.isSelected(node)){
44257                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44258                                 Roo.fly(node).removeClass(this.selectedClass);
44259                                 this.selections.remove(node);
44260                                 if(!suppressEvent){
44261                                         this.fireEvent("selectionchange", this, this.selections);
44262                                 }
44263                         }
44264                 }
44265     }
44266 });
44267 /*
44268  * Based on:
44269  * Ext JS Library 1.1.1
44270  * Copyright(c) 2006-2007, Ext JS, LLC.
44271  *
44272  * Originally Released Under LGPL - original licence link has changed is not relivant.
44273  *
44274  * Fork - LGPL
44275  * <script type="text/javascript">
44276  */
44277  
44278 /**
44279  * @class Roo.LayoutManager
44280  * @extends Roo.util.Observable
44281  * Base class for layout managers.
44282  */
44283 Roo.LayoutManager = function(container, config){
44284     Roo.LayoutManager.superclass.constructor.call(this);
44285     this.el = Roo.get(container);
44286     // ie scrollbar fix
44287     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44288         document.body.scroll = "no";
44289     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44290         this.el.position('relative');
44291     }
44292     this.id = this.el.id;
44293     this.el.addClass("x-layout-container");
44294     /** false to disable window resize monitoring @type Boolean */
44295     this.monitorWindowResize = true;
44296     this.regions = {};
44297     this.addEvents({
44298         /**
44299          * @event layout
44300          * Fires when a layout is performed. 
44301          * @param {Roo.LayoutManager} this
44302          */
44303         "layout" : true,
44304         /**
44305          * @event regionresized
44306          * Fires when the user resizes a region. 
44307          * @param {Roo.LayoutRegion} region The resized region
44308          * @param {Number} newSize The new size (width for east/west, height for north/south)
44309          */
44310         "regionresized" : true,
44311         /**
44312          * @event regioncollapsed
44313          * Fires when a region is collapsed. 
44314          * @param {Roo.LayoutRegion} region The collapsed region
44315          */
44316         "regioncollapsed" : true,
44317         /**
44318          * @event regionexpanded
44319          * Fires when a region is expanded.  
44320          * @param {Roo.LayoutRegion} region The expanded region
44321          */
44322         "regionexpanded" : true
44323     });
44324     this.updating = false;
44325     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44326 };
44327
44328 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44329     /**
44330      * Returns true if this layout is currently being updated
44331      * @return {Boolean}
44332      */
44333     isUpdating : function(){
44334         return this.updating; 
44335     },
44336     
44337     /**
44338      * Suspend the LayoutManager from doing auto-layouts while
44339      * making multiple add or remove calls
44340      */
44341     beginUpdate : function(){
44342         this.updating = true;    
44343     },
44344     
44345     /**
44346      * Restore auto-layouts and optionally disable the manager from performing a layout
44347      * @param {Boolean} noLayout true to disable a layout update 
44348      */
44349     endUpdate : function(noLayout){
44350         this.updating = false;
44351         if(!noLayout){
44352             this.layout();
44353         }    
44354     },
44355     
44356     layout: function(){
44357         
44358     },
44359     
44360     onRegionResized : function(region, newSize){
44361         this.fireEvent("regionresized", region, newSize);
44362         this.layout();
44363     },
44364     
44365     onRegionCollapsed : function(region){
44366         this.fireEvent("regioncollapsed", region);
44367     },
44368     
44369     onRegionExpanded : function(region){
44370         this.fireEvent("regionexpanded", region);
44371     },
44372         
44373     /**
44374      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44375      * performs box-model adjustments.
44376      * @return {Object} The size as an object {width: (the width), height: (the height)}
44377      */
44378     getViewSize : function(){
44379         var size;
44380         if(this.el.dom != document.body){
44381             size = this.el.getSize();
44382         }else{
44383             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44384         }
44385         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44386         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44387         return size;
44388     },
44389     
44390     /**
44391      * Returns the Element this layout is bound to.
44392      * @return {Roo.Element}
44393      */
44394     getEl : function(){
44395         return this.el;
44396     },
44397     
44398     /**
44399      * Returns the specified region.
44400      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44401      * @return {Roo.LayoutRegion}
44402      */
44403     getRegion : function(target){
44404         return this.regions[target.toLowerCase()];
44405     },
44406     
44407     onWindowResize : function(){
44408         if(this.monitorWindowResize){
44409             this.layout();
44410         }
44411     }
44412 });/*
44413  * Based on:
44414  * Ext JS Library 1.1.1
44415  * Copyright(c) 2006-2007, Ext JS, LLC.
44416  *
44417  * Originally Released Under LGPL - original licence link has changed is not relivant.
44418  *
44419  * Fork - LGPL
44420  * <script type="text/javascript">
44421  */
44422 /**
44423  * @class Roo.BorderLayout
44424  * @extends Roo.LayoutManager
44425  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44426  * please see: <br><br>
44427  * <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>
44428  * <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>
44429  * Example:
44430  <pre><code>
44431  var layout = new Roo.BorderLayout(document.body, {
44432     north: {
44433         initialSize: 25,
44434         titlebar: false
44435     },
44436     west: {
44437         split:true,
44438         initialSize: 200,
44439         minSize: 175,
44440         maxSize: 400,
44441         titlebar: true,
44442         collapsible: true
44443     },
44444     east: {
44445         split:true,
44446         initialSize: 202,
44447         minSize: 175,
44448         maxSize: 400,
44449         titlebar: true,
44450         collapsible: true
44451     },
44452     south: {
44453         split:true,
44454         initialSize: 100,
44455         minSize: 100,
44456         maxSize: 200,
44457         titlebar: true,
44458         collapsible: true
44459     },
44460     center: {
44461         titlebar: true,
44462         autoScroll:true,
44463         resizeTabs: true,
44464         minTabWidth: 50,
44465         preferredTabWidth: 150
44466     }
44467 });
44468
44469 // shorthand
44470 var CP = Roo.ContentPanel;
44471
44472 layout.beginUpdate();
44473 layout.add("north", new CP("north", "North"));
44474 layout.add("south", new CP("south", {title: "South", closable: true}));
44475 layout.add("west", new CP("west", {title: "West"}));
44476 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44477 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44478 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44479 layout.getRegion("center").showPanel("center1");
44480 layout.endUpdate();
44481 </code></pre>
44482
44483 <b>The container the layout is rendered into can be either the body element or any other element.
44484 If it is not the body element, the container needs to either be an absolute positioned element,
44485 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44486 the container size if it is not the body element.</b>
44487
44488 * @constructor
44489 * Create a new BorderLayout
44490 * @param {String/HTMLElement/Element} container The container this layout is bound to
44491 * @param {Object} config Configuration options
44492  */
44493 Roo.BorderLayout = function(container, config){
44494     config = config || {};
44495     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44496     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44497     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44498         var target = this.factory.validRegions[i];
44499         if(config[target]){
44500             this.addRegion(target, config[target]);
44501         }
44502     }
44503 };
44504
44505 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44506     /**
44507      * Creates and adds a new region if it doesn't already exist.
44508      * @param {String} target The target region key (north, south, east, west or center).
44509      * @param {Object} config The regions config object
44510      * @return {BorderLayoutRegion} The new region
44511      */
44512     addRegion : function(target, config){
44513         if(!this.regions[target]){
44514             var r = this.factory.create(target, this, config);
44515             this.bindRegion(target, r);
44516         }
44517         return this.regions[target];
44518     },
44519
44520     // private (kinda)
44521     bindRegion : function(name, r){
44522         this.regions[name] = r;
44523         r.on("visibilitychange", this.layout, this);
44524         r.on("paneladded", this.layout, this);
44525         r.on("panelremoved", this.layout, this);
44526         r.on("invalidated", this.layout, this);
44527         r.on("resized", this.onRegionResized, this);
44528         r.on("collapsed", this.onRegionCollapsed, this);
44529         r.on("expanded", this.onRegionExpanded, this);
44530     },
44531
44532     /**
44533      * Performs a layout update.
44534      */
44535     layout : function(){
44536         if(this.updating) return;
44537         var size = this.getViewSize();
44538         var w = size.width;
44539         var h = size.height;
44540         var centerW = w;
44541         var centerH = h;
44542         var centerY = 0;
44543         var centerX = 0;
44544         //var x = 0, y = 0;
44545
44546         var rs = this.regions;
44547         var north = rs["north"];
44548         var south = rs["south"]; 
44549         var west = rs["west"];
44550         var east = rs["east"];
44551         var center = rs["center"];
44552         //if(this.hideOnLayout){ // not supported anymore
44553             //c.el.setStyle("display", "none");
44554         //}
44555         if(north && north.isVisible()){
44556             var b = north.getBox();
44557             var m = north.getMargins();
44558             b.width = w - (m.left+m.right);
44559             b.x = m.left;
44560             b.y = m.top;
44561             centerY = b.height + b.y + m.bottom;
44562             centerH -= centerY;
44563             north.updateBox(this.safeBox(b));
44564         }
44565         if(south && south.isVisible()){
44566             var b = south.getBox();
44567             var m = south.getMargins();
44568             b.width = w - (m.left+m.right);
44569             b.x = m.left;
44570             var totalHeight = (b.height + m.top + m.bottom);
44571             b.y = h - totalHeight + m.top;
44572             centerH -= totalHeight;
44573             south.updateBox(this.safeBox(b));
44574         }
44575         if(west && west.isVisible()){
44576             var b = west.getBox();
44577             var m = west.getMargins();
44578             b.height = centerH - (m.top+m.bottom);
44579             b.x = m.left;
44580             b.y = centerY + m.top;
44581             var totalWidth = (b.width + m.left + m.right);
44582             centerX += totalWidth;
44583             centerW -= totalWidth;
44584             west.updateBox(this.safeBox(b));
44585         }
44586         if(east && east.isVisible()){
44587             var b = east.getBox();
44588             var m = east.getMargins();
44589             b.height = centerH - (m.top+m.bottom);
44590             var totalWidth = (b.width + m.left + m.right);
44591             b.x = w - totalWidth + m.left;
44592             b.y = centerY + m.top;
44593             centerW -= totalWidth;
44594             east.updateBox(this.safeBox(b));
44595         }
44596         if(center){
44597             var m = center.getMargins();
44598             var centerBox = {
44599                 x: centerX + m.left,
44600                 y: centerY + m.top,
44601                 width: centerW - (m.left+m.right),
44602                 height: centerH - (m.top+m.bottom)
44603             };
44604             //if(this.hideOnLayout){
44605                 //center.el.setStyle("display", "block");
44606             //}
44607             center.updateBox(this.safeBox(centerBox));
44608         }
44609         this.el.repaint();
44610         this.fireEvent("layout", this);
44611     },
44612
44613     // private
44614     safeBox : function(box){
44615         box.width = Math.max(0, box.width);
44616         box.height = Math.max(0, box.height);
44617         return box;
44618     },
44619
44620     /**
44621      * Adds a ContentPanel (or subclass) to this layout.
44622      * @param {String} target The target region key (north, south, east, west or center).
44623      * @param {Roo.ContentPanel} panel The panel to add
44624      * @return {Roo.ContentPanel} The added panel
44625      */
44626     add : function(target, panel){
44627          
44628         target = target.toLowerCase();
44629         return this.regions[target].add(panel);
44630     },
44631
44632     /**
44633      * Remove a ContentPanel (or subclass) to this layout.
44634      * @param {String} target The target region key (north, south, east, west or center).
44635      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44636      * @return {Roo.ContentPanel} The removed panel
44637      */
44638     remove : function(target, panel){
44639         target = target.toLowerCase();
44640         return this.regions[target].remove(panel);
44641     },
44642
44643     /**
44644      * Searches all regions for a panel with the specified id
44645      * @param {String} panelId
44646      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44647      */
44648     findPanel : function(panelId){
44649         var rs = this.regions;
44650         for(var target in rs){
44651             if(typeof rs[target] != "function"){
44652                 var p = rs[target].getPanel(panelId);
44653                 if(p){
44654                     return p;
44655                 }
44656             }
44657         }
44658         return null;
44659     },
44660
44661     /**
44662      * Searches all regions for a panel with the specified id and activates (shows) it.
44663      * @param {String/ContentPanel} panelId The panels id or the panel itself
44664      * @return {Roo.ContentPanel} The shown panel or null
44665      */
44666     showPanel : function(panelId) {
44667       var rs = this.regions;
44668       for(var target in rs){
44669          var r = rs[target];
44670          if(typeof r != "function"){
44671             if(r.hasPanel(panelId)){
44672                return r.showPanel(panelId);
44673             }
44674          }
44675       }
44676       return null;
44677    },
44678
44679    /**
44680      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44681      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44682      */
44683     restoreState : function(provider){
44684         if(!provider){
44685             provider = Roo.state.Manager;
44686         }
44687         var sm = new Roo.LayoutStateManager();
44688         sm.init(this, provider);
44689     },
44690
44691     /**
44692      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44693      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44694      * a valid ContentPanel config object.  Example:
44695      * <pre><code>
44696 // Create the main layout
44697 var layout = new Roo.BorderLayout('main-ct', {
44698     west: {
44699         split:true,
44700         minSize: 175,
44701         titlebar: true
44702     },
44703     center: {
44704         title:'Components'
44705     }
44706 }, 'main-ct');
44707
44708 // Create and add multiple ContentPanels at once via configs
44709 layout.batchAdd({
44710    west: {
44711        id: 'source-files',
44712        autoCreate:true,
44713        title:'Ext Source Files',
44714        autoScroll:true,
44715        fitToFrame:true
44716    },
44717    center : {
44718        el: cview,
44719        autoScroll:true,
44720        fitToFrame:true,
44721        toolbar: tb,
44722        resizeEl:'cbody'
44723    }
44724 });
44725 </code></pre>
44726      * @param {Object} regions An object containing ContentPanel configs by region name
44727      */
44728     batchAdd : function(regions){
44729         this.beginUpdate();
44730         for(var rname in regions){
44731             var lr = this.regions[rname];
44732             if(lr){
44733                 this.addTypedPanels(lr, regions[rname]);
44734             }
44735         }
44736         this.endUpdate();
44737     },
44738
44739     // private
44740     addTypedPanels : function(lr, ps){
44741         if(typeof ps == 'string'){
44742             lr.add(new Roo.ContentPanel(ps));
44743         }
44744         else if(ps instanceof Array){
44745             for(var i =0, len = ps.length; i < len; i++){
44746                 this.addTypedPanels(lr, ps[i]);
44747             }
44748         }
44749         else if(!ps.events){ // raw config?
44750             var el = ps.el;
44751             delete ps.el; // prevent conflict
44752             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44753         }
44754         else {  // panel object assumed!
44755             lr.add(ps);
44756         }
44757     },
44758     /**
44759      * Adds a xtype elements to the layout.
44760      * <pre><code>
44761
44762 layout.addxtype({
44763        xtype : 'ContentPanel',
44764        region: 'west',
44765        items: [ .... ]
44766    }
44767 );
44768
44769 layout.addxtype({
44770         xtype : 'NestedLayoutPanel',
44771         region: 'west',
44772         layout: {
44773            center: { },
44774            west: { }   
44775         },
44776         items : [ ... list of content panels or nested layout panels.. ]
44777    }
44778 );
44779 </code></pre>
44780      * @param {Object} cfg Xtype definition of item to add.
44781      */
44782     addxtype : function(cfg)
44783     {
44784         // basically accepts a pannel...
44785         // can accept a layout region..!?!?
44786         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44787         
44788         if (!cfg.xtype.match(/Panel$/)) {
44789             return false;
44790         }
44791         var ret = false;
44792         
44793         if (typeof(cfg.region) == 'undefined') {
44794             Roo.log("Failed to add Panel, region was not set");
44795             Roo.log(cfg);
44796             return false;
44797         }
44798         var region = cfg.region;
44799         delete cfg.region;
44800         
44801           
44802         var xitems = [];
44803         if (cfg.items) {
44804             xitems = cfg.items;
44805             delete cfg.items;
44806         }
44807         var nb = false;
44808         
44809         switch(cfg.xtype) 
44810         {
44811             case 'ContentPanel':  // ContentPanel (el, cfg)
44812             case 'ScrollPanel':  // ContentPanel (el, cfg)
44813                 if(cfg.autoCreate) {
44814                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44815                 } else {
44816                     var el = this.el.createChild();
44817                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44818                 }
44819                 
44820                 this.add(region, ret);
44821                 break;
44822             
44823             
44824             case 'TreePanel': // our new panel!
44825                 cfg.el = this.el.createChild();
44826                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44827                 this.add(region, ret);
44828                 break;
44829             
44830             case 'NestedLayoutPanel': 
44831                 // create a new Layout (which is  a Border Layout...
44832                 var el = this.el.createChild();
44833                 var clayout = cfg.layout;
44834                 delete cfg.layout;
44835                 clayout.items   = clayout.items  || [];
44836                 // replace this exitems with the clayout ones..
44837                 xitems = clayout.items;
44838                  
44839                 
44840                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44841                     cfg.background = false;
44842                 }
44843                 var layout = new Roo.BorderLayout(el, clayout);
44844                 
44845                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44846                 //console.log('adding nested layout panel '  + cfg.toSource());
44847                 this.add(region, ret);
44848                 nb = {}; /// find first...
44849                 break;
44850                 
44851             case 'GridPanel': 
44852             
44853                 // needs grid and region
44854                 
44855                 //var el = this.getRegion(region).el.createChild();
44856                 var el = this.el.createChild();
44857                 // create the grid first...
44858                 
44859                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44860                 delete cfg.grid;
44861                 if (region == 'center' && this.active ) {
44862                     cfg.background = false;
44863                 }
44864                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44865                 
44866                 this.add(region, ret);
44867                 if (cfg.background) {
44868                     ret.on('activate', function(gp) {
44869                         if (!gp.grid.rendered) {
44870                             gp.grid.render();
44871                         }
44872                     });
44873                 } else {
44874                     grid.render();
44875                 }
44876                 break;
44877            
44878                
44879                 
44880                 
44881             default: 
44882                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44883                 return null;
44884              // GridPanel (grid, cfg)
44885             
44886         }
44887         this.beginUpdate();
44888         // add children..
44889         var region = '';
44890         var abn = {};
44891         Roo.each(xitems, function(i)  {
44892             region = nb && i.region ? i.region : false;
44893             
44894             var add = ret.addxtype(i);
44895            
44896             if (region) {
44897                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44898                 if (!i.background) {
44899                     abn[region] = nb[region] ;
44900                 }
44901             }
44902             
44903         });
44904         this.endUpdate();
44905
44906         // make the last non-background panel active..
44907         //if (nb) { Roo.log(abn); }
44908         if (nb) {
44909             
44910             for(var r in abn) {
44911                 region = this.getRegion(r);
44912                 if (region) {
44913                     // tried using nb[r], but it does not work..
44914                      
44915                     region.showPanel(abn[r]);
44916                    
44917                 }
44918             }
44919         }
44920         return ret;
44921         
44922     }
44923 });
44924
44925 /**
44926  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44927  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44928  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44929  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44930  * <pre><code>
44931 // shorthand
44932 var CP = Roo.ContentPanel;
44933
44934 var layout = Roo.BorderLayout.create({
44935     north: {
44936         initialSize: 25,
44937         titlebar: false,
44938         panels: [new CP("north", "North")]
44939     },
44940     west: {
44941         split:true,
44942         initialSize: 200,
44943         minSize: 175,
44944         maxSize: 400,
44945         titlebar: true,
44946         collapsible: true,
44947         panels: [new CP("west", {title: "West"})]
44948     },
44949     east: {
44950         split:true,
44951         initialSize: 202,
44952         minSize: 175,
44953         maxSize: 400,
44954         titlebar: true,
44955         collapsible: true,
44956         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44957     },
44958     south: {
44959         split:true,
44960         initialSize: 100,
44961         minSize: 100,
44962         maxSize: 200,
44963         titlebar: true,
44964         collapsible: true,
44965         panels: [new CP("south", {title: "South", closable: true})]
44966     },
44967     center: {
44968         titlebar: true,
44969         autoScroll:true,
44970         resizeTabs: true,
44971         minTabWidth: 50,
44972         preferredTabWidth: 150,
44973         panels: [
44974             new CP("center1", {title: "Close Me", closable: true}),
44975             new CP("center2", {title: "Center Panel", closable: false})
44976         ]
44977     }
44978 }, document.body);
44979
44980 layout.getRegion("center").showPanel("center1");
44981 </code></pre>
44982  * @param config
44983  * @param targetEl
44984  */
44985 Roo.BorderLayout.create = function(config, targetEl){
44986     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44987     layout.beginUpdate();
44988     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44989     for(var j = 0, jlen = regions.length; j < jlen; j++){
44990         var lr = regions[j];
44991         if(layout.regions[lr] && config[lr].panels){
44992             var r = layout.regions[lr];
44993             var ps = config[lr].panels;
44994             layout.addTypedPanels(r, ps);
44995         }
44996     }
44997     layout.endUpdate();
44998     return layout;
44999 };
45000
45001 // private
45002 Roo.BorderLayout.RegionFactory = {
45003     // private
45004     validRegions : ["north","south","east","west","center"],
45005
45006     // private
45007     create : function(target, mgr, config){
45008         target = target.toLowerCase();
45009         if(config.lightweight || config.basic){
45010             return new Roo.BasicLayoutRegion(mgr, config, target);
45011         }
45012         switch(target){
45013             case "north":
45014                 return new Roo.NorthLayoutRegion(mgr, config);
45015             case "south":
45016                 return new Roo.SouthLayoutRegion(mgr, config);
45017             case "east":
45018                 return new Roo.EastLayoutRegion(mgr, config);
45019             case "west":
45020                 return new Roo.WestLayoutRegion(mgr, config);
45021             case "center":
45022                 return new Roo.CenterLayoutRegion(mgr, config);
45023         }
45024         throw 'Layout region "'+target+'" not supported.';
45025     }
45026 };/*
45027  * Based on:
45028  * Ext JS Library 1.1.1
45029  * Copyright(c) 2006-2007, Ext JS, LLC.
45030  *
45031  * Originally Released Under LGPL - original licence link has changed is not relivant.
45032  *
45033  * Fork - LGPL
45034  * <script type="text/javascript">
45035  */
45036  
45037 /**
45038  * @class Roo.BasicLayoutRegion
45039  * @extends Roo.util.Observable
45040  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45041  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45042  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45043  */
45044 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45045     this.mgr = mgr;
45046     this.position  = pos;
45047     this.events = {
45048         /**
45049          * @scope Roo.BasicLayoutRegion
45050          */
45051         
45052         /**
45053          * @event beforeremove
45054          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45055          * @param {Roo.LayoutRegion} this
45056          * @param {Roo.ContentPanel} panel The panel
45057          * @param {Object} e The cancel event object
45058          */
45059         "beforeremove" : true,
45060         /**
45061          * @event invalidated
45062          * Fires when the layout for this region is changed.
45063          * @param {Roo.LayoutRegion} this
45064          */
45065         "invalidated" : true,
45066         /**
45067          * @event visibilitychange
45068          * Fires when this region is shown or hidden 
45069          * @param {Roo.LayoutRegion} this
45070          * @param {Boolean} visibility true or false
45071          */
45072         "visibilitychange" : true,
45073         /**
45074          * @event paneladded
45075          * Fires when a panel is added. 
45076          * @param {Roo.LayoutRegion} this
45077          * @param {Roo.ContentPanel} panel The panel
45078          */
45079         "paneladded" : true,
45080         /**
45081          * @event panelremoved
45082          * Fires when a panel is removed. 
45083          * @param {Roo.LayoutRegion} this
45084          * @param {Roo.ContentPanel} panel The panel
45085          */
45086         "panelremoved" : true,
45087         /**
45088          * @event collapsed
45089          * Fires when this region is collapsed.
45090          * @param {Roo.LayoutRegion} this
45091          */
45092         "collapsed" : true,
45093         /**
45094          * @event expanded
45095          * Fires when this region is expanded.
45096          * @param {Roo.LayoutRegion} this
45097          */
45098         "expanded" : true,
45099         /**
45100          * @event slideshow
45101          * Fires when this region is slid into view.
45102          * @param {Roo.LayoutRegion} this
45103          */
45104         "slideshow" : true,
45105         /**
45106          * @event slidehide
45107          * Fires when this region slides out of view. 
45108          * @param {Roo.LayoutRegion} this
45109          */
45110         "slidehide" : true,
45111         /**
45112          * @event panelactivated
45113          * Fires when a panel is activated. 
45114          * @param {Roo.LayoutRegion} this
45115          * @param {Roo.ContentPanel} panel The activated panel
45116          */
45117         "panelactivated" : true,
45118         /**
45119          * @event resized
45120          * Fires when the user resizes this region. 
45121          * @param {Roo.LayoutRegion} this
45122          * @param {Number} newSize The new size (width for east/west, height for north/south)
45123          */
45124         "resized" : true
45125     };
45126     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45127     this.panels = new Roo.util.MixedCollection();
45128     this.panels.getKey = this.getPanelId.createDelegate(this);
45129     this.box = null;
45130     this.activePanel = null;
45131     // ensure listeners are added...
45132     
45133     if (config.listeners || config.events) {
45134         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45135             listeners : config.listeners || {},
45136             events : config.events || {}
45137         });
45138     }
45139     
45140     if(skipConfig !== true){
45141         this.applyConfig(config);
45142     }
45143 };
45144
45145 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45146     getPanelId : function(p){
45147         return p.getId();
45148     },
45149     
45150     applyConfig : function(config){
45151         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45152         this.config = config;
45153         
45154     },
45155     
45156     /**
45157      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45158      * the width, for horizontal (north, south) the height.
45159      * @param {Number} newSize The new width or height
45160      */
45161     resizeTo : function(newSize){
45162         var el = this.el ? this.el :
45163                  (this.activePanel ? this.activePanel.getEl() : null);
45164         if(el){
45165             switch(this.position){
45166                 case "east":
45167                 case "west":
45168                     el.setWidth(newSize);
45169                     this.fireEvent("resized", this, newSize);
45170                 break;
45171                 case "north":
45172                 case "south":
45173                     el.setHeight(newSize);
45174                     this.fireEvent("resized", this, newSize);
45175                 break;                
45176             }
45177         }
45178     },
45179     
45180     getBox : function(){
45181         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45182     },
45183     
45184     getMargins : function(){
45185         return this.margins;
45186     },
45187     
45188     updateBox : function(box){
45189         this.box = box;
45190         var el = this.activePanel.getEl();
45191         el.dom.style.left = box.x + "px";
45192         el.dom.style.top = box.y + "px";
45193         this.activePanel.setSize(box.width, box.height);
45194     },
45195     
45196     /**
45197      * Returns the container element for this region.
45198      * @return {Roo.Element}
45199      */
45200     getEl : function(){
45201         return this.activePanel;
45202     },
45203     
45204     /**
45205      * Returns true if this region is currently visible.
45206      * @return {Boolean}
45207      */
45208     isVisible : function(){
45209         return this.activePanel ? true : false;
45210     },
45211     
45212     setActivePanel : function(panel){
45213         panel = this.getPanel(panel);
45214         if(this.activePanel && this.activePanel != panel){
45215             this.activePanel.setActiveState(false);
45216             this.activePanel.getEl().setLeftTop(-10000,-10000);
45217         }
45218         this.activePanel = panel;
45219         panel.setActiveState(true);
45220         if(this.box){
45221             panel.setSize(this.box.width, this.box.height);
45222         }
45223         this.fireEvent("panelactivated", this, panel);
45224         this.fireEvent("invalidated");
45225     },
45226     
45227     /**
45228      * Show the specified panel.
45229      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45230      * @return {Roo.ContentPanel} The shown panel or null
45231      */
45232     showPanel : function(panel){
45233         if(panel = this.getPanel(panel)){
45234             this.setActivePanel(panel);
45235         }
45236         return panel;
45237     },
45238     
45239     /**
45240      * Get the active panel for this region.
45241      * @return {Roo.ContentPanel} The active panel or null
45242      */
45243     getActivePanel : function(){
45244         return this.activePanel;
45245     },
45246     
45247     /**
45248      * Add the passed ContentPanel(s)
45249      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45250      * @return {Roo.ContentPanel} The panel added (if only one was added)
45251      */
45252     add : function(panel){
45253         if(arguments.length > 1){
45254             for(var i = 0, len = arguments.length; i < len; i++) {
45255                 this.add(arguments[i]);
45256             }
45257             return null;
45258         }
45259         if(this.hasPanel(panel)){
45260             this.showPanel(panel);
45261             return panel;
45262         }
45263         var el = panel.getEl();
45264         if(el.dom.parentNode != this.mgr.el.dom){
45265             this.mgr.el.dom.appendChild(el.dom);
45266         }
45267         if(panel.setRegion){
45268             panel.setRegion(this);
45269         }
45270         this.panels.add(panel);
45271         el.setStyle("position", "absolute");
45272         if(!panel.background){
45273             this.setActivePanel(panel);
45274             if(this.config.initialSize && this.panels.getCount()==1){
45275                 this.resizeTo(this.config.initialSize);
45276             }
45277         }
45278         this.fireEvent("paneladded", this, panel);
45279         return panel;
45280     },
45281     
45282     /**
45283      * Returns true if the panel is in this region.
45284      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45285      * @return {Boolean}
45286      */
45287     hasPanel : function(panel){
45288         if(typeof panel == "object"){ // must be panel obj
45289             panel = panel.getId();
45290         }
45291         return this.getPanel(panel) ? true : false;
45292     },
45293     
45294     /**
45295      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45296      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45297      * @param {Boolean} preservePanel Overrides the config preservePanel option
45298      * @return {Roo.ContentPanel} The panel that was removed
45299      */
45300     remove : function(panel, preservePanel){
45301         panel = this.getPanel(panel);
45302         if(!panel){
45303             return null;
45304         }
45305         var e = {};
45306         this.fireEvent("beforeremove", this, panel, e);
45307         if(e.cancel === true){
45308             return null;
45309         }
45310         var panelId = panel.getId();
45311         this.panels.removeKey(panelId);
45312         return panel;
45313     },
45314     
45315     /**
45316      * Returns the panel specified or null if it's not in this region.
45317      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45318      * @return {Roo.ContentPanel}
45319      */
45320     getPanel : function(id){
45321         if(typeof id == "object"){ // must be panel obj
45322             return id;
45323         }
45324         return this.panels.get(id);
45325     },
45326     
45327     /**
45328      * Returns this regions position (north/south/east/west/center).
45329      * @return {String} 
45330      */
45331     getPosition: function(){
45332         return this.position;    
45333     }
45334 });/*
45335  * Based on:
45336  * Ext JS Library 1.1.1
45337  * Copyright(c) 2006-2007, Ext JS, LLC.
45338  *
45339  * Originally Released Under LGPL - original licence link has changed is not relivant.
45340  *
45341  * Fork - LGPL
45342  * <script type="text/javascript">
45343  */
45344  
45345 /**
45346  * @class Roo.LayoutRegion
45347  * @extends Roo.BasicLayoutRegion
45348  * This class represents a region in a layout manager.
45349  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45350  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45351  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45352  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45353  * @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})
45354  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45355  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45356  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45357  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45358  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45359  * @cfg {String}    title           The title for the region (overrides panel titles)
45360  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45361  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45362  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45363  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45364  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45365  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45366  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45367  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45368  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45369  * @cfg {Boolean}   showPin         True to show a pin button
45370  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45371  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45372  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45373  * @cfg {Number}    width           For East/West panels
45374  * @cfg {Number}    height          For North/South panels
45375  * @cfg {Boolean}   split           To show the splitter
45376  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45377  */
45378 Roo.LayoutRegion = function(mgr, config, pos){
45379     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45380     var dh = Roo.DomHelper;
45381     /** This region's container element 
45382     * @type Roo.Element */
45383     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45384     /** This region's title element 
45385     * @type Roo.Element */
45386
45387     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45388         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45389         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45390     ]}, true);
45391     this.titleEl.enableDisplayMode();
45392     /** This region's title text element 
45393     * @type HTMLElement */
45394     this.titleTextEl = this.titleEl.dom.firstChild;
45395     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45396     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45397     this.closeBtn.enableDisplayMode();
45398     this.closeBtn.on("click", this.closeClicked, this);
45399     this.closeBtn.hide();
45400
45401     this.createBody(config);
45402     this.visible = true;
45403     this.collapsed = false;
45404
45405     if(config.hideWhenEmpty){
45406         this.hide();
45407         this.on("paneladded", this.validateVisibility, this);
45408         this.on("panelremoved", this.validateVisibility, this);
45409     }
45410     this.applyConfig(config);
45411 };
45412
45413 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45414
45415     createBody : function(){
45416         /** This region's body element 
45417         * @type Roo.Element */
45418         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45419     },
45420
45421     applyConfig : function(c){
45422         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45423             var dh = Roo.DomHelper;
45424             if(c.titlebar !== false){
45425                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45426                 this.collapseBtn.on("click", this.collapse, this);
45427                 this.collapseBtn.enableDisplayMode();
45428
45429                 if(c.showPin === true || this.showPin){
45430                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45431                     this.stickBtn.enableDisplayMode();
45432                     this.stickBtn.on("click", this.expand, this);
45433                     this.stickBtn.hide();
45434                 }
45435             }
45436             /** This region's collapsed element
45437             * @type Roo.Element */
45438             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45439                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45440             ]}, true);
45441             if(c.floatable !== false){
45442                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45443                this.collapsedEl.on("click", this.collapseClick, this);
45444             }
45445
45446             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45447                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45448                    id: "message", unselectable: "on", style:{"float":"left"}});
45449                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45450              }
45451             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45452             this.expandBtn.on("click", this.expand, this);
45453         }
45454         if(this.collapseBtn){
45455             this.collapseBtn.setVisible(c.collapsible == true);
45456         }
45457         this.cmargins = c.cmargins || this.cmargins ||
45458                          (this.position == "west" || this.position == "east" ?
45459                              {top: 0, left: 2, right:2, bottom: 0} :
45460                              {top: 2, left: 0, right:0, bottom: 2});
45461         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45462         this.bottomTabs = c.tabPosition != "top";
45463         this.autoScroll = c.autoScroll || false;
45464         if(this.autoScroll){
45465             this.bodyEl.setStyle("overflow", "auto");
45466         }else{
45467             this.bodyEl.setStyle("overflow", "hidden");
45468         }
45469         //if(c.titlebar !== false){
45470             if((!c.titlebar && !c.title) || c.titlebar === false){
45471                 this.titleEl.hide();
45472             }else{
45473                 this.titleEl.show();
45474                 if(c.title){
45475                     this.titleTextEl.innerHTML = c.title;
45476                 }
45477             }
45478         //}
45479         this.duration = c.duration || .30;
45480         this.slideDuration = c.slideDuration || .45;
45481         this.config = c;
45482         if(c.collapsed){
45483             this.collapse(true);
45484         }
45485         if(c.hidden){
45486             this.hide();
45487         }
45488     },
45489     /**
45490      * Returns true if this region is currently visible.
45491      * @return {Boolean}
45492      */
45493     isVisible : function(){
45494         return this.visible;
45495     },
45496
45497     /**
45498      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45499      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45500      */
45501     setCollapsedTitle : function(title){
45502         title = title || "&#160;";
45503         if(this.collapsedTitleTextEl){
45504             this.collapsedTitleTextEl.innerHTML = title;
45505         }
45506     },
45507
45508     getBox : function(){
45509         var b;
45510         if(!this.collapsed){
45511             b = this.el.getBox(false, true);
45512         }else{
45513             b = this.collapsedEl.getBox(false, true);
45514         }
45515         return b;
45516     },
45517
45518     getMargins : function(){
45519         return this.collapsed ? this.cmargins : this.margins;
45520     },
45521
45522     highlight : function(){
45523         this.el.addClass("x-layout-panel-dragover");
45524     },
45525
45526     unhighlight : function(){
45527         this.el.removeClass("x-layout-panel-dragover");
45528     },
45529
45530     updateBox : function(box){
45531         this.box = box;
45532         if(!this.collapsed){
45533             this.el.dom.style.left = box.x + "px";
45534             this.el.dom.style.top = box.y + "px";
45535             this.updateBody(box.width, box.height);
45536         }else{
45537             this.collapsedEl.dom.style.left = box.x + "px";
45538             this.collapsedEl.dom.style.top = box.y + "px";
45539             this.collapsedEl.setSize(box.width, box.height);
45540         }
45541         if(this.tabs){
45542             this.tabs.autoSizeTabs();
45543         }
45544     },
45545
45546     updateBody : function(w, h){
45547         if(w !== null){
45548             this.el.setWidth(w);
45549             w -= this.el.getBorderWidth("rl");
45550             if(this.config.adjustments){
45551                 w += this.config.adjustments[0];
45552             }
45553         }
45554         if(h !== null){
45555             this.el.setHeight(h);
45556             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45557             h -= this.el.getBorderWidth("tb");
45558             if(this.config.adjustments){
45559                 h += this.config.adjustments[1];
45560             }
45561             this.bodyEl.setHeight(h);
45562             if(this.tabs){
45563                 h = this.tabs.syncHeight(h);
45564             }
45565         }
45566         if(this.panelSize){
45567             w = w !== null ? w : this.panelSize.width;
45568             h = h !== null ? h : this.panelSize.height;
45569         }
45570         if(this.activePanel){
45571             var el = this.activePanel.getEl();
45572             w = w !== null ? w : el.getWidth();
45573             h = h !== null ? h : el.getHeight();
45574             this.panelSize = {width: w, height: h};
45575             this.activePanel.setSize(w, h);
45576         }
45577         if(Roo.isIE && this.tabs){
45578             this.tabs.el.repaint();
45579         }
45580     },
45581
45582     /**
45583      * Returns the container element for this region.
45584      * @return {Roo.Element}
45585      */
45586     getEl : function(){
45587         return this.el;
45588     },
45589
45590     /**
45591      * Hides this region.
45592      */
45593     hide : function(){
45594         if(!this.collapsed){
45595             this.el.dom.style.left = "-2000px";
45596             this.el.hide();
45597         }else{
45598             this.collapsedEl.dom.style.left = "-2000px";
45599             this.collapsedEl.hide();
45600         }
45601         this.visible = false;
45602         this.fireEvent("visibilitychange", this, false);
45603     },
45604
45605     /**
45606      * Shows this region if it was previously hidden.
45607      */
45608     show : function(){
45609         if(!this.collapsed){
45610             this.el.show();
45611         }else{
45612             this.collapsedEl.show();
45613         }
45614         this.visible = true;
45615         this.fireEvent("visibilitychange", this, true);
45616     },
45617
45618     closeClicked : function(){
45619         if(this.activePanel){
45620             this.remove(this.activePanel);
45621         }
45622     },
45623
45624     collapseClick : function(e){
45625         if(this.isSlid){
45626            e.stopPropagation();
45627            this.slideIn();
45628         }else{
45629            e.stopPropagation();
45630            this.slideOut();
45631         }
45632     },
45633
45634     /**
45635      * Collapses this region.
45636      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45637      */
45638     collapse : function(skipAnim){
45639         if(this.collapsed) return;
45640         this.collapsed = true;
45641         if(this.split){
45642             this.split.el.hide();
45643         }
45644         if(this.config.animate && skipAnim !== true){
45645             this.fireEvent("invalidated", this);
45646             this.animateCollapse();
45647         }else{
45648             this.el.setLocation(-20000,-20000);
45649             this.el.hide();
45650             this.collapsedEl.show();
45651             this.fireEvent("collapsed", this);
45652             this.fireEvent("invalidated", this);
45653         }
45654     },
45655
45656     animateCollapse : function(){
45657         // overridden
45658     },
45659
45660     /**
45661      * Expands this region if it was previously collapsed.
45662      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45663      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45664      */
45665     expand : function(e, skipAnim){
45666         if(e) e.stopPropagation();
45667         if(!this.collapsed || this.el.hasActiveFx()) return;
45668         if(this.isSlid){
45669             this.afterSlideIn();
45670             skipAnim = true;
45671         }
45672         this.collapsed = false;
45673         if(this.config.animate && skipAnim !== true){
45674             this.animateExpand();
45675         }else{
45676             this.el.show();
45677             if(this.split){
45678                 this.split.el.show();
45679             }
45680             this.collapsedEl.setLocation(-2000,-2000);
45681             this.collapsedEl.hide();
45682             this.fireEvent("invalidated", this);
45683             this.fireEvent("expanded", this);
45684         }
45685     },
45686
45687     animateExpand : function(){
45688         // overridden
45689     },
45690
45691     initTabs : function()
45692     {
45693         this.bodyEl.setStyle("overflow", "hidden");
45694         var ts = new Roo.TabPanel(
45695                 this.bodyEl.dom,
45696                 {
45697                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45698                     disableTooltips: this.config.disableTabTips,
45699                     toolbar : this.config.toolbar
45700                 }
45701         );
45702         if(this.config.hideTabs){
45703             ts.stripWrap.setDisplayed(false);
45704         }
45705         this.tabs = ts;
45706         ts.resizeTabs = this.config.resizeTabs === true;
45707         ts.minTabWidth = this.config.minTabWidth || 40;
45708         ts.maxTabWidth = this.config.maxTabWidth || 250;
45709         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45710         ts.monitorResize = false;
45711         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45712         ts.bodyEl.addClass('x-layout-tabs-body');
45713         this.panels.each(this.initPanelAsTab, this);
45714     },
45715
45716     initPanelAsTab : function(panel){
45717         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45718                     this.config.closeOnTab && panel.isClosable());
45719         if(panel.tabTip !== undefined){
45720             ti.setTooltip(panel.tabTip);
45721         }
45722         ti.on("activate", function(){
45723               this.setActivePanel(panel);
45724         }, this);
45725         if(this.config.closeOnTab){
45726             ti.on("beforeclose", function(t, e){
45727                 e.cancel = true;
45728                 this.remove(panel);
45729             }, this);
45730         }
45731         return ti;
45732     },
45733
45734     updatePanelTitle : function(panel, title){
45735         if(this.activePanel == panel){
45736             this.updateTitle(title);
45737         }
45738         if(this.tabs){
45739             var ti = this.tabs.getTab(panel.getEl().id);
45740             ti.setText(title);
45741             if(panel.tabTip !== undefined){
45742                 ti.setTooltip(panel.tabTip);
45743             }
45744         }
45745     },
45746
45747     updateTitle : function(title){
45748         if(this.titleTextEl && !this.config.title){
45749             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45750         }
45751     },
45752
45753     setActivePanel : function(panel){
45754         panel = this.getPanel(panel);
45755         if(this.activePanel && this.activePanel != panel){
45756             this.activePanel.setActiveState(false);
45757         }
45758         this.activePanel = panel;
45759         panel.setActiveState(true);
45760         if(this.panelSize){
45761             panel.setSize(this.panelSize.width, this.panelSize.height);
45762         }
45763         if(this.closeBtn){
45764             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45765         }
45766         this.updateTitle(panel.getTitle());
45767         if(this.tabs){
45768             this.fireEvent("invalidated", this);
45769         }
45770         this.fireEvent("panelactivated", this, panel);
45771     },
45772
45773     /**
45774      * Shows the specified panel.
45775      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45776      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45777      */
45778     showPanel : function(panel){
45779         if(panel = this.getPanel(panel)){
45780             if(this.tabs){
45781                 var tab = this.tabs.getTab(panel.getEl().id);
45782                 if(tab.isHidden()){
45783                     this.tabs.unhideTab(tab.id);
45784                 }
45785                 tab.activate();
45786             }else{
45787                 this.setActivePanel(panel);
45788             }
45789         }
45790         return panel;
45791     },
45792
45793     /**
45794      * Get the active panel for this region.
45795      * @return {Roo.ContentPanel} The active panel or null
45796      */
45797     getActivePanel : function(){
45798         return this.activePanel;
45799     },
45800
45801     validateVisibility : function(){
45802         if(this.panels.getCount() < 1){
45803             this.updateTitle("&#160;");
45804             this.closeBtn.hide();
45805             this.hide();
45806         }else{
45807             if(!this.isVisible()){
45808                 this.show();
45809             }
45810         }
45811     },
45812
45813     /**
45814      * Adds the passed ContentPanel(s) to this region.
45815      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45816      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45817      */
45818     add : function(panel){
45819         if(arguments.length > 1){
45820             for(var i = 0, len = arguments.length; i < len; i++) {
45821                 this.add(arguments[i]);
45822             }
45823             return null;
45824         }
45825         if(this.hasPanel(panel)){
45826             this.showPanel(panel);
45827             return panel;
45828         }
45829         panel.setRegion(this);
45830         this.panels.add(panel);
45831         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45832             this.bodyEl.dom.appendChild(panel.getEl().dom);
45833             if(panel.background !== true){
45834                 this.setActivePanel(panel);
45835             }
45836             this.fireEvent("paneladded", this, panel);
45837             return panel;
45838         }
45839         if(!this.tabs){
45840             this.initTabs();
45841         }else{
45842             this.initPanelAsTab(panel);
45843         }
45844         if(panel.background !== true){
45845             this.tabs.activate(panel.getEl().id);
45846         }
45847         this.fireEvent("paneladded", this, panel);
45848         return panel;
45849     },
45850
45851     /**
45852      * Hides the tab for the specified panel.
45853      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45854      */
45855     hidePanel : function(panel){
45856         if(this.tabs && (panel = this.getPanel(panel))){
45857             this.tabs.hideTab(panel.getEl().id);
45858         }
45859     },
45860
45861     /**
45862      * Unhides the tab for a previously hidden panel.
45863      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45864      */
45865     unhidePanel : function(panel){
45866         if(this.tabs && (panel = this.getPanel(panel))){
45867             this.tabs.unhideTab(panel.getEl().id);
45868         }
45869     },
45870
45871     clearPanels : function(){
45872         while(this.panels.getCount() > 0){
45873              this.remove(this.panels.first());
45874         }
45875     },
45876
45877     /**
45878      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45879      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45880      * @param {Boolean} preservePanel Overrides the config preservePanel option
45881      * @return {Roo.ContentPanel} The panel that was removed
45882      */
45883     remove : function(panel, preservePanel){
45884         panel = this.getPanel(panel);
45885         if(!panel){
45886             return null;
45887         }
45888         var e = {};
45889         this.fireEvent("beforeremove", this, panel, e);
45890         if(e.cancel === true){
45891             return null;
45892         }
45893         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45894         var panelId = panel.getId();
45895         this.panels.removeKey(panelId);
45896         if(preservePanel){
45897             document.body.appendChild(panel.getEl().dom);
45898         }
45899         if(this.tabs){
45900             this.tabs.removeTab(panel.getEl().id);
45901         }else if (!preservePanel){
45902             this.bodyEl.dom.removeChild(panel.getEl().dom);
45903         }
45904         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45905             var p = this.panels.first();
45906             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45907             tempEl.appendChild(p.getEl().dom);
45908             this.bodyEl.update("");
45909             this.bodyEl.dom.appendChild(p.getEl().dom);
45910             tempEl = null;
45911             this.updateTitle(p.getTitle());
45912             this.tabs = null;
45913             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45914             this.setActivePanel(p);
45915         }
45916         panel.setRegion(null);
45917         if(this.activePanel == panel){
45918             this.activePanel = null;
45919         }
45920         if(this.config.autoDestroy !== false && preservePanel !== true){
45921             try{panel.destroy();}catch(e){}
45922         }
45923         this.fireEvent("panelremoved", this, panel);
45924         return panel;
45925     },
45926
45927     /**
45928      * Returns the TabPanel component used by this region
45929      * @return {Roo.TabPanel}
45930      */
45931     getTabs : function(){
45932         return this.tabs;
45933     },
45934
45935     createTool : function(parentEl, className){
45936         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45937             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45938         btn.addClassOnOver("x-layout-tools-button-over");
45939         return btn;
45940     }
45941 });/*
45942  * Based on:
45943  * Ext JS Library 1.1.1
45944  * Copyright(c) 2006-2007, Ext JS, LLC.
45945  *
45946  * Originally Released Under LGPL - original licence link has changed is not relivant.
45947  *
45948  * Fork - LGPL
45949  * <script type="text/javascript">
45950  */
45951  
45952
45953
45954 /**
45955  * @class Roo.SplitLayoutRegion
45956  * @extends Roo.LayoutRegion
45957  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45958  */
45959 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45960     this.cursor = cursor;
45961     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45962 };
45963
45964 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45965     splitTip : "Drag to resize.",
45966     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45967     useSplitTips : false,
45968
45969     applyConfig : function(config){
45970         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45971         if(config.split){
45972             if(!this.split){
45973                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45974                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45975                 /** The SplitBar for this region 
45976                 * @type Roo.SplitBar */
45977                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45978                 this.split.on("moved", this.onSplitMove, this);
45979                 this.split.useShim = config.useShim === true;
45980                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45981                 if(this.useSplitTips){
45982                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45983                 }
45984                 if(config.collapsible){
45985                     this.split.el.on("dblclick", this.collapse,  this);
45986                 }
45987             }
45988             if(typeof config.minSize != "undefined"){
45989                 this.split.minSize = config.minSize;
45990             }
45991             if(typeof config.maxSize != "undefined"){
45992                 this.split.maxSize = config.maxSize;
45993             }
45994             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45995                 this.hideSplitter();
45996             }
45997         }
45998     },
45999
46000     getHMaxSize : function(){
46001          var cmax = this.config.maxSize || 10000;
46002          var center = this.mgr.getRegion("center");
46003          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46004     },
46005
46006     getVMaxSize : function(){
46007          var cmax = this.config.maxSize || 10000;
46008          var center = this.mgr.getRegion("center");
46009          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46010     },
46011
46012     onSplitMove : function(split, newSize){
46013         this.fireEvent("resized", this, newSize);
46014     },
46015     
46016     /** 
46017      * Returns the {@link Roo.SplitBar} for this region.
46018      * @return {Roo.SplitBar}
46019      */
46020     getSplitBar : function(){
46021         return this.split;
46022     },
46023     
46024     hide : function(){
46025         this.hideSplitter();
46026         Roo.SplitLayoutRegion.superclass.hide.call(this);
46027     },
46028
46029     hideSplitter : function(){
46030         if(this.split){
46031             this.split.el.setLocation(-2000,-2000);
46032             this.split.el.hide();
46033         }
46034     },
46035
46036     show : function(){
46037         if(this.split){
46038             this.split.el.show();
46039         }
46040         Roo.SplitLayoutRegion.superclass.show.call(this);
46041     },
46042     
46043     beforeSlide: function(){
46044         if(Roo.isGecko){// firefox overflow auto bug workaround
46045             this.bodyEl.clip();
46046             if(this.tabs) this.tabs.bodyEl.clip();
46047             if(this.activePanel){
46048                 this.activePanel.getEl().clip();
46049                 
46050                 if(this.activePanel.beforeSlide){
46051                     this.activePanel.beforeSlide();
46052                 }
46053             }
46054         }
46055     },
46056     
46057     afterSlide : function(){
46058         if(Roo.isGecko){// firefox overflow auto bug workaround
46059             this.bodyEl.unclip();
46060             if(this.tabs) this.tabs.bodyEl.unclip();
46061             if(this.activePanel){
46062                 this.activePanel.getEl().unclip();
46063                 if(this.activePanel.afterSlide){
46064                     this.activePanel.afterSlide();
46065                 }
46066             }
46067         }
46068     },
46069
46070     initAutoHide : function(){
46071         if(this.autoHide !== false){
46072             if(!this.autoHideHd){
46073                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46074                 this.autoHideHd = {
46075                     "mouseout": function(e){
46076                         if(!e.within(this.el, true)){
46077                             st.delay(500);
46078                         }
46079                     },
46080                     "mouseover" : function(e){
46081                         st.cancel();
46082                     },
46083                     scope : this
46084                 };
46085             }
46086             this.el.on(this.autoHideHd);
46087         }
46088     },
46089
46090     clearAutoHide : function(){
46091         if(this.autoHide !== false){
46092             this.el.un("mouseout", this.autoHideHd.mouseout);
46093             this.el.un("mouseover", this.autoHideHd.mouseover);
46094         }
46095     },
46096
46097     clearMonitor : function(){
46098         Roo.get(document).un("click", this.slideInIf, this);
46099     },
46100
46101     // these names are backwards but not changed for compat
46102     slideOut : function(){
46103         if(this.isSlid || this.el.hasActiveFx()){
46104             return;
46105         }
46106         this.isSlid = true;
46107         if(this.collapseBtn){
46108             this.collapseBtn.hide();
46109         }
46110         this.closeBtnState = this.closeBtn.getStyle('display');
46111         this.closeBtn.hide();
46112         if(this.stickBtn){
46113             this.stickBtn.show();
46114         }
46115         this.el.show();
46116         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46117         this.beforeSlide();
46118         this.el.setStyle("z-index", 10001);
46119         this.el.slideIn(this.getSlideAnchor(), {
46120             callback: function(){
46121                 this.afterSlide();
46122                 this.initAutoHide();
46123                 Roo.get(document).on("click", this.slideInIf, this);
46124                 this.fireEvent("slideshow", this);
46125             },
46126             scope: this,
46127             block: true
46128         });
46129     },
46130
46131     afterSlideIn : function(){
46132         this.clearAutoHide();
46133         this.isSlid = false;
46134         this.clearMonitor();
46135         this.el.setStyle("z-index", "");
46136         if(this.collapseBtn){
46137             this.collapseBtn.show();
46138         }
46139         this.closeBtn.setStyle('display', this.closeBtnState);
46140         if(this.stickBtn){
46141             this.stickBtn.hide();
46142         }
46143         this.fireEvent("slidehide", this);
46144     },
46145
46146     slideIn : function(cb){
46147         if(!this.isSlid || this.el.hasActiveFx()){
46148             Roo.callback(cb);
46149             return;
46150         }
46151         this.isSlid = false;
46152         this.beforeSlide();
46153         this.el.slideOut(this.getSlideAnchor(), {
46154             callback: function(){
46155                 this.el.setLeftTop(-10000, -10000);
46156                 this.afterSlide();
46157                 this.afterSlideIn();
46158                 Roo.callback(cb);
46159             },
46160             scope: this,
46161             block: true
46162         });
46163     },
46164     
46165     slideInIf : function(e){
46166         if(!e.within(this.el)){
46167             this.slideIn();
46168         }
46169     },
46170
46171     animateCollapse : function(){
46172         this.beforeSlide();
46173         this.el.setStyle("z-index", 20000);
46174         var anchor = this.getSlideAnchor();
46175         this.el.slideOut(anchor, {
46176             callback : function(){
46177                 this.el.setStyle("z-index", "");
46178                 this.collapsedEl.slideIn(anchor, {duration:.3});
46179                 this.afterSlide();
46180                 this.el.setLocation(-10000,-10000);
46181                 this.el.hide();
46182                 this.fireEvent("collapsed", this);
46183             },
46184             scope: this,
46185             block: true
46186         });
46187     },
46188
46189     animateExpand : function(){
46190         this.beforeSlide();
46191         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46192         this.el.setStyle("z-index", 20000);
46193         this.collapsedEl.hide({
46194             duration:.1
46195         });
46196         this.el.slideIn(this.getSlideAnchor(), {
46197             callback : function(){
46198                 this.el.setStyle("z-index", "");
46199                 this.afterSlide();
46200                 if(this.split){
46201                     this.split.el.show();
46202                 }
46203                 this.fireEvent("invalidated", this);
46204                 this.fireEvent("expanded", this);
46205             },
46206             scope: this,
46207             block: true
46208         });
46209     },
46210
46211     anchors : {
46212         "west" : "left",
46213         "east" : "right",
46214         "north" : "top",
46215         "south" : "bottom"
46216     },
46217
46218     sanchors : {
46219         "west" : "l",
46220         "east" : "r",
46221         "north" : "t",
46222         "south" : "b"
46223     },
46224
46225     canchors : {
46226         "west" : "tl-tr",
46227         "east" : "tr-tl",
46228         "north" : "tl-bl",
46229         "south" : "bl-tl"
46230     },
46231
46232     getAnchor : function(){
46233         return this.anchors[this.position];
46234     },
46235
46236     getCollapseAnchor : function(){
46237         return this.canchors[this.position];
46238     },
46239
46240     getSlideAnchor : function(){
46241         return this.sanchors[this.position];
46242     },
46243
46244     getAlignAdj : function(){
46245         var cm = this.cmargins;
46246         switch(this.position){
46247             case "west":
46248                 return [0, 0];
46249             break;
46250             case "east":
46251                 return [0, 0];
46252             break;
46253             case "north":
46254                 return [0, 0];
46255             break;
46256             case "south":
46257                 return [0, 0];
46258             break;
46259         }
46260     },
46261
46262     getExpandAdj : function(){
46263         var c = this.collapsedEl, cm = this.cmargins;
46264         switch(this.position){
46265             case "west":
46266                 return [-(cm.right+c.getWidth()+cm.left), 0];
46267             break;
46268             case "east":
46269                 return [cm.right+c.getWidth()+cm.left, 0];
46270             break;
46271             case "north":
46272                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46273             break;
46274             case "south":
46275                 return [0, cm.top+cm.bottom+c.getHeight()];
46276             break;
46277         }
46278     }
46279 });/*
46280  * Based on:
46281  * Ext JS Library 1.1.1
46282  * Copyright(c) 2006-2007, Ext JS, LLC.
46283  *
46284  * Originally Released Under LGPL - original licence link has changed is not relivant.
46285  *
46286  * Fork - LGPL
46287  * <script type="text/javascript">
46288  */
46289 /*
46290  * These classes are private internal classes
46291  */
46292 Roo.CenterLayoutRegion = function(mgr, config){
46293     Roo.LayoutRegion.call(this, mgr, config, "center");
46294     this.visible = true;
46295     this.minWidth = config.minWidth || 20;
46296     this.minHeight = config.minHeight || 20;
46297 };
46298
46299 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46300     hide : function(){
46301         // center panel can't be hidden
46302     },
46303     
46304     show : function(){
46305         // center panel can't be hidden
46306     },
46307     
46308     getMinWidth: function(){
46309         return this.minWidth;
46310     },
46311     
46312     getMinHeight: function(){
46313         return this.minHeight;
46314     }
46315 });
46316
46317
46318 Roo.NorthLayoutRegion = function(mgr, config){
46319     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46320     if(this.split){
46321         this.split.placement = Roo.SplitBar.TOP;
46322         this.split.orientation = Roo.SplitBar.VERTICAL;
46323         this.split.el.addClass("x-layout-split-v");
46324     }
46325     var size = config.initialSize || config.height;
46326     if(typeof size != "undefined"){
46327         this.el.setHeight(size);
46328     }
46329 };
46330 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46331     orientation: Roo.SplitBar.VERTICAL,
46332     getBox : function(){
46333         if(this.collapsed){
46334             return this.collapsedEl.getBox();
46335         }
46336         var box = this.el.getBox();
46337         if(this.split){
46338             box.height += this.split.el.getHeight();
46339         }
46340         return box;
46341     },
46342     
46343     updateBox : function(box){
46344         if(this.split && !this.collapsed){
46345             box.height -= this.split.el.getHeight();
46346             this.split.el.setLeft(box.x);
46347             this.split.el.setTop(box.y+box.height);
46348             this.split.el.setWidth(box.width);
46349         }
46350         if(this.collapsed){
46351             this.updateBody(box.width, null);
46352         }
46353         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46354     }
46355 });
46356
46357 Roo.SouthLayoutRegion = function(mgr, config){
46358     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46359     if(this.split){
46360         this.split.placement = Roo.SplitBar.BOTTOM;
46361         this.split.orientation = Roo.SplitBar.VERTICAL;
46362         this.split.el.addClass("x-layout-split-v");
46363     }
46364     var size = config.initialSize || config.height;
46365     if(typeof size != "undefined"){
46366         this.el.setHeight(size);
46367     }
46368 };
46369 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46370     orientation: Roo.SplitBar.VERTICAL,
46371     getBox : function(){
46372         if(this.collapsed){
46373             return this.collapsedEl.getBox();
46374         }
46375         var box = this.el.getBox();
46376         if(this.split){
46377             var sh = this.split.el.getHeight();
46378             box.height += sh;
46379             box.y -= sh;
46380         }
46381         return box;
46382     },
46383     
46384     updateBox : function(box){
46385         if(this.split && !this.collapsed){
46386             var sh = this.split.el.getHeight();
46387             box.height -= sh;
46388             box.y += sh;
46389             this.split.el.setLeft(box.x);
46390             this.split.el.setTop(box.y-sh);
46391             this.split.el.setWidth(box.width);
46392         }
46393         if(this.collapsed){
46394             this.updateBody(box.width, null);
46395         }
46396         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46397     }
46398 });
46399
46400 Roo.EastLayoutRegion = function(mgr, config){
46401     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46402     if(this.split){
46403         this.split.placement = Roo.SplitBar.RIGHT;
46404         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46405         this.split.el.addClass("x-layout-split-h");
46406     }
46407     var size = config.initialSize || config.width;
46408     if(typeof size != "undefined"){
46409         this.el.setWidth(size);
46410     }
46411 };
46412 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46413     orientation: Roo.SplitBar.HORIZONTAL,
46414     getBox : function(){
46415         if(this.collapsed){
46416             return this.collapsedEl.getBox();
46417         }
46418         var box = this.el.getBox();
46419         if(this.split){
46420             var sw = this.split.el.getWidth();
46421             box.width += sw;
46422             box.x -= sw;
46423         }
46424         return box;
46425     },
46426
46427     updateBox : function(box){
46428         if(this.split && !this.collapsed){
46429             var sw = this.split.el.getWidth();
46430             box.width -= sw;
46431             this.split.el.setLeft(box.x);
46432             this.split.el.setTop(box.y);
46433             this.split.el.setHeight(box.height);
46434             box.x += sw;
46435         }
46436         if(this.collapsed){
46437             this.updateBody(null, box.height);
46438         }
46439         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46440     }
46441 });
46442
46443 Roo.WestLayoutRegion = function(mgr, config){
46444     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46445     if(this.split){
46446         this.split.placement = Roo.SplitBar.LEFT;
46447         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46448         this.split.el.addClass("x-layout-split-h");
46449     }
46450     var size = config.initialSize || config.width;
46451     if(typeof size != "undefined"){
46452         this.el.setWidth(size);
46453     }
46454 };
46455 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46456     orientation: Roo.SplitBar.HORIZONTAL,
46457     getBox : function(){
46458         if(this.collapsed){
46459             return this.collapsedEl.getBox();
46460         }
46461         var box = this.el.getBox();
46462         if(this.split){
46463             box.width += this.split.el.getWidth();
46464         }
46465         return box;
46466     },
46467     
46468     updateBox : function(box){
46469         if(this.split && !this.collapsed){
46470             var sw = this.split.el.getWidth();
46471             box.width -= sw;
46472             this.split.el.setLeft(box.x+box.width);
46473             this.split.el.setTop(box.y);
46474             this.split.el.setHeight(box.height);
46475         }
46476         if(this.collapsed){
46477             this.updateBody(null, box.height);
46478         }
46479         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46480     }
46481 });
46482 /*
46483  * Based on:
46484  * Ext JS Library 1.1.1
46485  * Copyright(c) 2006-2007, Ext JS, LLC.
46486  *
46487  * Originally Released Under LGPL - original licence link has changed is not relivant.
46488  *
46489  * Fork - LGPL
46490  * <script type="text/javascript">
46491  */
46492  
46493  
46494 /*
46495  * Private internal class for reading and applying state
46496  */
46497 Roo.LayoutStateManager = function(layout){
46498      // default empty state
46499      this.state = {
46500         north: {},
46501         south: {},
46502         east: {},
46503         west: {}       
46504     };
46505 };
46506
46507 Roo.LayoutStateManager.prototype = {
46508     init : function(layout, provider){
46509         this.provider = provider;
46510         var state = provider.get(layout.id+"-layout-state");
46511         if(state){
46512             var wasUpdating = layout.isUpdating();
46513             if(!wasUpdating){
46514                 layout.beginUpdate();
46515             }
46516             for(var key in state){
46517                 if(typeof state[key] != "function"){
46518                     var rstate = state[key];
46519                     var r = layout.getRegion(key);
46520                     if(r && rstate){
46521                         if(rstate.size){
46522                             r.resizeTo(rstate.size);
46523                         }
46524                         if(rstate.collapsed == true){
46525                             r.collapse(true);
46526                         }else{
46527                             r.expand(null, true);
46528                         }
46529                     }
46530                 }
46531             }
46532             if(!wasUpdating){
46533                 layout.endUpdate();
46534             }
46535             this.state = state; 
46536         }
46537         this.layout = layout;
46538         layout.on("regionresized", this.onRegionResized, this);
46539         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46540         layout.on("regionexpanded", this.onRegionExpanded, this);
46541     },
46542     
46543     storeState : function(){
46544         this.provider.set(this.layout.id+"-layout-state", this.state);
46545     },
46546     
46547     onRegionResized : function(region, newSize){
46548         this.state[region.getPosition()].size = newSize;
46549         this.storeState();
46550     },
46551     
46552     onRegionCollapsed : function(region){
46553         this.state[region.getPosition()].collapsed = true;
46554         this.storeState();
46555     },
46556     
46557     onRegionExpanded : function(region){
46558         this.state[region.getPosition()].collapsed = false;
46559         this.storeState();
46560     }
46561 };/*
46562  * Based on:
46563  * Ext JS Library 1.1.1
46564  * Copyright(c) 2006-2007, Ext JS, LLC.
46565  *
46566  * Originally Released Under LGPL - original licence link has changed is not relivant.
46567  *
46568  * Fork - LGPL
46569  * <script type="text/javascript">
46570  */
46571 /**
46572  * @class Roo.ContentPanel
46573  * @extends Roo.util.Observable
46574  * A basic ContentPanel element.
46575  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46576  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46577  * @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
46578  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46579  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46580  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46581  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46582  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46583  * @cfg {String} title          The title for this panel
46584  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46585  * @cfg {String} url            Calls {@link #setUrl} with this value
46586  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46587  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46588  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46589  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46590
46591  * @constructor
46592  * Create a new ContentPanel.
46593  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46594  * @param {String/Object} config A string to set only the title or a config object
46595  * @param {String} content (optional) Set the HTML content for this panel
46596  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46597  */
46598 Roo.ContentPanel = function(el, config, content){
46599     
46600      
46601     /*
46602     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46603         config = el;
46604         el = Roo.id();
46605     }
46606     if (config && config.parentLayout) { 
46607         el = config.parentLayout.el.createChild(); 
46608     }
46609     */
46610     if(el.autoCreate){ // xtype is available if this is called from factory
46611         config = el;
46612         el = Roo.id();
46613     }
46614     this.el = Roo.get(el);
46615     if(!this.el && config && config.autoCreate){
46616         if(typeof config.autoCreate == "object"){
46617             if(!config.autoCreate.id){
46618                 config.autoCreate.id = config.id||el;
46619             }
46620             this.el = Roo.DomHelper.append(document.body,
46621                         config.autoCreate, true);
46622         }else{
46623             this.el = Roo.DomHelper.append(document.body,
46624                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46625         }
46626     }
46627     this.closable = false;
46628     this.loaded = false;
46629     this.active = false;
46630     if(typeof config == "string"){
46631         this.title = config;
46632     }else{
46633         Roo.apply(this, config);
46634     }
46635     
46636     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46637         this.wrapEl = this.el.wrap();
46638         this.toolbar.container = this.el.insertSibling(false, 'before');
46639         this.toolbar = new Roo.Toolbar(this.toolbar);
46640     }
46641     
46642     
46643     
46644     if(this.resizeEl){
46645         this.resizeEl = Roo.get(this.resizeEl, true);
46646     }else{
46647         this.resizeEl = this.el;
46648     }
46649     this.addEvents({
46650         /**
46651          * @event activate
46652          * Fires when this panel is activated. 
46653          * @param {Roo.ContentPanel} this
46654          */
46655         "activate" : true,
46656         /**
46657          * @event deactivate
46658          * Fires when this panel is activated. 
46659          * @param {Roo.ContentPanel} this
46660          */
46661         "deactivate" : true,
46662
46663         /**
46664          * @event resize
46665          * Fires when this panel is resized if fitToFrame is true.
46666          * @param {Roo.ContentPanel} this
46667          * @param {Number} width The width after any component adjustments
46668          * @param {Number} height The height after any component adjustments
46669          */
46670         "resize" : true,
46671         
46672          /**
46673          * @event render
46674          * Fires when this tab is created
46675          * @param {Roo.ContentPanel} this
46676          */
46677         "render" : true
46678         
46679         
46680         
46681     });
46682     if(this.autoScroll){
46683         this.resizeEl.setStyle("overflow", "auto");
46684     } else {
46685         // fix randome scrolling
46686         this.el.on('scroll', function() {
46687             Roo.log('fix random scolling');
46688             this.scrollTo('top',0); 
46689         });
46690     }
46691     content = content || this.content;
46692     if(content){
46693         this.setContent(content);
46694     }
46695     if(config && config.url){
46696         this.setUrl(this.url, this.params, this.loadOnce);
46697     }
46698     
46699     
46700     
46701     Roo.ContentPanel.superclass.constructor.call(this);
46702     
46703     this.fireEvent('render', this);
46704 };
46705
46706 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46707     tabTip:'',
46708     setRegion : function(region){
46709         this.region = region;
46710         if(region){
46711            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46712         }else{
46713            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46714         } 
46715     },
46716     
46717     /**
46718      * Returns the toolbar for this Panel if one was configured. 
46719      * @return {Roo.Toolbar} 
46720      */
46721     getToolbar : function(){
46722         return this.toolbar;
46723     },
46724     
46725     setActiveState : function(active){
46726         this.active = active;
46727         if(!active){
46728             this.fireEvent("deactivate", this);
46729         }else{
46730             this.fireEvent("activate", this);
46731         }
46732     },
46733     /**
46734      * Updates this panel's element
46735      * @param {String} content The new content
46736      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46737     */
46738     setContent : function(content, loadScripts){
46739         this.el.update(content, loadScripts);
46740     },
46741
46742     ignoreResize : function(w, h){
46743         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46744             return true;
46745         }else{
46746             this.lastSize = {width: w, height: h};
46747             return false;
46748         }
46749     },
46750     /**
46751      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46752      * @return {Roo.UpdateManager} The UpdateManager
46753      */
46754     getUpdateManager : function(){
46755         return this.el.getUpdateManager();
46756     },
46757      /**
46758      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46759      * @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:
46760 <pre><code>
46761 panel.load({
46762     url: "your-url.php",
46763     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46764     callback: yourFunction,
46765     scope: yourObject, //(optional scope)
46766     discardUrl: false,
46767     nocache: false,
46768     text: "Loading...",
46769     timeout: 30,
46770     scripts: false
46771 });
46772 </code></pre>
46773      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46774      * 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.
46775      * @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}
46776      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46777      * @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.
46778      * @return {Roo.ContentPanel} this
46779      */
46780     load : function(){
46781         var um = this.el.getUpdateManager();
46782         um.update.apply(um, arguments);
46783         return this;
46784     },
46785
46786
46787     /**
46788      * 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.
46789      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46790      * @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)
46791      * @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)
46792      * @return {Roo.UpdateManager} The UpdateManager
46793      */
46794     setUrl : function(url, params, loadOnce){
46795         if(this.refreshDelegate){
46796             this.removeListener("activate", this.refreshDelegate);
46797         }
46798         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46799         this.on("activate", this.refreshDelegate);
46800         return this.el.getUpdateManager();
46801     },
46802     
46803     _handleRefresh : function(url, params, loadOnce){
46804         if(!loadOnce || !this.loaded){
46805             var updater = this.el.getUpdateManager();
46806             updater.update(url, params, this._setLoaded.createDelegate(this));
46807         }
46808     },
46809     
46810     _setLoaded : function(){
46811         this.loaded = true;
46812     }, 
46813     
46814     /**
46815      * Returns this panel's id
46816      * @return {String} 
46817      */
46818     getId : function(){
46819         return this.el.id;
46820     },
46821     
46822     /** 
46823      * Returns this panel's element - used by regiosn to add.
46824      * @return {Roo.Element} 
46825      */
46826     getEl : function(){
46827         return this.wrapEl || this.el;
46828     },
46829     
46830     adjustForComponents : function(width, height){
46831         if(this.resizeEl != this.el){
46832             width -= this.el.getFrameWidth('lr');
46833             height -= this.el.getFrameWidth('tb');
46834         }
46835         if(this.toolbar){
46836             var te = this.toolbar.getEl();
46837             height -= te.getHeight();
46838             te.setWidth(width);
46839         }
46840         if(this.adjustments){
46841             width += this.adjustments[0];
46842             height += this.adjustments[1];
46843         }
46844         return {"width": width, "height": height};
46845     },
46846     
46847     setSize : function(width, height){
46848         if(this.fitToFrame && !this.ignoreResize(width, height)){
46849             if(this.fitContainer && this.resizeEl != this.el){
46850                 this.el.setSize(width, height);
46851             }
46852             var size = this.adjustForComponents(width, height);
46853             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46854             this.fireEvent('resize', this, size.width, size.height);
46855         }
46856     },
46857     
46858     /**
46859      * Returns this panel's title
46860      * @return {String} 
46861      */
46862     getTitle : function(){
46863         return this.title;
46864     },
46865     
46866     /**
46867      * Set this panel's title
46868      * @param {String} title
46869      */
46870     setTitle : function(title){
46871         this.title = title;
46872         if(this.region){
46873             this.region.updatePanelTitle(this, title);
46874         }
46875     },
46876     
46877     /**
46878      * Returns true is this panel was configured to be closable
46879      * @return {Boolean} 
46880      */
46881     isClosable : function(){
46882         return this.closable;
46883     },
46884     
46885     beforeSlide : function(){
46886         this.el.clip();
46887         this.resizeEl.clip();
46888     },
46889     
46890     afterSlide : function(){
46891         this.el.unclip();
46892         this.resizeEl.unclip();
46893     },
46894     
46895     /**
46896      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46897      *   Will fail silently if the {@link #setUrl} method has not been called.
46898      *   This does not activate the panel, just updates its content.
46899      */
46900     refresh : function(){
46901         if(this.refreshDelegate){
46902            this.loaded = false;
46903            this.refreshDelegate();
46904         }
46905     },
46906     
46907     /**
46908      * Destroys this panel
46909      */
46910     destroy : function(){
46911         this.el.removeAllListeners();
46912         var tempEl = document.createElement("span");
46913         tempEl.appendChild(this.el.dom);
46914         tempEl.innerHTML = "";
46915         this.el.remove();
46916         this.el = null;
46917     },
46918     
46919     /**
46920      * form - if the content panel contains a form - this is a reference to it.
46921      * @type {Roo.form.Form}
46922      */
46923     form : false,
46924     /**
46925      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46926      *    This contains a reference to it.
46927      * @type {Roo.View}
46928      */
46929     view : false,
46930     
46931       /**
46932      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46933      * <pre><code>
46934
46935 layout.addxtype({
46936        xtype : 'Form',
46937        items: [ .... ]
46938    }
46939 );
46940
46941 </code></pre>
46942      * @param {Object} cfg Xtype definition of item to add.
46943      */
46944     
46945     addxtype : function(cfg) {
46946         // add form..
46947         if (cfg.xtype.match(/^Form$/)) {
46948             var el = this.el.createChild();
46949
46950             this.form = new  Roo.form.Form(cfg);
46951             
46952             
46953             if ( this.form.allItems.length) this.form.render(el.dom);
46954             return this.form;
46955         }
46956         // should only have one of theses..
46957         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46958             // views..
46959             cfg.el = this.el.appendChild(document.createElement("div"));
46960             // factory?
46961             
46962             var ret = new Roo.factory(cfg);
46963             ret.render && ret.render(false, ''); // render blank..
46964             this.view = ret;
46965             return ret;
46966         }
46967         return false;
46968     }
46969 });
46970
46971 /**
46972  * @class Roo.GridPanel
46973  * @extends Roo.ContentPanel
46974  * @constructor
46975  * Create a new GridPanel.
46976  * @param {Roo.grid.Grid} grid The grid for this panel
46977  * @param {String/Object} config A string to set only the panel's title, or a config object
46978  */
46979 Roo.GridPanel = function(grid, config){
46980     
46981   
46982     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46983         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46984         
46985     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46986     
46987     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46988     
46989     if(this.toolbar){
46990         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46991     }
46992     // xtype created footer. - not sure if will work as we normally have to render first..
46993     if (this.footer && !this.footer.el && this.footer.xtype) {
46994         
46995         this.footer.container = this.grid.getView().getFooterPanel(true);
46996         this.footer.dataSource = this.grid.dataSource;
46997         this.footer = Roo.factory(this.footer, Roo);
46998         
46999     }
47000     
47001     grid.monitorWindowResize = false; // turn off autosizing
47002     grid.autoHeight = false;
47003     grid.autoWidth = false;
47004     this.grid = grid;
47005     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47006 };
47007
47008 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47009     getId : function(){
47010         return this.grid.id;
47011     },
47012     
47013     /**
47014      * Returns the grid for this panel
47015      * @return {Roo.grid.Grid} 
47016      */
47017     getGrid : function(){
47018         return this.grid;    
47019     },
47020     
47021     setSize : function(width, height){
47022         if(!this.ignoreResize(width, height)){
47023             var grid = this.grid;
47024             var size = this.adjustForComponents(width, height);
47025             grid.getGridEl().setSize(size.width, size.height);
47026             grid.autoSize();
47027         }
47028     },
47029     
47030     beforeSlide : function(){
47031         this.grid.getView().scroller.clip();
47032     },
47033     
47034     afterSlide : function(){
47035         this.grid.getView().scroller.unclip();
47036     },
47037     
47038     destroy : function(){
47039         this.grid.destroy();
47040         delete this.grid;
47041         Roo.GridPanel.superclass.destroy.call(this); 
47042     }
47043 });
47044
47045
47046 /**
47047  * @class Roo.NestedLayoutPanel
47048  * @extends Roo.ContentPanel
47049  * @constructor
47050  * Create a new NestedLayoutPanel.
47051  * 
47052  * 
47053  * @param {Roo.BorderLayout} layout The layout for this panel
47054  * @param {String/Object} config A string to set only the title or a config object
47055  */
47056 Roo.NestedLayoutPanel = function(layout, config)
47057 {
47058     // construct with only one argument..
47059     /* FIXME - implement nicer consturctors
47060     if (layout.layout) {
47061         config = layout;
47062         layout = config.layout;
47063         delete config.layout;
47064     }
47065     if (layout.xtype && !layout.getEl) {
47066         // then layout needs constructing..
47067         layout = Roo.factory(layout, Roo);
47068     }
47069     */
47070     
47071     
47072     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47073     
47074     layout.monitorWindowResize = false; // turn off autosizing
47075     this.layout = layout;
47076     this.layout.getEl().addClass("x-layout-nested-layout");
47077     
47078     
47079     
47080     
47081 };
47082
47083 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47084
47085     setSize : function(width, height){
47086         if(!this.ignoreResize(width, height)){
47087             var size = this.adjustForComponents(width, height);
47088             var el = this.layout.getEl();
47089             el.setSize(size.width, size.height);
47090             var touch = el.dom.offsetWidth;
47091             this.layout.layout();
47092             // ie requires a double layout on the first pass
47093             if(Roo.isIE && !this.initialized){
47094                 this.initialized = true;
47095                 this.layout.layout();
47096             }
47097         }
47098     },
47099     
47100     // activate all subpanels if not currently active..
47101     
47102     setActiveState : function(active){
47103         this.active = active;
47104         if(!active){
47105             this.fireEvent("deactivate", this);
47106             return;
47107         }
47108         
47109         this.fireEvent("activate", this);
47110         // not sure if this should happen before or after..
47111         if (!this.layout) {
47112             return; // should not happen..
47113         }
47114         var reg = false;
47115         for (var r in this.layout.regions) {
47116             reg = this.layout.getRegion(r);
47117             if (reg.getActivePanel()) {
47118                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47119                 reg.setActivePanel(reg.getActivePanel());
47120                 continue;
47121             }
47122             if (!reg.panels.length) {
47123                 continue;
47124             }
47125             reg.showPanel(reg.getPanel(0));
47126         }
47127         
47128         
47129         
47130         
47131     },
47132     
47133     /**
47134      * Returns the nested BorderLayout for this panel
47135      * @return {Roo.BorderLayout} 
47136      */
47137     getLayout : function(){
47138         return this.layout;
47139     },
47140     
47141      /**
47142      * Adds a xtype elements to the layout of the nested panel
47143      * <pre><code>
47144
47145 panel.addxtype({
47146        xtype : 'ContentPanel',
47147        region: 'west',
47148        items: [ .... ]
47149    }
47150 );
47151
47152 panel.addxtype({
47153         xtype : 'NestedLayoutPanel',
47154         region: 'west',
47155         layout: {
47156            center: { },
47157            west: { }   
47158         },
47159         items : [ ... list of content panels or nested layout panels.. ]
47160    }
47161 );
47162 </code></pre>
47163      * @param {Object} cfg Xtype definition of item to add.
47164      */
47165     addxtype : function(cfg) {
47166         return this.layout.addxtype(cfg);
47167     
47168     }
47169 });
47170
47171 Roo.ScrollPanel = function(el, config, content){
47172     config = config || {};
47173     config.fitToFrame = true;
47174     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47175     
47176     this.el.dom.style.overflow = "hidden";
47177     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47178     this.el.removeClass("x-layout-inactive-content");
47179     this.el.on("mousewheel", this.onWheel, this);
47180
47181     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47182     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47183     up.unselectable(); down.unselectable();
47184     up.on("click", this.scrollUp, this);
47185     down.on("click", this.scrollDown, this);
47186     up.addClassOnOver("x-scroller-btn-over");
47187     down.addClassOnOver("x-scroller-btn-over");
47188     up.addClassOnClick("x-scroller-btn-click");
47189     down.addClassOnClick("x-scroller-btn-click");
47190     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47191
47192     this.resizeEl = this.el;
47193     this.el = wrap; this.up = up; this.down = down;
47194 };
47195
47196 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47197     increment : 100,
47198     wheelIncrement : 5,
47199     scrollUp : function(){
47200         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47201     },
47202
47203     scrollDown : function(){
47204         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47205     },
47206
47207     afterScroll : function(){
47208         var el = this.resizeEl;
47209         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47210         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47211         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47212     },
47213
47214     setSize : function(){
47215         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47216         this.afterScroll();
47217     },
47218
47219     onWheel : function(e){
47220         var d = e.getWheelDelta();
47221         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47222         this.afterScroll();
47223         e.stopEvent();
47224     },
47225
47226     setContent : function(content, loadScripts){
47227         this.resizeEl.update(content, loadScripts);
47228     }
47229
47230 });
47231
47232
47233
47234
47235
47236
47237
47238
47239
47240 /**
47241  * @class Roo.TreePanel
47242  * @extends Roo.ContentPanel
47243  * @constructor
47244  * Create a new TreePanel. - defaults to fit/scoll contents.
47245  * @param {String/Object} config A string to set only the panel's title, or a config object
47246  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47247  */
47248 Roo.TreePanel = function(config){
47249     var el = config.el;
47250     var tree = config.tree;
47251     delete config.tree; 
47252     delete config.el; // hopefull!
47253     
47254     // wrapper for IE7 strict & safari scroll issue
47255     
47256     var treeEl = el.createChild();
47257     config.resizeEl = treeEl;
47258     
47259     
47260     
47261     Roo.TreePanel.superclass.constructor.call(this, el, config);
47262  
47263  
47264     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47265     //console.log(tree);
47266     this.on('activate', function()
47267     {
47268         if (this.tree.rendered) {
47269             return;
47270         }
47271         //console.log('render tree');
47272         this.tree.render();
47273     });
47274     
47275     this.on('resize',  function (cp, w, h) {
47276             this.tree.innerCt.setWidth(w);
47277             this.tree.innerCt.setHeight(h);
47278             this.tree.innerCt.setStyle('overflow-y', 'auto');
47279     });
47280
47281         
47282     
47283 };
47284
47285 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47286     fitToFrame : true,
47287     autoScroll : true
47288 });
47289
47290
47291
47292
47293
47294
47295
47296
47297
47298
47299
47300 /*
47301  * Based on:
47302  * Ext JS Library 1.1.1
47303  * Copyright(c) 2006-2007, Ext JS, LLC.
47304  *
47305  * Originally Released Under LGPL - original licence link has changed is not relivant.
47306  *
47307  * Fork - LGPL
47308  * <script type="text/javascript">
47309  */
47310  
47311
47312 /**
47313  * @class Roo.ReaderLayout
47314  * @extends Roo.BorderLayout
47315  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47316  * center region containing two nested regions (a top one for a list view and one for item preview below),
47317  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47318  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47319  * expedites the setup of the overall layout and regions for this common application style.
47320  * Example:
47321  <pre><code>
47322 var reader = new Roo.ReaderLayout();
47323 var CP = Roo.ContentPanel;  // shortcut for adding
47324
47325 reader.beginUpdate();
47326 reader.add("north", new CP("north", "North"));
47327 reader.add("west", new CP("west", {title: "West"}));
47328 reader.add("east", new CP("east", {title: "East"}));
47329
47330 reader.regions.listView.add(new CP("listView", "List"));
47331 reader.regions.preview.add(new CP("preview", "Preview"));
47332 reader.endUpdate();
47333 </code></pre>
47334 * @constructor
47335 * Create a new ReaderLayout
47336 * @param {Object} config Configuration options
47337 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47338 * document.body if omitted)
47339 */
47340 Roo.ReaderLayout = function(config, renderTo){
47341     var c = config || {size:{}};
47342     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47343         north: c.north !== false ? Roo.apply({
47344             split:false,
47345             initialSize: 32,
47346             titlebar: false
47347         }, c.north) : false,
47348         west: c.west !== false ? Roo.apply({
47349             split:true,
47350             initialSize: 200,
47351             minSize: 175,
47352             maxSize: 400,
47353             titlebar: true,
47354             collapsible: true,
47355             animate: true,
47356             margins:{left:5,right:0,bottom:5,top:5},
47357             cmargins:{left:5,right:5,bottom:5,top:5}
47358         }, c.west) : false,
47359         east: c.east !== false ? Roo.apply({
47360             split:true,
47361             initialSize: 200,
47362             minSize: 175,
47363             maxSize: 400,
47364             titlebar: true,
47365             collapsible: true,
47366             animate: true,
47367             margins:{left:0,right:5,bottom:5,top:5},
47368             cmargins:{left:5,right:5,bottom:5,top:5}
47369         }, c.east) : false,
47370         center: Roo.apply({
47371             tabPosition: 'top',
47372             autoScroll:false,
47373             closeOnTab: true,
47374             titlebar:false,
47375             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47376         }, c.center)
47377     });
47378
47379     this.el.addClass('x-reader');
47380
47381     this.beginUpdate();
47382
47383     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47384         south: c.preview !== false ? Roo.apply({
47385             split:true,
47386             initialSize: 200,
47387             minSize: 100,
47388             autoScroll:true,
47389             collapsible:true,
47390             titlebar: true,
47391             cmargins:{top:5,left:0, right:0, bottom:0}
47392         }, c.preview) : false,
47393         center: Roo.apply({
47394             autoScroll:false,
47395             titlebar:false,
47396             minHeight:200
47397         }, c.listView)
47398     });
47399     this.add('center', new Roo.NestedLayoutPanel(inner,
47400             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47401
47402     this.endUpdate();
47403
47404     this.regions.preview = inner.getRegion('south');
47405     this.regions.listView = inner.getRegion('center');
47406 };
47407
47408 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47409  * Based on:
47410  * Ext JS Library 1.1.1
47411  * Copyright(c) 2006-2007, Ext JS, LLC.
47412  *
47413  * Originally Released Under LGPL - original licence link has changed is not relivant.
47414  *
47415  * Fork - LGPL
47416  * <script type="text/javascript">
47417  */
47418  
47419 /**
47420  * @class Roo.grid.Grid
47421  * @extends Roo.util.Observable
47422  * This class represents the primary interface of a component based grid control.
47423  * <br><br>Usage:<pre><code>
47424  var grid = new Roo.grid.Grid("my-container-id", {
47425      ds: myDataStore,
47426      cm: myColModel,
47427      selModel: mySelectionModel,
47428      autoSizeColumns: true,
47429      monitorWindowResize: false,
47430      trackMouseOver: true
47431  });
47432  // set any options
47433  grid.render();
47434  * </code></pre>
47435  * <b>Common Problems:</b><br/>
47436  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47437  * element will correct this<br/>
47438  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47439  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47440  * are unpredictable.<br/>
47441  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47442  * grid to calculate dimensions/offsets.<br/>
47443   * @constructor
47444  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47445  * The container MUST have some type of size defined for the grid to fill. The container will be
47446  * automatically set to position relative if it isn't already.
47447  * @param {Object} config A config object that sets properties on this grid.
47448  */
47449 Roo.grid.Grid = function(container, config){
47450         // initialize the container
47451         this.container = Roo.get(container);
47452         this.container.update("");
47453         this.container.setStyle("overflow", "hidden");
47454     this.container.addClass('x-grid-container');
47455
47456     this.id = this.container.id;
47457
47458     Roo.apply(this, config);
47459     // check and correct shorthanded configs
47460     if(this.ds){
47461         this.dataSource = this.ds;
47462         delete this.ds;
47463     }
47464     if(this.cm){
47465         this.colModel = this.cm;
47466         delete this.cm;
47467     }
47468     if(this.sm){
47469         this.selModel = this.sm;
47470         delete this.sm;
47471     }
47472
47473     if (this.selModel) {
47474         this.selModel = Roo.factory(this.selModel, Roo.grid);
47475         this.sm = this.selModel;
47476         this.sm.xmodule = this.xmodule || false;
47477     }
47478     if (typeof(this.colModel.config) == 'undefined') {
47479         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47480         this.cm = this.colModel;
47481         this.cm.xmodule = this.xmodule || false;
47482     }
47483     if (this.dataSource) {
47484         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47485         this.ds = this.dataSource;
47486         this.ds.xmodule = this.xmodule || false;
47487          
47488     }
47489     
47490     
47491     
47492     if(this.width){
47493         this.container.setWidth(this.width);
47494     }
47495
47496     if(this.height){
47497         this.container.setHeight(this.height);
47498     }
47499     /** @private */
47500         this.addEvents({
47501         // raw events
47502         /**
47503          * @event click
47504          * The raw click event for the entire grid.
47505          * @param {Roo.EventObject} e
47506          */
47507         "click" : true,
47508         /**
47509          * @event dblclick
47510          * The raw dblclick event for the entire grid.
47511          * @param {Roo.EventObject} e
47512          */
47513         "dblclick" : true,
47514         /**
47515          * @event contextmenu
47516          * The raw contextmenu event for the entire grid.
47517          * @param {Roo.EventObject} e
47518          */
47519         "contextmenu" : true,
47520         /**
47521          * @event mousedown
47522          * The raw mousedown event for the entire grid.
47523          * @param {Roo.EventObject} e
47524          */
47525         "mousedown" : true,
47526         /**
47527          * @event mouseup
47528          * The raw mouseup event for the entire grid.
47529          * @param {Roo.EventObject} e
47530          */
47531         "mouseup" : true,
47532         /**
47533          * @event mouseover
47534          * The raw mouseover event for the entire grid.
47535          * @param {Roo.EventObject} e
47536          */
47537         "mouseover" : true,
47538         /**
47539          * @event mouseout
47540          * The raw mouseout event for the entire grid.
47541          * @param {Roo.EventObject} e
47542          */
47543         "mouseout" : true,
47544         /**
47545          * @event keypress
47546          * The raw keypress event for the entire grid.
47547          * @param {Roo.EventObject} e
47548          */
47549         "keypress" : true,
47550         /**
47551          * @event keydown
47552          * The raw keydown event for the entire grid.
47553          * @param {Roo.EventObject} e
47554          */
47555         "keydown" : true,
47556
47557         // custom events
47558
47559         /**
47560          * @event cellclick
47561          * Fires when a cell is clicked
47562          * @param {Grid} this
47563          * @param {Number} rowIndex
47564          * @param {Number} columnIndex
47565          * @param {Roo.EventObject} e
47566          */
47567         "cellclick" : true,
47568         /**
47569          * @event celldblclick
47570          * Fires when a cell is double clicked
47571          * @param {Grid} this
47572          * @param {Number} rowIndex
47573          * @param {Number} columnIndex
47574          * @param {Roo.EventObject} e
47575          */
47576         "celldblclick" : true,
47577         /**
47578          * @event rowclick
47579          * Fires when a row is clicked
47580          * @param {Grid} this
47581          * @param {Number} rowIndex
47582          * @param {Roo.EventObject} e
47583          */
47584         "rowclick" : true,
47585         /**
47586          * @event rowdblclick
47587          * Fires when a row is double clicked
47588          * @param {Grid} this
47589          * @param {Number} rowIndex
47590          * @param {Roo.EventObject} e
47591          */
47592         "rowdblclick" : true,
47593         /**
47594          * @event headerclick
47595          * Fires when a header is clicked
47596          * @param {Grid} this
47597          * @param {Number} columnIndex
47598          * @param {Roo.EventObject} e
47599          */
47600         "headerclick" : true,
47601         /**
47602          * @event headerdblclick
47603          * Fires when a header cell is double clicked
47604          * @param {Grid} this
47605          * @param {Number} columnIndex
47606          * @param {Roo.EventObject} e
47607          */
47608         "headerdblclick" : true,
47609         /**
47610          * @event rowcontextmenu
47611          * Fires when a row is right clicked
47612          * @param {Grid} this
47613          * @param {Number} rowIndex
47614          * @param {Roo.EventObject} e
47615          */
47616         "rowcontextmenu" : true,
47617         /**
47618          * @event cellcontextmenu
47619          * Fires when a cell is right clicked
47620          * @param {Grid} this
47621          * @param {Number} rowIndex
47622          * @param {Number} cellIndex
47623          * @param {Roo.EventObject} e
47624          */
47625          "cellcontextmenu" : true,
47626         /**
47627          * @event headercontextmenu
47628          * Fires when a header is right clicked
47629          * @param {Grid} this
47630          * @param {Number} columnIndex
47631          * @param {Roo.EventObject} e
47632          */
47633         "headercontextmenu" : true,
47634         /**
47635          * @event bodyscroll
47636          * Fires when the body element is scrolled
47637          * @param {Number} scrollLeft
47638          * @param {Number} scrollTop
47639          */
47640         "bodyscroll" : true,
47641         /**
47642          * @event columnresize
47643          * Fires when the user resizes a column
47644          * @param {Number} columnIndex
47645          * @param {Number} newSize
47646          */
47647         "columnresize" : true,
47648         /**
47649          * @event columnmove
47650          * Fires when the user moves a column
47651          * @param {Number} oldIndex
47652          * @param {Number} newIndex
47653          */
47654         "columnmove" : true,
47655         /**
47656          * @event startdrag
47657          * Fires when row(s) start being dragged
47658          * @param {Grid} this
47659          * @param {Roo.GridDD} dd The drag drop object
47660          * @param {event} e The raw browser event
47661          */
47662         "startdrag" : true,
47663         /**
47664          * @event enddrag
47665          * Fires when a drag operation is complete
47666          * @param {Grid} this
47667          * @param {Roo.GridDD} dd The drag drop object
47668          * @param {event} e The raw browser event
47669          */
47670         "enddrag" : true,
47671         /**
47672          * @event dragdrop
47673          * Fires when dragged row(s) are dropped on a valid DD target
47674          * @param {Grid} this
47675          * @param {Roo.GridDD} dd The drag drop object
47676          * @param {String} targetId The target drag drop object
47677          * @param {event} e The raw browser event
47678          */
47679         "dragdrop" : true,
47680         /**
47681          * @event dragover
47682          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47683          * @param {Grid} this
47684          * @param {Roo.GridDD} dd The drag drop object
47685          * @param {String} targetId The target drag drop object
47686          * @param {event} e The raw browser event
47687          */
47688         "dragover" : true,
47689         /**
47690          * @event dragenter
47691          *  Fires when the dragged row(s) first cross another DD target while being dragged
47692          * @param {Grid} this
47693          * @param {Roo.GridDD} dd The drag drop object
47694          * @param {String} targetId The target drag drop object
47695          * @param {event} e The raw browser event
47696          */
47697         "dragenter" : true,
47698         /**
47699          * @event dragout
47700          * Fires when the dragged row(s) leave another DD target while being dragged
47701          * @param {Grid} this
47702          * @param {Roo.GridDD} dd The drag drop object
47703          * @param {String} targetId The target drag drop object
47704          * @param {event} e The raw browser event
47705          */
47706         "dragout" : true,
47707         /**
47708          * @event rowclass
47709          * Fires when a row is rendered, so you can change add a style to it.
47710          * @param {GridView} gridview   The grid view
47711          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47712          */
47713         'rowclass' : true,
47714
47715         /**
47716          * @event render
47717          * Fires when the grid is rendered
47718          * @param {Grid} grid
47719          */
47720         'render' : true
47721     });
47722
47723     Roo.grid.Grid.superclass.constructor.call(this);
47724 };
47725 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47726     
47727     /**
47728      * @cfg {String} ddGroup - drag drop group.
47729      */
47730
47731     /**
47732      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47733      */
47734     minColumnWidth : 25,
47735
47736     /**
47737      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47738      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47739      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47740      */
47741     autoSizeColumns : false,
47742
47743     /**
47744      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47745      */
47746     autoSizeHeaders : true,
47747
47748     /**
47749      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47750      */
47751     monitorWindowResize : true,
47752
47753     /**
47754      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47755      * rows measured to get a columns size. Default is 0 (all rows).
47756      */
47757     maxRowsToMeasure : 0,
47758
47759     /**
47760      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47761      */
47762     trackMouseOver : true,
47763
47764     /**
47765     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47766     */
47767     
47768     /**
47769     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47770     */
47771     enableDragDrop : false,
47772     
47773     /**
47774     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47775     */
47776     enableColumnMove : true,
47777     
47778     /**
47779     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47780     */
47781     enableColumnHide : true,
47782     
47783     /**
47784     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47785     */
47786     enableRowHeightSync : false,
47787     
47788     /**
47789     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47790     */
47791     stripeRows : true,
47792     
47793     /**
47794     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47795     */
47796     autoHeight : false,
47797
47798     /**
47799      * @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.
47800      */
47801     autoExpandColumn : false,
47802
47803     /**
47804     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47805     * Default is 50.
47806     */
47807     autoExpandMin : 50,
47808
47809     /**
47810     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47811     */
47812     autoExpandMax : 1000,
47813
47814     /**
47815     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47816     */
47817     view : null,
47818
47819     /**
47820     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47821     */
47822     loadMask : false,
47823     /**
47824     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47825     */
47826     dropTarget: false,
47827     
47828    
47829     
47830     // private
47831     rendered : false,
47832
47833     /**
47834     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47835     * of a fixed width. Default is false.
47836     */
47837     /**
47838     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47839     */
47840     /**
47841      * Called once after all setup has been completed and the grid is ready to be rendered.
47842      * @return {Roo.grid.Grid} this
47843      */
47844     render : function()
47845     {
47846         var c = this.container;
47847         // try to detect autoHeight/width mode
47848         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47849             this.autoHeight = true;
47850         }
47851         var view = this.getView();
47852         view.init(this);
47853
47854         c.on("click", this.onClick, this);
47855         c.on("dblclick", this.onDblClick, this);
47856         c.on("contextmenu", this.onContextMenu, this);
47857         c.on("keydown", this.onKeyDown, this);
47858
47859         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47860
47861         this.getSelectionModel().init(this);
47862
47863         view.render();
47864
47865         if(this.loadMask){
47866             this.loadMask = new Roo.LoadMask(this.container,
47867                     Roo.apply({store:this.dataSource}, this.loadMask));
47868         }
47869         
47870         
47871         if (this.toolbar && this.toolbar.xtype) {
47872             this.toolbar.container = this.getView().getHeaderPanel(true);
47873             this.toolbar = new Roo.Toolbar(this.toolbar);
47874         }
47875         if (this.footer && this.footer.xtype) {
47876             this.footer.dataSource = this.getDataSource();
47877             this.footer.container = this.getView().getFooterPanel(true);
47878             this.footer = Roo.factory(this.footer, Roo);
47879         }
47880         if (this.dropTarget && this.dropTarget.xtype) {
47881             delete this.dropTarget.xtype;
47882             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47883         }
47884         
47885         
47886         this.rendered = true;
47887         this.fireEvent('render', this);
47888         return this;
47889     },
47890
47891         /**
47892          * Reconfigures the grid to use a different Store and Column Model.
47893          * The View will be bound to the new objects and refreshed.
47894          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47895          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47896          */
47897     reconfigure : function(dataSource, colModel){
47898         if(this.loadMask){
47899             this.loadMask.destroy();
47900             this.loadMask = new Roo.LoadMask(this.container,
47901                     Roo.apply({store:dataSource}, this.loadMask));
47902         }
47903         this.view.bind(dataSource, colModel);
47904         this.dataSource = dataSource;
47905         this.colModel = colModel;
47906         this.view.refresh(true);
47907     },
47908
47909     // private
47910     onKeyDown : function(e){
47911         this.fireEvent("keydown", e);
47912     },
47913
47914     /**
47915      * Destroy this grid.
47916      * @param {Boolean} removeEl True to remove the element
47917      */
47918     destroy : function(removeEl, keepListeners){
47919         if(this.loadMask){
47920             this.loadMask.destroy();
47921         }
47922         var c = this.container;
47923         c.removeAllListeners();
47924         this.view.destroy();
47925         this.colModel.purgeListeners();
47926         if(!keepListeners){
47927             this.purgeListeners();
47928         }
47929         c.update("");
47930         if(removeEl === true){
47931             c.remove();
47932         }
47933     },
47934
47935     // private
47936     processEvent : function(name, e){
47937         this.fireEvent(name, e);
47938         var t = e.getTarget();
47939         var v = this.view;
47940         var header = v.findHeaderIndex(t);
47941         if(header !== false){
47942             this.fireEvent("header" + name, this, header, e);
47943         }else{
47944             var row = v.findRowIndex(t);
47945             var cell = v.findCellIndex(t);
47946             if(row !== false){
47947                 this.fireEvent("row" + name, this, row, e);
47948                 if(cell !== false){
47949                     this.fireEvent("cell" + name, this, row, cell, e);
47950                 }
47951             }
47952         }
47953     },
47954
47955     // private
47956     onClick : function(e){
47957         this.processEvent("click", e);
47958     },
47959
47960     // private
47961     onContextMenu : function(e, t){
47962         this.processEvent("contextmenu", e);
47963     },
47964
47965     // private
47966     onDblClick : function(e){
47967         this.processEvent("dblclick", e);
47968     },
47969
47970     // private
47971     walkCells : function(row, col, step, fn, scope){
47972         var cm = this.colModel, clen = cm.getColumnCount();
47973         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47974         if(step < 0){
47975             if(col < 0){
47976                 row--;
47977                 first = false;
47978             }
47979             while(row >= 0){
47980                 if(!first){
47981                     col = clen-1;
47982                 }
47983                 first = false;
47984                 while(col >= 0){
47985                     if(fn.call(scope || this, row, col, cm) === true){
47986                         return [row, col];
47987                     }
47988                     col--;
47989                 }
47990                 row--;
47991             }
47992         } else {
47993             if(col >= clen){
47994                 row++;
47995                 first = false;
47996             }
47997             while(row < rlen){
47998                 if(!first){
47999                     col = 0;
48000                 }
48001                 first = false;
48002                 while(col < clen){
48003                     if(fn.call(scope || this, row, col, cm) === true){
48004                         return [row, col];
48005                     }
48006                     col++;
48007                 }
48008                 row++;
48009             }
48010         }
48011         return null;
48012     },
48013
48014     // private
48015     getSelections : function(){
48016         return this.selModel.getSelections();
48017     },
48018
48019     /**
48020      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48021      * but if manual update is required this method will initiate it.
48022      */
48023     autoSize : function(){
48024         if(this.rendered){
48025             this.view.layout();
48026             if(this.view.adjustForScroll){
48027                 this.view.adjustForScroll();
48028             }
48029         }
48030     },
48031
48032     /**
48033      * Returns the grid's underlying element.
48034      * @return {Element} The element
48035      */
48036     getGridEl : function(){
48037         return this.container;
48038     },
48039
48040     // private for compatibility, overridden by editor grid
48041     stopEditing : function(){},
48042
48043     /**
48044      * Returns the grid's SelectionModel.
48045      * @return {SelectionModel}
48046      */
48047     getSelectionModel : function(){
48048         if(!this.selModel){
48049             this.selModel = new Roo.grid.RowSelectionModel();
48050         }
48051         return this.selModel;
48052     },
48053
48054     /**
48055      * Returns the grid's DataSource.
48056      * @return {DataSource}
48057      */
48058     getDataSource : function(){
48059         return this.dataSource;
48060     },
48061
48062     /**
48063      * Returns the grid's ColumnModel.
48064      * @return {ColumnModel}
48065      */
48066     getColumnModel : function(){
48067         return this.colModel;
48068     },
48069
48070     /**
48071      * Returns the grid's GridView object.
48072      * @return {GridView}
48073      */
48074     getView : function(){
48075         if(!this.view){
48076             this.view = new Roo.grid.GridView(this.viewConfig);
48077         }
48078         return this.view;
48079     },
48080     /**
48081      * Called to get grid's drag proxy text, by default returns this.ddText.
48082      * @return {String}
48083      */
48084     getDragDropText : function(){
48085         var count = this.selModel.getCount();
48086         return String.format(this.ddText, count, count == 1 ? '' : 's');
48087     }
48088 });
48089 /**
48090  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48091  * %0 is replaced with the number of selected rows.
48092  * @type String
48093  */
48094 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48095  * Based on:
48096  * Ext JS Library 1.1.1
48097  * Copyright(c) 2006-2007, Ext JS, LLC.
48098  *
48099  * Originally Released Under LGPL - original licence link has changed is not relivant.
48100  *
48101  * Fork - LGPL
48102  * <script type="text/javascript">
48103  */
48104  
48105 Roo.grid.AbstractGridView = function(){
48106         this.grid = null;
48107         
48108         this.events = {
48109             "beforerowremoved" : true,
48110             "beforerowsinserted" : true,
48111             "beforerefresh" : true,
48112             "rowremoved" : true,
48113             "rowsinserted" : true,
48114             "rowupdated" : true,
48115             "refresh" : true
48116         };
48117     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48118 };
48119
48120 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48121     rowClass : "x-grid-row",
48122     cellClass : "x-grid-cell",
48123     tdClass : "x-grid-td",
48124     hdClass : "x-grid-hd",
48125     splitClass : "x-grid-hd-split",
48126     
48127         init: function(grid){
48128         this.grid = grid;
48129                 var cid = this.grid.getGridEl().id;
48130         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48131         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48132         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48133         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48134         },
48135         
48136         getColumnRenderers : function(){
48137         var renderers = [];
48138         var cm = this.grid.colModel;
48139         var colCount = cm.getColumnCount();
48140         for(var i = 0; i < colCount; i++){
48141             renderers[i] = cm.getRenderer(i);
48142         }
48143         return renderers;
48144     },
48145     
48146     getColumnIds : function(){
48147         var ids = [];
48148         var cm = this.grid.colModel;
48149         var colCount = cm.getColumnCount();
48150         for(var i = 0; i < colCount; i++){
48151             ids[i] = cm.getColumnId(i);
48152         }
48153         return ids;
48154     },
48155     
48156     getDataIndexes : function(){
48157         if(!this.indexMap){
48158             this.indexMap = this.buildIndexMap();
48159         }
48160         return this.indexMap.colToData;
48161     },
48162     
48163     getColumnIndexByDataIndex : function(dataIndex){
48164         if(!this.indexMap){
48165             this.indexMap = this.buildIndexMap();
48166         }
48167         return this.indexMap.dataToCol[dataIndex];
48168     },
48169     
48170     /**
48171      * Set a css style for a column dynamically. 
48172      * @param {Number} colIndex The index of the column
48173      * @param {String} name The css property name
48174      * @param {String} value The css value
48175      */
48176     setCSSStyle : function(colIndex, name, value){
48177         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48178         Roo.util.CSS.updateRule(selector, name, value);
48179     },
48180     
48181     generateRules : function(cm){
48182         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48183         Roo.util.CSS.removeStyleSheet(rulesId);
48184         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48185             var cid = cm.getColumnId(i);
48186             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48187                          this.tdSelector, cid, " {\n}\n",
48188                          this.hdSelector, cid, " {\n}\n",
48189                          this.splitSelector, cid, " {\n}\n");
48190         }
48191         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48192     }
48193 });/*
48194  * Based on:
48195  * Ext JS Library 1.1.1
48196  * Copyright(c) 2006-2007, Ext JS, LLC.
48197  *
48198  * Originally Released Under LGPL - original licence link has changed is not relivant.
48199  *
48200  * Fork - LGPL
48201  * <script type="text/javascript">
48202  */
48203
48204 // private
48205 // This is a support class used internally by the Grid components
48206 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48207     this.grid = grid;
48208     this.view = grid.getView();
48209     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48210     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48211     if(hd2){
48212         this.setHandleElId(Roo.id(hd));
48213         this.setOuterHandleElId(Roo.id(hd2));
48214     }
48215     this.scroll = false;
48216 };
48217 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48218     maxDragWidth: 120,
48219     getDragData : function(e){
48220         var t = Roo.lib.Event.getTarget(e);
48221         var h = this.view.findHeaderCell(t);
48222         if(h){
48223             return {ddel: h.firstChild, header:h};
48224         }
48225         return false;
48226     },
48227
48228     onInitDrag : function(e){
48229         this.view.headersDisabled = true;
48230         var clone = this.dragData.ddel.cloneNode(true);
48231         clone.id = Roo.id();
48232         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48233         this.proxy.update(clone);
48234         return true;
48235     },
48236
48237     afterValidDrop : function(){
48238         var v = this.view;
48239         setTimeout(function(){
48240             v.headersDisabled = false;
48241         }, 50);
48242     },
48243
48244     afterInvalidDrop : function(){
48245         var v = this.view;
48246         setTimeout(function(){
48247             v.headersDisabled = false;
48248         }, 50);
48249     }
48250 });
48251 /*
48252  * Based on:
48253  * Ext JS Library 1.1.1
48254  * Copyright(c) 2006-2007, Ext JS, LLC.
48255  *
48256  * Originally Released Under LGPL - original licence link has changed is not relivant.
48257  *
48258  * Fork - LGPL
48259  * <script type="text/javascript">
48260  */
48261 // private
48262 // This is a support class used internally by the Grid components
48263 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48264     this.grid = grid;
48265     this.view = grid.getView();
48266     // split the proxies so they don't interfere with mouse events
48267     this.proxyTop = Roo.DomHelper.append(document.body, {
48268         cls:"col-move-top", html:"&#160;"
48269     }, true);
48270     this.proxyBottom = Roo.DomHelper.append(document.body, {
48271         cls:"col-move-bottom", html:"&#160;"
48272     }, true);
48273     this.proxyTop.hide = this.proxyBottom.hide = function(){
48274         this.setLeftTop(-100,-100);
48275         this.setStyle("visibility", "hidden");
48276     };
48277     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48278     // temporarily disabled
48279     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48280     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48281 };
48282 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48283     proxyOffsets : [-4, -9],
48284     fly: Roo.Element.fly,
48285
48286     getTargetFromEvent : function(e){
48287         var t = Roo.lib.Event.getTarget(e);
48288         var cindex = this.view.findCellIndex(t);
48289         if(cindex !== false){
48290             return this.view.getHeaderCell(cindex);
48291         }
48292         return null;
48293     },
48294
48295     nextVisible : function(h){
48296         var v = this.view, cm = this.grid.colModel;
48297         h = h.nextSibling;
48298         while(h){
48299             if(!cm.isHidden(v.getCellIndex(h))){
48300                 return h;
48301             }
48302             h = h.nextSibling;
48303         }
48304         return null;
48305     },
48306
48307     prevVisible : function(h){
48308         var v = this.view, cm = this.grid.colModel;
48309         h = h.prevSibling;
48310         while(h){
48311             if(!cm.isHidden(v.getCellIndex(h))){
48312                 return h;
48313             }
48314             h = h.prevSibling;
48315         }
48316         return null;
48317     },
48318
48319     positionIndicator : function(h, n, e){
48320         var x = Roo.lib.Event.getPageX(e);
48321         var r = Roo.lib.Dom.getRegion(n.firstChild);
48322         var px, pt, py = r.top + this.proxyOffsets[1];
48323         if((r.right - x) <= (r.right-r.left)/2){
48324             px = r.right+this.view.borderWidth;
48325             pt = "after";
48326         }else{
48327             px = r.left;
48328             pt = "before";
48329         }
48330         var oldIndex = this.view.getCellIndex(h);
48331         var newIndex = this.view.getCellIndex(n);
48332
48333         if(this.grid.colModel.isFixed(newIndex)){
48334             return false;
48335         }
48336
48337         var locked = this.grid.colModel.isLocked(newIndex);
48338
48339         if(pt == "after"){
48340             newIndex++;
48341         }
48342         if(oldIndex < newIndex){
48343             newIndex--;
48344         }
48345         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48346             return false;
48347         }
48348         px +=  this.proxyOffsets[0];
48349         this.proxyTop.setLeftTop(px, py);
48350         this.proxyTop.show();
48351         if(!this.bottomOffset){
48352             this.bottomOffset = this.view.mainHd.getHeight();
48353         }
48354         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48355         this.proxyBottom.show();
48356         return pt;
48357     },
48358
48359     onNodeEnter : function(n, dd, e, data){
48360         if(data.header != n){
48361             this.positionIndicator(data.header, n, e);
48362         }
48363     },
48364
48365     onNodeOver : function(n, dd, e, data){
48366         var result = false;
48367         if(data.header != n){
48368             result = this.positionIndicator(data.header, n, e);
48369         }
48370         if(!result){
48371             this.proxyTop.hide();
48372             this.proxyBottom.hide();
48373         }
48374         return result ? this.dropAllowed : this.dropNotAllowed;
48375     },
48376
48377     onNodeOut : function(n, dd, e, data){
48378         this.proxyTop.hide();
48379         this.proxyBottom.hide();
48380     },
48381
48382     onNodeDrop : function(n, dd, e, data){
48383         var h = data.header;
48384         if(h != n){
48385             var cm = this.grid.colModel;
48386             var x = Roo.lib.Event.getPageX(e);
48387             var r = Roo.lib.Dom.getRegion(n.firstChild);
48388             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48389             var oldIndex = this.view.getCellIndex(h);
48390             var newIndex = this.view.getCellIndex(n);
48391             var locked = cm.isLocked(newIndex);
48392             if(pt == "after"){
48393                 newIndex++;
48394             }
48395             if(oldIndex < newIndex){
48396                 newIndex--;
48397             }
48398             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48399                 return false;
48400             }
48401             cm.setLocked(oldIndex, locked, true);
48402             cm.moveColumn(oldIndex, newIndex);
48403             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48404             return true;
48405         }
48406         return false;
48407     }
48408 });
48409 /*
48410  * Based on:
48411  * Ext JS Library 1.1.1
48412  * Copyright(c) 2006-2007, Ext JS, LLC.
48413  *
48414  * Originally Released Under LGPL - original licence link has changed is not relivant.
48415  *
48416  * Fork - LGPL
48417  * <script type="text/javascript">
48418  */
48419   
48420 /**
48421  * @class Roo.grid.GridView
48422  * @extends Roo.util.Observable
48423  *
48424  * @constructor
48425  * @param {Object} config
48426  */
48427 Roo.grid.GridView = function(config){
48428     Roo.grid.GridView.superclass.constructor.call(this);
48429     this.el = null;
48430
48431     Roo.apply(this, config);
48432 };
48433
48434 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48435
48436     
48437     rowClass : "x-grid-row",
48438
48439     cellClass : "x-grid-col",
48440
48441     tdClass : "x-grid-td",
48442
48443     hdClass : "x-grid-hd",
48444
48445     splitClass : "x-grid-split",
48446
48447     sortClasses : ["sort-asc", "sort-desc"],
48448
48449     enableMoveAnim : false,
48450
48451     hlColor: "C3DAF9",
48452
48453     dh : Roo.DomHelper,
48454
48455     fly : Roo.Element.fly,
48456
48457     css : Roo.util.CSS,
48458
48459     borderWidth: 1,
48460
48461     splitOffset: 3,
48462
48463     scrollIncrement : 22,
48464
48465     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48466
48467     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48468
48469     bind : function(ds, cm){
48470         if(this.ds){
48471             this.ds.un("load", this.onLoad, this);
48472             this.ds.un("datachanged", this.onDataChange, this);
48473             this.ds.un("add", this.onAdd, this);
48474             this.ds.un("remove", this.onRemove, this);
48475             this.ds.un("update", this.onUpdate, this);
48476             this.ds.un("clear", this.onClear, this);
48477         }
48478         if(ds){
48479             ds.on("load", this.onLoad, this);
48480             ds.on("datachanged", this.onDataChange, this);
48481             ds.on("add", this.onAdd, this);
48482             ds.on("remove", this.onRemove, this);
48483             ds.on("update", this.onUpdate, this);
48484             ds.on("clear", this.onClear, this);
48485         }
48486         this.ds = ds;
48487
48488         if(this.cm){
48489             this.cm.un("widthchange", this.onColWidthChange, this);
48490             this.cm.un("headerchange", this.onHeaderChange, this);
48491             this.cm.un("hiddenchange", this.onHiddenChange, this);
48492             this.cm.un("columnmoved", this.onColumnMove, this);
48493             this.cm.un("columnlockchange", this.onColumnLock, this);
48494         }
48495         if(cm){
48496             this.generateRules(cm);
48497             cm.on("widthchange", this.onColWidthChange, this);
48498             cm.on("headerchange", this.onHeaderChange, this);
48499             cm.on("hiddenchange", this.onHiddenChange, this);
48500             cm.on("columnmoved", this.onColumnMove, this);
48501             cm.on("columnlockchange", this.onColumnLock, this);
48502         }
48503         this.cm = cm;
48504     },
48505
48506     init: function(grid){
48507         Roo.grid.GridView.superclass.init.call(this, grid);
48508
48509         this.bind(grid.dataSource, grid.colModel);
48510
48511         grid.on("headerclick", this.handleHeaderClick, this);
48512
48513         if(grid.trackMouseOver){
48514             grid.on("mouseover", this.onRowOver, this);
48515             grid.on("mouseout", this.onRowOut, this);
48516         }
48517         grid.cancelTextSelection = function(){};
48518         this.gridId = grid.id;
48519
48520         var tpls = this.templates || {};
48521
48522         if(!tpls.master){
48523             tpls.master = new Roo.Template(
48524                '<div class="x-grid" hidefocus="true">',
48525                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48526                   '<div class="x-grid-topbar"></div>',
48527                   '<div class="x-grid-scroller"><div></div></div>',
48528                   '<div class="x-grid-locked">',
48529                       '<div class="x-grid-header">{lockedHeader}</div>',
48530                       '<div class="x-grid-body">{lockedBody}</div>',
48531                   "</div>",
48532                   '<div class="x-grid-viewport">',
48533                       '<div class="x-grid-header">{header}</div>',
48534                       '<div class="x-grid-body">{body}</div>',
48535                   "</div>",
48536                   '<div class="x-grid-bottombar"></div>',
48537                  
48538                   '<div class="x-grid-resize-proxy">&#160;</div>',
48539                "</div>"
48540             );
48541             tpls.master.disableformats = true;
48542         }
48543
48544         if(!tpls.header){
48545             tpls.header = new Roo.Template(
48546                '<table border="0" cellspacing="0" cellpadding="0">',
48547                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48548                "</table>{splits}"
48549             );
48550             tpls.header.disableformats = true;
48551         }
48552         tpls.header.compile();
48553
48554         if(!tpls.hcell){
48555             tpls.hcell = new Roo.Template(
48556                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48557                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48558                 "</div></td>"
48559              );
48560              tpls.hcell.disableFormats = true;
48561         }
48562         tpls.hcell.compile();
48563
48564         if(!tpls.hsplit){
48565             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48566             tpls.hsplit.disableFormats = true;
48567         }
48568         tpls.hsplit.compile();
48569
48570         if(!tpls.body){
48571             tpls.body = new Roo.Template(
48572                '<table border="0" cellspacing="0" cellpadding="0">',
48573                "<tbody>{rows}</tbody>",
48574                "</table>"
48575             );
48576             tpls.body.disableFormats = true;
48577         }
48578         tpls.body.compile();
48579
48580         if(!tpls.row){
48581             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48582             tpls.row.disableFormats = true;
48583         }
48584         tpls.row.compile();
48585
48586         if(!tpls.cell){
48587             tpls.cell = new Roo.Template(
48588                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48589                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48590                 "</td>"
48591             );
48592             tpls.cell.disableFormats = true;
48593         }
48594         tpls.cell.compile();
48595
48596         this.templates = tpls;
48597     },
48598
48599     // remap these for backwards compat
48600     onColWidthChange : function(){
48601         this.updateColumns.apply(this, arguments);
48602     },
48603     onHeaderChange : function(){
48604         this.updateHeaders.apply(this, arguments);
48605     }, 
48606     onHiddenChange : function(){
48607         this.handleHiddenChange.apply(this, arguments);
48608     },
48609     onColumnMove : function(){
48610         this.handleColumnMove.apply(this, arguments);
48611     },
48612     onColumnLock : function(){
48613         this.handleLockChange.apply(this, arguments);
48614     },
48615
48616     onDataChange : function(){
48617         this.refresh();
48618         this.updateHeaderSortState();
48619     },
48620
48621     onClear : function(){
48622         this.refresh();
48623     },
48624
48625     onUpdate : function(ds, record){
48626         this.refreshRow(record);
48627     },
48628
48629     refreshRow : function(record){
48630         var ds = this.ds, index;
48631         if(typeof record == 'number'){
48632             index = record;
48633             record = ds.getAt(index);
48634         }else{
48635             index = ds.indexOf(record);
48636         }
48637         this.insertRows(ds, index, index, true);
48638         this.onRemove(ds, record, index+1, true);
48639         this.syncRowHeights(index, index);
48640         this.layout();
48641         this.fireEvent("rowupdated", this, index, record);
48642     },
48643
48644     onAdd : function(ds, records, index){
48645         this.insertRows(ds, index, index + (records.length-1));
48646     },
48647
48648     onRemove : function(ds, record, index, isUpdate){
48649         if(isUpdate !== true){
48650             this.fireEvent("beforerowremoved", this, index, record);
48651         }
48652         var bt = this.getBodyTable(), lt = this.getLockedTable();
48653         if(bt.rows[index]){
48654             bt.firstChild.removeChild(bt.rows[index]);
48655         }
48656         if(lt.rows[index]){
48657             lt.firstChild.removeChild(lt.rows[index]);
48658         }
48659         if(isUpdate !== true){
48660             this.stripeRows(index);
48661             this.syncRowHeights(index, index);
48662             this.layout();
48663             this.fireEvent("rowremoved", this, index, record);
48664         }
48665     },
48666
48667     onLoad : function(){
48668         this.scrollToTop();
48669     },
48670
48671     /**
48672      * Scrolls the grid to the top
48673      */
48674     scrollToTop : function(){
48675         if(this.scroller){
48676             this.scroller.dom.scrollTop = 0;
48677             this.syncScroll();
48678         }
48679     },
48680
48681     /**
48682      * Gets a panel in the header of the grid that can be used for toolbars etc.
48683      * After modifying the contents of this panel a call to grid.autoSize() may be
48684      * required to register any changes in size.
48685      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48686      * @return Roo.Element
48687      */
48688     getHeaderPanel : function(doShow){
48689         if(doShow){
48690             this.headerPanel.show();
48691         }
48692         return this.headerPanel;
48693     },
48694
48695     /**
48696      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48697      * After modifying the contents of this panel a call to grid.autoSize() may be
48698      * required to register any changes in size.
48699      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48700      * @return Roo.Element
48701      */
48702     getFooterPanel : function(doShow){
48703         if(doShow){
48704             this.footerPanel.show();
48705         }
48706         return this.footerPanel;
48707     },
48708
48709     initElements : function(){
48710         var E = Roo.Element;
48711         var el = this.grid.getGridEl().dom.firstChild;
48712         var cs = el.childNodes;
48713
48714         this.el = new E(el);
48715         
48716          this.focusEl = new E(el.firstChild);
48717         this.focusEl.swallowEvent("click", true);
48718         
48719         this.headerPanel = new E(cs[1]);
48720         this.headerPanel.enableDisplayMode("block");
48721
48722         this.scroller = new E(cs[2]);
48723         this.scrollSizer = new E(this.scroller.dom.firstChild);
48724
48725         this.lockedWrap = new E(cs[3]);
48726         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48727         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48728
48729         this.mainWrap = new E(cs[4]);
48730         this.mainHd = new E(this.mainWrap.dom.firstChild);
48731         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48732
48733         this.footerPanel = new E(cs[5]);
48734         this.footerPanel.enableDisplayMode("block");
48735
48736         this.resizeProxy = new E(cs[6]);
48737
48738         this.headerSelector = String.format(
48739            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48740            this.lockedHd.id, this.mainHd.id
48741         );
48742
48743         this.splitterSelector = String.format(
48744            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48745            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48746         );
48747     },
48748     idToCssName : function(s)
48749     {
48750         return s.replace(/[^a-z0-9]+/ig, '-');
48751     },
48752
48753     getHeaderCell : function(index){
48754         return Roo.DomQuery.select(this.headerSelector)[index];
48755     },
48756
48757     getHeaderCellMeasure : function(index){
48758         return this.getHeaderCell(index).firstChild;
48759     },
48760
48761     getHeaderCellText : function(index){
48762         return this.getHeaderCell(index).firstChild.firstChild;
48763     },
48764
48765     getLockedTable : function(){
48766         return this.lockedBody.dom.firstChild;
48767     },
48768
48769     getBodyTable : function(){
48770         return this.mainBody.dom.firstChild;
48771     },
48772
48773     getLockedRow : function(index){
48774         return this.getLockedTable().rows[index];
48775     },
48776
48777     getRow : function(index){
48778         return this.getBodyTable().rows[index];
48779     },
48780
48781     getRowComposite : function(index){
48782         if(!this.rowEl){
48783             this.rowEl = new Roo.CompositeElementLite();
48784         }
48785         var els = [], lrow, mrow;
48786         if(lrow = this.getLockedRow(index)){
48787             els.push(lrow);
48788         }
48789         if(mrow = this.getRow(index)){
48790             els.push(mrow);
48791         }
48792         this.rowEl.elements = els;
48793         return this.rowEl;
48794     },
48795     /**
48796      * Gets the 'td' of the cell
48797      * 
48798      * @param {Integer} rowIndex row to select
48799      * @param {Integer} colIndex column to select
48800      * 
48801      * @return {Object} 
48802      */
48803     getCell : function(rowIndex, colIndex){
48804         var locked = this.cm.getLockedCount();
48805         var source;
48806         if(colIndex < locked){
48807             source = this.lockedBody.dom.firstChild;
48808         }else{
48809             source = this.mainBody.dom.firstChild;
48810             colIndex -= locked;
48811         }
48812         return source.rows[rowIndex].childNodes[colIndex];
48813     },
48814
48815     getCellText : function(rowIndex, colIndex){
48816         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48817     },
48818
48819     getCellBox : function(cell){
48820         var b = this.fly(cell).getBox();
48821         if(Roo.isOpera){ // opera fails to report the Y
48822             b.y = cell.offsetTop + this.mainBody.getY();
48823         }
48824         return b;
48825     },
48826
48827     getCellIndex : function(cell){
48828         var id = String(cell.className).match(this.cellRE);
48829         if(id){
48830             return parseInt(id[1], 10);
48831         }
48832         return 0;
48833     },
48834
48835     findHeaderIndex : function(n){
48836         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48837         return r ? this.getCellIndex(r) : false;
48838     },
48839
48840     findHeaderCell : function(n){
48841         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48842         return r ? r : false;
48843     },
48844
48845     findRowIndex : function(n){
48846         if(!n){
48847             return false;
48848         }
48849         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48850         return r ? r.rowIndex : false;
48851     },
48852
48853     findCellIndex : function(node){
48854         var stop = this.el.dom;
48855         while(node && node != stop){
48856             if(this.findRE.test(node.className)){
48857                 return this.getCellIndex(node);
48858             }
48859             node = node.parentNode;
48860         }
48861         return false;
48862     },
48863
48864     getColumnId : function(index){
48865         return this.cm.getColumnId(index);
48866     },
48867
48868     getSplitters : function()
48869     {
48870         if(this.splitterSelector){
48871            return Roo.DomQuery.select(this.splitterSelector);
48872         }else{
48873             return null;
48874       }
48875     },
48876
48877     getSplitter : function(index){
48878         return this.getSplitters()[index];
48879     },
48880
48881     onRowOver : function(e, t){
48882         var row;
48883         if((row = this.findRowIndex(t)) !== false){
48884             this.getRowComposite(row).addClass("x-grid-row-over");
48885         }
48886     },
48887
48888     onRowOut : function(e, t){
48889         var row;
48890         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48891             this.getRowComposite(row).removeClass("x-grid-row-over");
48892         }
48893     },
48894
48895     renderHeaders : function(){
48896         var cm = this.cm;
48897         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48898         var cb = [], lb = [], sb = [], lsb = [], p = {};
48899         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48900             p.cellId = "x-grid-hd-0-" + i;
48901             p.splitId = "x-grid-csplit-0-" + i;
48902             p.id = cm.getColumnId(i);
48903             p.title = cm.getColumnTooltip(i) || "";
48904             p.value = cm.getColumnHeader(i) || "";
48905             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48906             if(!cm.isLocked(i)){
48907                 cb[cb.length] = ct.apply(p);
48908                 sb[sb.length] = st.apply(p);
48909             }else{
48910                 lb[lb.length] = ct.apply(p);
48911                 lsb[lsb.length] = st.apply(p);
48912             }
48913         }
48914         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48915                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48916     },
48917
48918     updateHeaders : function(){
48919         var html = this.renderHeaders();
48920         this.lockedHd.update(html[0]);
48921         this.mainHd.update(html[1]);
48922     },
48923
48924     /**
48925      * Focuses the specified row.
48926      * @param {Number} row The row index
48927      */
48928     focusRow : function(row)
48929     {
48930         //Roo.log('GridView.focusRow');
48931         var x = this.scroller.dom.scrollLeft;
48932         this.focusCell(row, 0, false);
48933         this.scroller.dom.scrollLeft = x;
48934     },
48935
48936     /**
48937      * Focuses the specified cell.
48938      * @param {Number} row The row index
48939      * @param {Number} col The column index
48940      * @param {Boolean} hscroll false to disable horizontal scrolling
48941      */
48942     focusCell : function(row, col, hscroll)
48943     {
48944         //Roo.log('GridView.focusCell');
48945         var el = this.ensureVisible(row, col, hscroll);
48946         this.focusEl.alignTo(el, "tl-tl");
48947         if(Roo.isGecko){
48948             this.focusEl.focus();
48949         }else{
48950             this.focusEl.focus.defer(1, this.focusEl);
48951         }
48952     },
48953
48954     /**
48955      * Scrolls the specified cell into view
48956      * @param {Number} row The row index
48957      * @param {Number} col The column index
48958      * @param {Boolean} hscroll false to disable horizontal scrolling
48959      */
48960     ensureVisible : function(row, col, hscroll)
48961     {
48962         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48963         //return null; //disable for testing.
48964         if(typeof row != "number"){
48965             row = row.rowIndex;
48966         }
48967         if(row < 0 && row >= this.ds.getCount()){
48968             return  null;
48969         }
48970         col = (col !== undefined ? col : 0);
48971         var cm = this.grid.colModel;
48972         while(cm.isHidden(col)){
48973             col++;
48974         }
48975
48976         var el = this.getCell(row, col);
48977         if(!el){
48978             return null;
48979         }
48980         var c = this.scroller.dom;
48981
48982         var ctop = parseInt(el.offsetTop, 10);
48983         var cleft = parseInt(el.offsetLeft, 10);
48984         var cbot = ctop + el.offsetHeight;
48985         var cright = cleft + el.offsetWidth;
48986         
48987         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48988         var stop = parseInt(c.scrollTop, 10);
48989         var sleft = parseInt(c.scrollLeft, 10);
48990         var sbot = stop + ch;
48991         var sright = sleft + c.clientWidth;
48992         /*
48993         Roo.log('GridView.ensureVisible:' +
48994                 ' ctop:' + ctop +
48995                 ' c.clientHeight:' + c.clientHeight +
48996                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48997                 ' stop:' + stop +
48998                 ' cbot:' + cbot +
48999                 ' sbot:' + sbot +
49000                 ' ch:' + ch  
49001                 );
49002         */
49003         if(ctop < stop){
49004              c.scrollTop = ctop;
49005             //Roo.log("set scrolltop to ctop DISABLE?");
49006         }else if(cbot > sbot){
49007             //Roo.log("set scrolltop to cbot-ch");
49008             c.scrollTop = cbot-ch;
49009         }
49010         
49011         if(hscroll !== false){
49012             if(cleft < sleft){
49013                 c.scrollLeft = cleft;
49014             }else if(cright > sright){
49015                 c.scrollLeft = cright-c.clientWidth;
49016             }
49017         }
49018          
49019         return el;
49020     },
49021
49022     updateColumns : function(){
49023         this.grid.stopEditing();
49024         var cm = this.grid.colModel, colIds = this.getColumnIds();
49025         //var totalWidth = cm.getTotalWidth();
49026         var pos = 0;
49027         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49028             //if(cm.isHidden(i)) continue;
49029             var w = cm.getColumnWidth(i);
49030             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49031             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49032         }
49033         this.updateSplitters();
49034     },
49035
49036     generateRules : function(cm){
49037         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49038         Roo.util.CSS.removeStyleSheet(rulesId);
49039         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49040             var cid = cm.getColumnId(i);
49041             var align = '';
49042             if(cm.config[i].align){
49043                 align = 'text-align:'+cm.config[i].align+';';
49044             }
49045             var hidden = '';
49046             if(cm.isHidden(i)){
49047                 hidden = 'display:none;';
49048             }
49049             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49050             ruleBuf.push(
49051                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49052                     this.hdSelector, cid, " {\n", align, width, "}\n",
49053                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49054                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49055         }
49056         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49057     },
49058
49059     updateSplitters : function(){
49060         var cm = this.cm, s = this.getSplitters();
49061         if(s){ // splitters not created yet
49062             var pos = 0, locked = true;
49063             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49064                 if(cm.isHidden(i)) continue;
49065                 var w = cm.getColumnWidth(i); // make sure it's a number
49066                 if(!cm.isLocked(i) && locked){
49067                     pos = 0;
49068                     locked = false;
49069                 }
49070                 pos += w;
49071                 s[i].style.left = (pos-this.splitOffset) + "px";
49072             }
49073         }
49074     },
49075
49076     handleHiddenChange : function(colModel, colIndex, hidden){
49077         if(hidden){
49078             this.hideColumn(colIndex);
49079         }else{
49080             this.unhideColumn(colIndex);
49081         }
49082     },
49083
49084     hideColumn : function(colIndex){
49085         var cid = this.getColumnId(colIndex);
49086         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49087         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49088         if(Roo.isSafari){
49089             this.updateHeaders();
49090         }
49091         this.updateSplitters();
49092         this.layout();
49093     },
49094
49095     unhideColumn : function(colIndex){
49096         var cid = this.getColumnId(colIndex);
49097         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49098         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49099
49100         if(Roo.isSafari){
49101             this.updateHeaders();
49102         }
49103         this.updateSplitters();
49104         this.layout();
49105     },
49106
49107     insertRows : function(dm, firstRow, lastRow, isUpdate){
49108         if(firstRow == 0 && lastRow == dm.getCount()-1){
49109             this.refresh();
49110         }else{
49111             if(!isUpdate){
49112                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49113             }
49114             var s = this.getScrollState();
49115             var markup = this.renderRows(firstRow, lastRow);
49116             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49117             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49118             this.restoreScroll(s);
49119             if(!isUpdate){
49120                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49121                 this.syncRowHeights(firstRow, lastRow);
49122                 this.stripeRows(firstRow);
49123                 this.layout();
49124             }
49125         }
49126     },
49127
49128     bufferRows : function(markup, target, index){
49129         var before = null, trows = target.rows, tbody = target.tBodies[0];
49130         if(index < trows.length){
49131             before = trows[index];
49132         }
49133         var b = document.createElement("div");
49134         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49135         var rows = b.firstChild.rows;
49136         for(var i = 0, len = rows.length; i < len; i++){
49137             if(before){
49138                 tbody.insertBefore(rows[0], before);
49139             }else{
49140                 tbody.appendChild(rows[0]);
49141             }
49142         }
49143         b.innerHTML = "";
49144         b = null;
49145     },
49146
49147     deleteRows : function(dm, firstRow, lastRow){
49148         if(dm.getRowCount()<1){
49149             this.fireEvent("beforerefresh", this);
49150             this.mainBody.update("");
49151             this.lockedBody.update("");
49152             this.fireEvent("refresh", this);
49153         }else{
49154             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49155             var bt = this.getBodyTable();
49156             var tbody = bt.firstChild;
49157             var rows = bt.rows;
49158             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49159                 tbody.removeChild(rows[firstRow]);
49160             }
49161             this.stripeRows(firstRow);
49162             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49163         }
49164     },
49165
49166     updateRows : function(dataSource, firstRow, lastRow){
49167         var s = this.getScrollState();
49168         this.refresh();
49169         this.restoreScroll(s);
49170     },
49171
49172     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49173         if(!noRefresh){
49174            this.refresh();
49175         }
49176         this.updateHeaderSortState();
49177     },
49178
49179     getScrollState : function(){
49180         
49181         var sb = this.scroller.dom;
49182         return {left: sb.scrollLeft, top: sb.scrollTop};
49183     },
49184
49185     stripeRows : function(startRow){
49186         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49187             return;
49188         }
49189         startRow = startRow || 0;
49190         var rows = this.getBodyTable().rows;
49191         var lrows = this.getLockedTable().rows;
49192         var cls = ' x-grid-row-alt ';
49193         for(var i = startRow, len = rows.length; i < len; i++){
49194             var row = rows[i], lrow = lrows[i];
49195             var isAlt = ((i+1) % 2 == 0);
49196             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49197             if(isAlt == hasAlt){
49198                 continue;
49199             }
49200             if(isAlt){
49201                 row.className += " x-grid-row-alt";
49202             }else{
49203                 row.className = row.className.replace("x-grid-row-alt", "");
49204             }
49205             if(lrow){
49206                 lrow.className = row.className;
49207             }
49208         }
49209     },
49210
49211     restoreScroll : function(state){
49212         //Roo.log('GridView.restoreScroll');
49213         var sb = this.scroller.dom;
49214         sb.scrollLeft = state.left;
49215         sb.scrollTop = state.top;
49216         this.syncScroll();
49217     },
49218
49219     syncScroll : function(){
49220         //Roo.log('GridView.syncScroll');
49221         var sb = this.scroller.dom;
49222         var sh = this.mainHd.dom;
49223         var bs = this.mainBody.dom;
49224         var lv = this.lockedBody.dom;
49225         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49226         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49227     },
49228
49229     handleScroll : function(e){
49230         this.syncScroll();
49231         var sb = this.scroller.dom;
49232         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49233         e.stopEvent();
49234     },
49235
49236     handleWheel : function(e){
49237         var d = e.getWheelDelta();
49238         this.scroller.dom.scrollTop -= d*22;
49239         // set this here to prevent jumpy scrolling on large tables
49240         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49241         e.stopEvent();
49242     },
49243
49244     renderRows : function(startRow, endRow){
49245         // pull in all the crap needed to render rows
49246         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49247         var colCount = cm.getColumnCount();
49248
49249         if(ds.getCount() < 1){
49250             return ["", ""];
49251         }
49252
49253         // build a map for all the columns
49254         var cs = [];
49255         for(var i = 0; i < colCount; i++){
49256             var name = cm.getDataIndex(i);
49257             cs[i] = {
49258                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49259                 renderer : cm.getRenderer(i),
49260                 id : cm.getColumnId(i),
49261                 locked : cm.isLocked(i)
49262             };
49263         }
49264
49265         startRow = startRow || 0;
49266         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49267
49268         // records to render
49269         var rs = ds.getRange(startRow, endRow);
49270
49271         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49272     },
49273
49274     // As much as I hate to duplicate code, this was branched because FireFox really hates
49275     // [].join("") on strings. The performance difference was substantial enough to
49276     // branch this function
49277     doRender : Roo.isGecko ?
49278             function(cs, rs, ds, startRow, colCount, stripe){
49279                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49280                 // buffers
49281                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49282                 
49283                 var hasListener = this.grid.hasListener('rowclass');
49284                 var rowcfg = {};
49285                 for(var j = 0, len = rs.length; j < len; j++){
49286                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49287                     for(var i = 0; i < colCount; i++){
49288                         c = cs[i];
49289                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49290                         p.id = c.id;
49291                         p.css = p.attr = "";
49292                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49293                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49294                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49295                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49296                         }
49297                         var markup = ct.apply(p);
49298                         if(!c.locked){
49299                             cb+= markup;
49300                         }else{
49301                             lcb+= markup;
49302                         }
49303                     }
49304                     var alt = [];
49305                     if(stripe && ((rowIndex+1) % 2 == 0)){
49306                         alt.push("x-grid-row-alt")
49307                     }
49308                     if(r.dirty){
49309                         alt.push(  " x-grid-dirty-row");
49310                     }
49311                     rp.cells = lcb;
49312                     if(this.getRowClass){
49313                         alt.push(this.getRowClass(r, rowIndex));
49314                     }
49315                     if (hasListener) {
49316                         rowcfg = {
49317                              
49318                             record: r,
49319                             rowIndex : rowIndex,
49320                             rowClass : ''
49321                         }
49322                         this.grid.fireEvent('rowclass', this, rowcfg);
49323                         alt.push(rowcfg.rowClass);
49324                     }
49325                     rp.alt = alt.join(" ");
49326                     lbuf+= rt.apply(rp);
49327                     rp.cells = cb;
49328                     buf+=  rt.apply(rp);
49329                 }
49330                 return [lbuf, buf];
49331             } :
49332             function(cs, rs, ds, startRow, colCount, stripe){
49333                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49334                 // buffers
49335                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49336                 var hasListener = this.grid.hasListener('rowclass');
49337  
49338                 var rowcfg = {};
49339                 for(var j = 0, len = rs.length; j < len; j++){
49340                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49341                     for(var i = 0; i < colCount; i++){
49342                         c = cs[i];
49343                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49344                         p.id = c.id;
49345                         p.css = p.attr = "";
49346                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49347                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49348                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49349                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49350                         }
49351                         
49352                         var markup = ct.apply(p);
49353                         if(!c.locked){
49354                             cb[cb.length] = markup;
49355                         }else{
49356                             lcb[lcb.length] = markup;
49357                         }
49358                     }
49359                     var alt = [];
49360                     if(stripe && ((rowIndex+1) % 2 == 0)){
49361                         alt.push( "x-grid-row-alt");
49362                     }
49363                     if(r.dirty){
49364                         alt.push(" x-grid-dirty-row");
49365                     }
49366                     rp.cells = lcb;
49367                     if(this.getRowClass){
49368                         alt.push( this.getRowClass(r, rowIndex));
49369                     }
49370                     if (hasListener) {
49371                         rowcfg = {
49372                              
49373                             record: r,
49374                             rowIndex : rowIndex,
49375                             rowClass : ''
49376                         }
49377                         this.grid.fireEvent('rowclass', this, rowcfg);
49378                         alt.push(rowcfg.rowClass);
49379                     }
49380                     rp.alt = alt.join(" ");
49381                     rp.cells = lcb.join("");
49382                     lbuf[lbuf.length] = rt.apply(rp);
49383                     rp.cells = cb.join("");
49384                     buf[buf.length] =  rt.apply(rp);
49385                 }
49386                 return [lbuf.join(""), buf.join("")];
49387             },
49388
49389     renderBody : function(){
49390         var markup = this.renderRows();
49391         var bt = this.templates.body;
49392         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49393     },
49394
49395     /**
49396      * Refreshes the grid
49397      * @param {Boolean} headersToo
49398      */
49399     refresh : function(headersToo){
49400         this.fireEvent("beforerefresh", this);
49401         this.grid.stopEditing();
49402         var result = this.renderBody();
49403         this.lockedBody.update(result[0]);
49404         this.mainBody.update(result[1]);
49405         if(headersToo === true){
49406             this.updateHeaders();
49407             this.updateColumns();
49408             this.updateSplitters();
49409             this.updateHeaderSortState();
49410         }
49411         this.syncRowHeights();
49412         this.layout();
49413         this.fireEvent("refresh", this);
49414     },
49415
49416     handleColumnMove : function(cm, oldIndex, newIndex){
49417         this.indexMap = null;
49418         var s = this.getScrollState();
49419         this.refresh(true);
49420         this.restoreScroll(s);
49421         this.afterMove(newIndex);
49422     },
49423
49424     afterMove : function(colIndex){
49425         if(this.enableMoveAnim && Roo.enableFx){
49426             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49427         }
49428         // if multisort - fix sortOrder, and reload..
49429         if (this.grid.dataSource.multiSort) {
49430             // the we can call sort again..
49431             var dm = this.grid.dataSource;
49432             var cm = this.grid.colModel;
49433             var so = [];
49434             for(var i = 0; i < cm.config.length; i++ ) {
49435                 
49436                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49437                     continue; // dont' bother, it's not in sort list or being set.
49438                 }
49439                 
49440                 so.push(cm.config[i].dataIndex);
49441             };
49442             dm.sortOrder = so;
49443             dm.load(dm.lastOptions);
49444             
49445             
49446         }
49447         
49448     },
49449
49450     updateCell : function(dm, rowIndex, dataIndex){
49451         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49452         if(typeof colIndex == "undefined"){ // not present in grid
49453             return;
49454         }
49455         var cm = this.grid.colModel;
49456         var cell = this.getCell(rowIndex, colIndex);
49457         var cellText = this.getCellText(rowIndex, colIndex);
49458
49459         var p = {
49460             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49461             id : cm.getColumnId(colIndex),
49462             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49463         };
49464         var renderer = cm.getRenderer(colIndex);
49465         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49466         if(typeof val == "undefined" || val === "") val = "&#160;";
49467         cellText.innerHTML = val;
49468         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49469         this.syncRowHeights(rowIndex, rowIndex);
49470     },
49471
49472     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49473         var maxWidth = 0;
49474         if(this.grid.autoSizeHeaders){
49475             var h = this.getHeaderCellMeasure(colIndex);
49476             maxWidth = Math.max(maxWidth, h.scrollWidth);
49477         }
49478         var tb, index;
49479         if(this.cm.isLocked(colIndex)){
49480             tb = this.getLockedTable();
49481             index = colIndex;
49482         }else{
49483             tb = this.getBodyTable();
49484             index = colIndex - this.cm.getLockedCount();
49485         }
49486         if(tb && tb.rows){
49487             var rows = tb.rows;
49488             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49489             for(var i = 0; i < stopIndex; i++){
49490                 var cell = rows[i].childNodes[index].firstChild;
49491                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49492             }
49493         }
49494         return maxWidth + /*margin for error in IE*/ 5;
49495     },
49496     /**
49497      * Autofit a column to its content.
49498      * @param {Number} colIndex
49499      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49500      */
49501      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49502          if(this.cm.isHidden(colIndex)){
49503              return; // can't calc a hidden column
49504          }
49505         if(forceMinSize){
49506             var cid = this.cm.getColumnId(colIndex);
49507             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49508            if(this.grid.autoSizeHeaders){
49509                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49510            }
49511         }
49512         var newWidth = this.calcColumnWidth(colIndex);
49513         this.cm.setColumnWidth(colIndex,
49514             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49515         if(!suppressEvent){
49516             this.grid.fireEvent("columnresize", colIndex, newWidth);
49517         }
49518     },
49519
49520     /**
49521      * Autofits all columns to their content and then expands to fit any extra space in the grid
49522      */
49523      autoSizeColumns : function(){
49524         var cm = this.grid.colModel;
49525         var colCount = cm.getColumnCount();
49526         for(var i = 0; i < colCount; i++){
49527             this.autoSizeColumn(i, true, true);
49528         }
49529         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49530             this.fitColumns();
49531         }else{
49532             this.updateColumns();
49533             this.layout();
49534         }
49535     },
49536
49537     /**
49538      * Autofits all columns to the grid's width proportionate with their current size
49539      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49540      */
49541     fitColumns : function(reserveScrollSpace){
49542         var cm = this.grid.colModel;
49543         var colCount = cm.getColumnCount();
49544         var cols = [];
49545         var width = 0;
49546         var i, w;
49547         for (i = 0; i < colCount; i++){
49548             if(!cm.isHidden(i) && !cm.isFixed(i)){
49549                 w = cm.getColumnWidth(i);
49550                 cols.push(i);
49551                 cols.push(w);
49552                 width += w;
49553             }
49554         }
49555         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49556         if(reserveScrollSpace){
49557             avail -= 17;
49558         }
49559         var frac = (avail - cm.getTotalWidth())/width;
49560         while (cols.length){
49561             w = cols.pop();
49562             i = cols.pop();
49563             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49564         }
49565         this.updateColumns();
49566         this.layout();
49567     },
49568
49569     onRowSelect : function(rowIndex){
49570         var row = this.getRowComposite(rowIndex);
49571         row.addClass("x-grid-row-selected");
49572     },
49573
49574     onRowDeselect : function(rowIndex){
49575         var row = this.getRowComposite(rowIndex);
49576         row.removeClass("x-grid-row-selected");
49577     },
49578
49579     onCellSelect : function(row, col){
49580         var cell = this.getCell(row, col);
49581         if(cell){
49582             Roo.fly(cell).addClass("x-grid-cell-selected");
49583         }
49584     },
49585
49586     onCellDeselect : function(row, col){
49587         var cell = this.getCell(row, col);
49588         if(cell){
49589             Roo.fly(cell).removeClass("x-grid-cell-selected");
49590         }
49591     },
49592
49593     updateHeaderSortState : function(){
49594         
49595         // sort state can be single { field: xxx, direction : yyy}
49596         // or   { xxx=>ASC , yyy : DESC ..... }
49597         
49598         var mstate = {};
49599         if (!this.ds.multiSort) { 
49600             var state = this.ds.getSortState();
49601             if(!state){
49602                 return;
49603             }
49604             mstate[state.field] = state.direction;
49605             // FIXME... - this is not used here.. but might be elsewhere..
49606             this.sortState = state;
49607             
49608         } else {
49609             mstate = this.ds.sortToggle;
49610         }
49611         //remove existing sort classes..
49612         
49613         var sc = this.sortClasses;
49614         var hds = this.el.select(this.headerSelector).removeClass(sc);
49615         
49616         for(var f in mstate) {
49617         
49618             var sortColumn = this.cm.findColumnIndex(f);
49619             
49620             if(sortColumn != -1){
49621                 var sortDir = mstate[f];        
49622                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49623             }
49624         }
49625         
49626          
49627         
49628     },
49629
49630
49631     handleHeaderClick : function(g, index){
49632         if(this.headersDisabled){
49633             return;
49634         }
49635         var dm = g.dataSource, cm = g.colModel;
49636         if(!cm.isSortable(index)){
49637             return;
49638         }
49639         g.stopEditing();
49640         
49641         if (dm.multiSort) {
49642             // update the sortOrder
49643             var so = [];
49644             for(var i = 0; i < cm.config.length; i++ ) {
49645                 
49646                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49647                     continue; // dont' bother, it's not in sort list or being set.
49648                 }
49649                 
49650                 so.push(cm.config[i].dataIndex);
49651             };
49652             dm.sortOrder = so;
49653         }
49654         
49655         
49656         dm.sort(cm.getDataIndex(index));
49657     },
49658
49659
49660     destroy : function(){
49661         if(this.colMenu){
49662             this.colMenu.removeAll();
49663             Roo.menu.MenuMgr.unregister(this.colMenu);
49664             this.colMenu.getEl().remove();
49665             delete this.colMenu;
49666         }
49667         if(this.hmenu){
49668             this.hmenu.removeAll();
49669             Roo.menu.MenuMgr.unregister(this.hmenu);
49670             this.hmenu.getEl().remove();
49671             delete this.hmenu;
49672         }
49673         if(this.grid.enableColumnMove){
49674             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49675             if(dds){
49676                 for(var dd in dds){
49677                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49678                         var elid = dds[dd].dragElId;
49679                         dds[dd].unreg();
49680                         Roo.get(elid).remove();
49681                     } else if(dds[dd].config.isTarget){
49682                         dds[dd].proxyTop.remove();
49683                         dds[dd].proxyBottom.remove();
49684                         dds[dd].unreg();
49685                     }
49686                     if(Roo.dd.DDM.locationCache[dd]){
49687                         delete Roo.dd.DDM.locationCache[dd];
49688                     }
49689                 }
49690                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49691             }
49692         }
49693         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49694         this.bind(null, null);
49695         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49696     },
49697
49698     handleLockChange : function(){
49699         this.refresh(true);
49700     },
49701
49702     onDenyColumnLock : function(){
49703
49704     },
49705
49706     onDenyColumnHide : function(){
49707
49708     },
49709
49710     handleHdMenuClick : function(item){
49711         var index = this.hdCtxIndex;
49712         var cm = this.cm, ds = this.ds;
49713         switch(item.id){
49714             case "asc":
49715                 ds.sort(cm.getDataIndex(index), "ASC");
49716                 break;
49717             case "desc":
49718                 ds.sort(cm.getDataIndex(index), "DESC");
49719                 break;
49720             case "lock":
49721                 var lc = cm.getLockedCount();
49722                 if(cm.getColumnCount(true) <= lc+1){
49723                     this.onDenyColumnLock();
49724                     return;
49725                 }
49726                 if(lc != index){
49727                     cm.setLocked(index, true, true);
49728                     cm.moveColumn(index, lc);
49729                     this.grid.fireEvent("columnmove", index, lc);
49730                 }else{
49731                     cm.setLocked(index, true);
49732                 }
49733             break;
49734             case "unlock":
49735                 var lc = cm.getLockedCount();
49736                 if((lc-1) != index){
49737                     cm.setLocked(index, false, true);
49738                     cm.moveColumn(index, lc-1);
49739                     this.grid.fireEvent("columnmove", index, lc-1);
49740                 }else{
49741                     cm.setLocked(index, false);
49742                 }
49743             break;
49744             default:
49745                 index = cm.getIndexById(item.id.substr(4));
49746                 if(index != -1){
49747                     if(item.checked && cm.getColumnCount(true) <= 1){
49748                         this.onDenyColumnHide();
49749                         return false;
49750                     }
49751                     cm.setHidden(index, item.checked);
49752                 }
49753         }
49754         return true;
49755     },
49756
49757     beforeColMenuShow : function(){
49758         var cm = this.cm,  colCount = cm.getColumnCount();
49759         this.colMenu.removeAll();
49760         for(var i = 0; i < colCount; i++){
49761             this.colMenu.add(new Roo.menu.CheckItem({
49762                 id: "col-"+cm.getColumnId(i),
49763                 text: cm.getColumnHeader(i),
49764                 checked: !cm.isHidden(i),
49765                 hideOnClick:false
49766             }));
49767         }
49768     },
49769
49770     handleHdCtx : function(g, index, e){
49771         e.stopEvent();
49772         var hd = this.getHeaderCell(index);
49773         this.hdCtxIndex = index;
49774         var ms = this.hmenu.items, cm = this.cm;
49775         ms.get("asc").setDisabled(!cm.isSortable(index));
49776         ms.get("desc").setDisabled(!cm.isSortable(index));
49777         if(this.grid.enableColLock !== false){
49778             ms.get("lock").setDisabled(cm.isLocked(index));
49779             ms.get("unlock").setDisabled(!cm.isLocked(index));
49780         }
49781         this.hmenu.show(hd, "tl-bl");
49782     },
49783
49784     handleHdOver : function(e){
49785         var hd = this.findHeaderCell(e.getTarget());
49786         if(hd && !this.headersDisabled){
49787             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49788                this.fly(hd).addClass("x-grid-hd-over");
49789             }
49790         }
49791     },
49792
49793     handleHdOut : function(e){
49794         var hd = this.findHeaderCell(e.getTarget());
49795         if(hd){
49796             this.fly(hd).removeClass("x-grid-hd-over");
49797         }
49798     },
49799
49800     handleSplitDblClick : function(e, t){
49801         var i = this.getCellIndex(t);
49802         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49803             this.autoSizeColumn(i, true);
49804             this.layout();
49805         }
49806     },
49807
49808     render : function(){
49809
49810         var cm = this.cm;
49811         var colCount = cm.getColumnCount();
49812
49813         if(this.grid.monitorWindowResize === true){
49814             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49815         }
49816         var header = this.renderHeaders();
49817         var body = this.templates.body.apply({rows:""});
49818         var html = this.templates.master.apply({
49819             lockedBody: body,
49820             body: body,
49821             lockedHeader: header[0],
49822             header: header[1]
49823         });
49824
49825         //this.updateColumns();
49826
49827         this.grid.getGridEl().dom.innerHTML = html;
49828
49829         this.initElements();
49830         
49831         // a kludge to fix the random scolling effect in webkit
49832         this.el.on("scroll", function() {
49833             this.el.dom.scrollTop=0; // hopefully not recursive..
49834         },this);
49835
49836         this.scroller.on("scroll", this.handleScroll, this);
49837         this.lockedBody.on("mousewheel", this.handleWheel, this);
49838         this.mainBody.on("mousewheel", this.handleWheel, this);
49839
49840         this.mainHd.on("mouseover", this.handleHdOver, this);
49841         this.mainHd.on("mouseout", this.handleHdOut, this);
49842         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49843                 {delegate: "."+this.splitClass});
49844
49845         this.lockedHd.on("mouseover", this.handleHdOver, this);
49846         this.lockedHd.on("mouseout", this.handleHdOut, this);
49847         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49848                 {delegate: "."+this.splitClass});
49849
49850         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49851             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49852         }
49853
49854         this.updateSplitters();
49855
49856         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49857             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49858             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49859         }
49860
49861         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49862             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49863             this.hmenu.add(
49864                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49865                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49866             );
49867             if(this.grid.enableColLock !== false){
49868                 this.hmenu.add('-',
49869                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49870                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49871                 );
49872             }
49873             if(this.grid.enableColumnHide !== false){
49874
49875                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49876                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49877                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49878
49879                 this.hmenu.add('-',
49880                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49881                 );
49882             }
49883             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49884
49885             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49886         }
49887
49888         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49889             this.dd = new Roo.grid.GridDragZone(this.grid, {
49890                 ddGroup : this.grid.ddGroup || 'GridDD'
49891             });
49892         }
49893
49894         /*
49895         for(var i = 0; i < colCount; i++){
49896             if(cm.isHidden(i)){
49897                 this.hideColumn(i);
49898             }
49899             if(cm.config[i].align){
49900                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49901                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49902             }
49903         }*/
49904         
49905         this.updateHeaderSortState();
49906
49907         this.beforeInitialResize();
49908         this.layout(true);
49909
49910         // two part rendering gives faster view to the user
49911         this.renderPhase2.defer(1, this);
49912     },
49913
49914     renderPhase2 : function(){
49915         // render the rows now
49916         this.refresh();
49917         if(this.grid.autoSizeColumns){
49918             this.autoSizeColumns();
49919         }
49920     },
49921
49922     beforeInitialResize : function(){
49923
49924     },
49925
49926     onColumnSplitterMoved : function(i, w){
49927         this.userResized = true;
49928         var cm = this.grid.colModel;
49929         cm.setColumnWidth(i, w, true);
49930         var cid = cm.getColumnId(i);
49931         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49932         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49933         this.updateSplitters();
49934         this.layout();
49935         this.grid.fireEvent("columnresize", i, w);
49936     },
49937
49938     syncRowHeights : function(startIndex, endIndex){
49939         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49940             startIndex = startIndex || 0;
49941             var mrows = this.getBodyTable().rows;
49942             var lrows = this.getLockedTable().rows;
49943             var len = mrows.length-1;
49944             endIndex = Math.min(endIndex || len, len);
49945             for(var i = startIndex; i <= endIndex; i++){
49946                 var m = mrows[i], l = lrows[i];
49947                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49948                 m.style.height = l.style.height = h + "px";
49949             }
49950         }
49951     },
49952
49953     layout : function(initialRender, is2ndPass){
49954         var g = this.grid;
49955         var auto = g.autoHeight;
49956         var scrollOffset = 16;
49957         var c = g.getGridEl(), cm = this.cm,
49958                 expandCol = g.autoExpandColumn,
49959                 gv = this;
49960         //c.beginMeasure();
49961
49962         if(!c.dom.offsetWidth){ // display:none?
49963             if(initialRender){
49964                 this.lockedWrap.show();
49965                 this.mainWrap.show();
49966             }
49967             return;
49968         }
49969
49970         var hasLock = this.cm.isLocked(0);
49971
49972         var tbh = this.headerPanel.getHeight();
49973         var bbh = this.footerPanel.getHeight();
49974
49975         if(auto){
49976             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49977             var newHeight = ch + c.getBorderWidth("tb");
49978             if(g.maxHeight){
49979                 newHeight = Math.min(g.maxHeight, newHeight);
49980             }
49981             c.setHeight(newHeight);
49982         }
49983
49984         if(g.autoWidth){
49985             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49986         }
49987
49988         var s = this.scroller;
49989
49990         var csize = c.getSize(true);
49991
49992         this.el.setSize(csize.width, csize.height);
49993
49994         this.headerPanel.setWidth(csize.width);
49995         this.footerPanel.setWidth(csize.width);
49996
49997         var hdHeight = this.mainHd.getHeight();
49998         var vw = csize.width;
49999         var vh = csize.height - (tbh + bbh);
50000
50001         s.setSize(vw, vh);
50002
50003         var bt = this.getBodyTable();
50004         var ltWidth = hasLock ?
50005                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50006
50007         var scrollHeight = bt.offsetHeight;
50008         var scrollWidth = ltWidth + bt.offsetWidth;
50009         var vscroll = false, hscroll = false;
50010
50011         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50012
50013         var lw = this.lockedWrap, mw = this.mainWrap;
50014         var lb = this.lockedBody, mb = this.mainBody;
50015
50016         setTimeout(function(){
50017             var t = s.dom.offsetTop;
50018             var w = s.dom.clientWidth,
50019                 h = s.dom.clientHeight;
50020
50021             lw.setTop(t);
50022             lw.setSize(ltWidth, h);
50023
50024             mw.setLeftTop(ltWidth, t);
50025             mw.setSize(w-ltWidth, h);
50026
50027             lb.setHeight(h-hdHeight);
50028             mb.setHeight(h-hdHeight);
50029
50030             if(is2ndPass !== true && !gv.userResized && expandCol){
50031                 // high speed resize without full column calculation
50032                 
50033                 var ci = cm.getIndexById(expandCol);
50034                 if (ci < 0) {
50035                     ci = cm.findColumnIndex(expandCol);
50036                 }
50037                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50038                 var expandId = cm.getColumnId(ci);
50039                 var  tw = cm.getTotalWidth(false);
50040                 var currentWidth = cm.getColumnWidth(ci);
50041                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50042                 if(currentWidth != cw){
50043                     cm.setColumnWidth(ci, cw, true);
50044                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50045                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50046                     gv.updateSplitters();
50047                     gv.layout(false, true);
50048                 }
50049             }
50050
50051             if(initialRender){
50052                 lw.show();
50053                 mw.show();
50054             }
50055             //c.endMeasure();
50056         }, 10);
50057     },
50058
50059     onWindowResize : function(){
50060         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50061             return;
50062         }
50063         this.layout();
50064     },
50065
50066     appendFooter : function(parentEl){
50067         return null;
50068     },
50069
50070     sortAscText : "Sort Ascending",
50071     sortDescText : "Sort Descending",
50072     lockText : "Lock Column",
50073     unlockText : "Unlock Column",
50074     columnsText : "Columns"
50075 });
50076
50077
50078 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50079     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50080     this.proxy.el.addClass('x-grid3-col-dd');
50081 };
50082
50083 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50084     handleMouseDown : function(e){
50085
50086     },
50087
50088     callHandleMouseDown : function(e){
50089         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50090     }
50091 });
50092 /*
50093  * Based on:
50094  * Ext JS Library 1.1.1
50095  * Copyright(c) 2006-2007, Ext JS, LLC.
50096  *
50097  * Originally Released Under LGPL - original licence link has changed is not relivant.
50098  *
50099  * Fork - LGPL
50100  * <script type="text/javascript">
50101  */
50102  
50103 // private
50104 // This is a support class used internally by the Grid components
50105 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50106     this.grid = grid;
50107     this.view = grid.getView();
50108     this.proxy = this.view.resizeProxy;
50109     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50110         "gridSplitters" + this.grid.getGridEl().id, {
50111         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50112     });
50113     this.setHandleElId(Roo.id(hd));
50114     this.setOuterHandleElId(Roo.id(hd2));
50115     this.scroll = false;
50116 };
50117 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50118     fly: Roo.Element.fly,
50119
50120     b4StartDrag : function(x, y){
50121         this.view.headersDisabled = true;
50122         this.proxy.setHeight(this.view.mainWrap.getHeight());
50123         var w = this.cm.getColumnWidth(this.cellIndex);
50124         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50125         this.resetConstraints();
50126         this.setXConstraint(minw, 1000);
50127         this.setYConstraint(0, 0);
50128         this.minX = x - minw;
50129         this.maxX = x + 1000;
50130         this.startPos = x;
50131         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50132     },
50133
50134
50135     handleMouseDown : function(e){
50136         ev = Roo.EventObject.setEvent(e);
50137         var t = this.fly(ev.getTarget());
50138         if(t.hasClass("x-grid-split")){
50139             this.cellIndex = this.view.getCellIndex(t.dom);
50140             this.split = t.dom;
50141             this.cm = this.grid.colModel;
50142             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50143                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50144             }
50145         }
50146     },
50147
50148     endDrag : function(e){
50149         this.view.headersDisabled = false;
50150         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50151         var diff = endX - this.startPos;
50152         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50153     },
50154
50155     autoOffset : function(){
50156         this.setDelta(0,0);
50157     }
50158 });/*
50159  * Based on:
50160  * Ext JS Library 1.1.1
50161  * Copyright(c) 2006-2007, Ext JS, LLC.
50162  *
50163  * Originally Released Under LGPL - original licence link has changed is not relivant.
50164  *
50165  * Fork - LGPL
50166  * <script type="text/javascript">
50167  */
50168  
50169 // private
50170 // This is a support class used internally by the Grid components
50171 Roo.grid.GridDragZone = function(grid, config){
50172     this.view = grid.getView();
50173     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50174     if(this.view.lockedBody){
50175         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50176         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50177     }
50178     this.scroll = false;
50179     this.grid = grid;
50180     this.ddel = document.createElement('div');
50181     this.ddel.className = 'x-grid-dd-wrap';
50182 };
50183
50184 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50185     ddGroup : "GridDD",
50186
50187     getDragData : function(e){
50188         var t = Roo.lib.Event.getTarget(e);
50189         var rowIndex = this.view.findRowIndex(t);
50190         if(rowIndex !== false){
50191             var sm = this.grid.selModel;
50192             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50193               //  sm.mouseDown(e, t);
50194             //}
50195             if (e.hasModifier()){
50196                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50197             }
50198             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50199         }
50200         return false;
50201     },
50202
50203     onInitDrag : function(e){
50204         var data = this.dragData;
50205         this.ddel.innerHTML = this.grid.getDragDropText();
50206         this.proxy.update(this.ddel);
50207         // fire start drag?
50208     },
50209
50210     afterRepair : function(){
50211         this.dragging = false;
50212     },
50213
50214     getRepairXY : function(e, data){
50215         return false;
50216     },
50217
50218     onEndDrag : function(data, e){
50219         // fire end drag?
50220     },
50221
50222     onValidDrop : function(dd, e, id){
50223         // fire drag drop?
50224         this.hideProxy();
50225     },
50226
50227     beforeInvalidDrop : function(e, id){
50228
50229     }
50230 });/*
50231  * Based on:
50232  * Ext JS Library 1.1.1
50233  * Copyright(c) 2006-2007, Ext JS, LLC.
50234  *
50235  * Originally Released Under LGPL - original licence link has changed is not relivant.
50236  *
50237  * Fork - LGPL
50238  * <script type="text/javascript">
50239  */
50240  
50241
50242 /**
50243  * @class Roo.grid.ColumnModel
50244  * @extends Roo.util.Observable
50245  * This is the default implementation of a ColumnModel used by the Grid. It defines
50246  * the columns in the grid.
50247  * <br>Usage:<br>
50248  <pre><code>
50249  var colModel = new Roo.grid.ColumnModel([
50250         {header: "Ticker", width: 60, sortable: true, locked: true},
50251         {header: "Company Name", width: 150, sortable: true},
50252         {header: "Market Cap.", width: 100, sortable: true},
50253         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50254         {header: "Employees", width: 100, sortable: true, resizable: false}
50255  ]);
50256  </code></pre>
50257  * <p>
50258  
50259  * The config options listed for this class are options which may appear in each
50260  * individual column definition.
50261  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50262  * @constructor
50263  * @param {Object} config An Array of column config objects. See this class's
50264  * config objects for details.
50265 */
50266 Roo.grid.ColumnModel = function(config){
50267         /**
50268      * The config passed into the constructor
50269      */
50270     this.config = config;
50271     this.lookup = {};
50272
50273     // if no id, create one
50274     // if the column does not have a dataIndex mapping,
50275     // map it to the order it is in the config
50276     for(var i = 0, len = config.length; i < len; i++){
50277         var c = config[i];
50278         if(typeof c.dataIndex == "undefined"){
50279             c.dataIndex = i;
50280         }
50281         if(typeof c.renderer == "string"){
50282             c.renderer = Roo.util.Format[c.renderer];
50283         }
50284         if(typeof c.id == "undefined"){
50285             c.id = Roo.id();
50286         }
50287         if(c.editor && c.editor.xtype){
50288             c.editor  = Roo.factory(c.editor, Roo.grid);
50289         }
50290         if(c.editor && c.editor.isFormField){
50291             c.editor = new Roo.grid.GridEditor(c.editor);
50292         }
50293         this.lookup[c.id] = c;
50294     }
50295
50296     /**
50297      * The width of columns which have no width specified (defaults to 100)
50298      * @type Number
50299      */
50300     this.defaultWidth = 100;
50301
50302     /**
50303      * Default sortable of columns which have no sortable specified (defaults to false)
50304      * @type Boolean
50305      */
50306     this.defaultSortable = false;
50307
50308     this.addEvents({
50309         /**
50310              * @event widthchange
50311              * Fires when the width of a column changes.
50312              * @param {ColumnModel} this
50313              * @param {Number} columnIndex The column index
50314              * @param {Number} newWidth The new width
50315              */
50316             "widthchange": true,
50317         /**
50318              * @event headerchange
50319              * Fires when the text of a header changes.
50320              * @param {ColumnModel} this
50321              * @param {Number} columnIndex The column index
50322              * @param {Number} newText The new header text
50323              */
50324             "headerchange": true,
50325         /**
50326              * @event hiddenchange
50327              * Fires when a column is hidden or "unhidden".
50328              * @param {ColumnModel} this
50329              * @param {Number} columnIndex The column index
50330              * @param {Boolean} hidden true if hidden, false otherwise
50331              */
50332             "hiddenchange": true,
50333             /**
50334          * @event columnmoved
50335          * Fires when a column is moved.
50336          * @param {ColumnModel} this
50337          * @param {Number} oldIndex
50338          * @param {Number} newIndex
50339          */
50340         "columnmoved" : true,
50341         /**
50342          * @event columlockchange
50343          * Fires when a column's locked state is changed
50344          * @param {ColumnModel} this
50345          * @param {Number} colIndex
50346          * @param {Boolean} locked true if locked
50347          */
50348         "columnlockchange" : true
50349     });
50350     Roo.grid.ColumnModel.superclass.constructor.call(this);
50351 };
50352 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50353     /**
50354      * @cfg {String} header The header text to display in the Grid view.
50355      */
50356     /**
50357      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50358      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50359      * specified, the column's index is used as an index into the Record's data Array.
50360      */
50361     /**
50362      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50363      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50364      */
50365     /**
50366      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50367      * Defaults to the value of the {@link #defaultSortable} property.
50368      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50369      */
50370     /**
50371      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50372      */
50373     /**
50374      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50375      */
50376     /**
50377      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50378      */
50379     /**
50380      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50381      */
50382     /**
50383      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50384      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50385      * default renderer uses the raw data value.
50386      */
50387        /**
50388      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50389      */
50390     /**
50391      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50392      */
50393
50394     /**
50395      * Returns the id of the column at the specified index.
50396      * @param {Number} index The column index
50397      * @return {String} the id
50398      */
50399     getColumnId : function(index){
50400         return this.config[index].id;
50401     },
50402
50403     /**
50404      * Returns the column for a specified id.
50405      * @param {String} id The column id
50406      * @return {Object} the column
50407      */
50408     getColumnById : function(id){
50409         return this.lookup[id];
50410     },
50411
50412     
50413     /**
50414      * Returns the column for a specified dataIndex.
50415      * @param {String} dataIndex The column dataIndex
50416      * @return {Object|Boolean} the column or false if not found
50417      */
50418     getColumnByDataIndex: function(dataIndex){
50419         var index = this.findColumnIndex(dataIndex);
50420         return index > -1 ? this.config[index] : false;
50421     },
50422     
50423     /**
50424      * Returns the index for a specified column id.
50425      * @param {String} id The column id
50426      * @return {Number} the index, or -1 if not found
50427      */
50428     getIndexById : function(id){
50429         for(var i = 0, len = this.config.length; i < len; i++){
50430             if(this.config[i].id == id){
50431                 return i;
50432             }
50433         }
50434         return -1;
50435     },
50436     
50437     /**
50438      * Returns the index for a specified column dataIndex.
50439      * @param {String} dataIndex The column dataIndex
50440      * @return {Number} the index, or -1 if not found
50441      */
50442     
50443     findColumnIndex : function(dataIndex){
50444         for(var i = 0, len = this.config.length; i < len; i++){
50445             if(this.config[i].dataIndex == dataIndex){
50446                 return i;
50447             }
50448         }
50449         return -1;
50450     },
50451     
50452     
50453     moveColumn : function(oldIndex, newIndex){
50454         var c = this.config[oldIndex];
50455         this.config.splice(oldIndex, 1);
50456         this.config.splice(newIndex, 0, c);
50457         this.dataMap = null;
50458         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50459     },
50460
50461     isLocked : function(colIndex){
50462         return this.config[colIndex].locked === true;
50463     },
50464
50465     setLocked : function(colIndex, value, suppressEvent){
50466         if(this.isLocked(colIndex) == value){
50467             return;
50468         }
50469         this.config[colIndex].locked = value;
50470         if(!suppressEvent){
50471             this.fireEvent("columnlockchange", this, colIndex, value);
50472         }
50473     },
50474
50475     getTotalLockedWidth : function(){
50476         var totalWidth = 0;
50477         for(var i = 0; i < this.config.length; i++){
50478             if(this.isLocked(i) && !this.isHidden(i)){
50479                 this.totalWidth += this.getColumnWidth(i);
50480             }
50481         }
50482         return totalWidth;
50483     },
50484
50485     getLockedCount : function(){
50486         for(var i = 0, len = this.config.length; i < len; i++){
50487             if(!this.isLocked(i)){
50488                 return i;
50489             }
50490         }
50491     },
50492
50493     /**
50494      * Returns the number of columns.
50495      * @return {Number}
50496      */
50497     getColumnCount : function(visibleOnly){
50498         if(visibleOnly === true){
50499             var c = 0;
50500             for(var i = 0, len = this.config.length; i < len; i++){
50501                 if(!this.isHidden(i)){
50502                     c++;
50503                 }
50504             }
50505             return c;
50506         }
50507         return this.config.length;
50508     },
50509
50510     /**
50511      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50512      * @param {Function} fn
50513      * @param {Object} scope (optional)
50514      * @return {Array} result
50515      */
50516     getColumnsBy : function(fn, scope){
50517         var r = [];
50518         for(var i = 0, len = this.config.length; i < len; i++){
50519             var c = this.config[i];
50520             if(fn.call(scope||this, c, i) === true){
50521                 r[r.length] = c;
50522             }
50523         }
50524         return r;
50525     },
50526
50527     /**
50528      * Returns true if the specified column is sortable.
50529      * @param {Number} col The column index
50530      * @return {Boolean}
50531      */
50532     isSortable : function(col){
50533         if(typeof this.config[col].sortable == "undefined"){
50534             return this.defaultSortable;
50535         }
50536         return this.config[col].sortable;
50537     },
50538
50539     /**
50540      * Returns the rendering (formatting) function defined for the column.
50541      * @param {Number} col The column index.
50542      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50543      */
50544     getRenderer : function(col){
50545         if(!this.config[col].renderer){
50546             return Roo.grid.ColumnModel.defaultRenderer;
50547         }
50548         return this.config[col].renderer;
50549     },
50550
50551     /**
50552      * Sets the rendering (formatting) function for a column.
50553      * @param {Number} col The column index
50554      * @param {Function} fn The function to use to process the cell's raw data
50555      * to return HTML markup for the grid view. The render function is called with
50556      * the following parameters:<ul>
50557      * <li>Data value.</li>
50558      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50559      * <li>css A CSS style string to apply to the table cell.</li>
50560      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50561      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50562      * <li>Row index</li>
50563      * <li>Column index</li>
50564      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50565      */
50566     setRenderer : function(col, fn){
50567         this.config[col].renderer = fn;
50568     },
50569
50570     /**
50571      * Returns the width for the specified column.
50572      * @param {Number} col The column index
50573      * @return {Number}
50574      */
50575     getColumnWidth : function(col){
50576         return this.config[col].width * 1 || this.defaultWidth;
50577     },
50578
50579     /**
50580      * Sets the width for a column.
50581      * @param {Number} col The column index
50582      * @param {Number} width The new width
50583      */
50584     setColumnWidth : function(col, width, suppressEvent){
50585         this.config[col].width = width;
50586         this.totalWidth = null;
50587         if(!suppressEvent){
50588              this.fireEvent("widthchange", this, col, width);
50589         }
50590     },
50591
50592     /**
50593      * Returns the total width of all columns.
50594      * @param {Boolean} includeHidden True to include hidden column widths
50595      * @return {Number}
50596      */
50597     getTotalWidth : function(includeHidden){
50598         if(!this.totalWidth){
50599             this.totalWidth = 0;
50600             for(var i = 0, len = this.config.length; i < len; i++){
50601                 if(includeHidden || !this.isHidden(i)){
50602                     this.totalWidth += this.getColumnWidth(i);
50603                 }
50604             }
50605         }
50606         return this.totalWidth;
50607     },
50608
50609     /**
50610      * Returns the header for the specified column.
50611      * @param {Number} col The column index
50612      * @return {String}
50613      */
50614     getColumnHeader : function(col){
50615         return this.config[col].header;
50616     },
50617
50618     /**
50619      * Sets the header for a column.
50620      * @param {Number} col The column index
50621      * @param {String} header The new header
50622      */
50623     setColumnHeader : function(col, header){
50624         this.config[col].header = header;
50625         this.fireEvent("headerchange", this, col, header);
50626     },
50627
50628     /**
50629      * Returns the tooltip for the specified column.
50630      * @param {Number} col The column index
50631      * @return {String}
50632      */
50633     getColumnTooltip : function(col){
50634             return this.config[col].tooltip;
50635     },
50636     /**
50637      * Sets the tooltip for a column.
50638      * @param {Number} col The column index
50639      * @param {String} tooltip The new tooltip
50640      */
50641     setColumnTooltip : function(col, tooltip){
50642             this.config[col].tooltip = tooltip;
50643     },
50644
50645     /**
50646      * Returns the dataIndex for the specified column.
50647      * @param {Number} col The column index
50648      * @return {Number}
50649      */
50650     getDataIndex : function(col){
50651         return this.config[col].dataIndex;
50652     },
50653
50654     /**
50655      * Sets the dataIndex for a column.
50656      * @param {Number} col The column index
50657      * @param {Number} dataIndex The new dataIndex
50658      */
50659     setDataIndex : function(col, dataIndex){
50660         this.config[col].dataIndex = dataIndex;
50661     },
50662
50663     
50664     
50665     /**
50666      * Returns true if the cell is editable.
50667      * @param {Number} colIndex The column index
50668      * @param {Number} rowIndex The row index
50669      * @return {Boolean}
50670      */
50671     isCellEditable : function(colIndex, rowIndex){
50672         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50673     },
50674
50675     /**
50676      * Returns the editor defined for the cell/column.
50677      * return false or null to disable editing.
50678      * @param {Number} colIndex The column index
50679      * @param {Number} rowIndex The row index
50680      * @return {Object}
50681      */
50682     getCellEditor : function(colIndex, rowIndex){
50683         return this.config[colIndex].editor;
50684     },
50685
50686     /**
50687      * Sets if a column is editable.
50688      * @param {Number} col The column index
50689      * @param {Boolean} editable True if the column is editable
50690      */
50691     setEditable : function(col, editable){
50692         this.config[col].editable = editable;
50693     },
50694
50695
50696     /**
50697      * Returns true if the column is hidden.
50698      * @param {Number} colIndex The column index
50699      * @return {Boolean}
50700      */
50701     isHidden : function(colIndex){
50702         return this.config[colIndex].hidden;
50703     },
50704
50705
50706     /**
50707      * Returns true if the column width cannot be changed
50708      */
50709     isFixed : function(colIndex){
50710         return this.config[colIndex].fixed;
50711     },
50712
50713     /**
50714      * Returns true if the column can be resized
50715      * @return {Boolean}
50716      */
50717     isResizable : function(colIndex){
50718         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50719     },
50720     /**
50721      * Sets if a column is hidden.
50722      * @param {Number} colIndex The column index
50723      * @param {Boolean} hidden True if the column is hidden
50724      */
50725     setHidden : function(colIndex, hidden){
50726         this.config[colIndex].hidden = hidden;
50727         this.totalWidth = null;
50728         this.fireEvent("hiddenchange", this, colIndex, hidden);
50729     },
50730
50731     /**
50732      * Sets the editor for a column.
50733      * @param {Number} col The column index
50734      * @param {Object} editor The editor object
50735      */
50736     setEditor : function(col, editor){
50737         this.config[col].editor = editor;
50738     }
50739 });
50740
50741 Roo.grid.ColumnModel.defaultRenderer = function(value){
50742         if(typeof value == "string" && value.length < 1){
50743             return "&#160;";
50744         }
50745         return value;
50746 };
50747
50748 // Alias for backwards compatibility
50749 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50750 /*
50751  * Based on:
50752  * Ext JS Library 1.1.1
50753  * Copyright(c) 2006-2007, Ext JS, LLC.
50754  *
50755  * Originally Released Under LGPL - original licence link has changed is not relivant.
50756  *
50757  * Fork - LGPL
50758  * <script type="text/javascript">
50759  */
50760
50761 /**
50762  * @class Roo.grid.AbstractSelectionModel
50763  * @extends Roo.util.Observable
50764  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50765  * implemented by descendant classes.  This class should not be directly instantiated.
50766  * @constructor
50767  */
50768 Roo.grid.AbstractSelectionModel = function(){
50769     this.locked = false;
50770     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50771 };
50772
50773 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50774     /** @ignore Called by the grid automatically. Do not call directly. */
50775     init : function(grid){
50776         this.grid = grid;
50777         this.initEvents();
50778     },
50779
50780     /**
50781      * Locks the selections.
50782      */
50783     lock : function(){
50784         this.locked = true;
50785     },
50786
50787     /**
50788      * Unlocks the selections.
50789      */
50790     unlock : function(){
50791         this.locked = false;
50792     },
50793
50794     /**
50795      * Returns true if the selections are locked.
50796      * @return {Boolean}
50797      */
50798     isLocked : function(){
50799         return this.locked;
50800     }
50801 });/*
50802  * Based on:
50803  * Ext JS Library 1.1.1
50804  * Copyright(c) 2006-2007, Ext JS, LLC.
50805  *
50806  * Originally Released Under LGPL - original licence link has changed is not relivant.
50807  *
50808  * Fork - LGPL
50809  * <script type="text/javascript">
50810  */
50811 /**
50812  * @extends Roo.grid.AbstractSelectionModel
50813  * @class Roo.grid.RowSelectionModel
50814  * The default SelectionModel used by {@link Roo.grid.Grid}.
50815  * It supports multiple selections and keyboard selection/navigation. 
50816  * @constructor
50817  * @param {Object} config
50818  */
50819 Roo.grid.RowSelectionModel = function(config){
50820     Roo.apply(this, config);
50821     this.selections = new Roo.util.MixedCollection(false, function(o){
50822         return o.id;
50823     });
50824
50825     this.last = false;
50826     this.lastActive = false;
50827
50828     this.addEvents({
50829         /**
50830              * @event selectionchange
50831              * Fires when the selection changes
50832              * @param {SelectionModel} this
50833              */
50834             "selectionchange" : true,
50835         /**
50836              * @event afterselectionchange
50837              * Fires after the selection changes (eg. by key press or clicking)
50838              * @param {SelectionModel} this
50839              */
50840             "afterselectionchange" : true,
50841         /**
50842              * @event beforerowselect
50843              * Fires when a row is selected being selected, return false to cancel.
50844              * @param {SelectionModel} this
50845              * @param {Number} rowIndex The selected index
50846              * @param {Boolean} keepExisting False if other selections will be cleared
50847              */
50848             "beforerowselect" : true,
50849         /**
50850              * @event rowselect
50851              * Fires when a row is selected.
50852              * @param {SelectionModel} this
50853              * @param {Number} rowIndex The selected index
50854              * @param {Roo.data.Record} r The record
50855              */
50856             "rowselect" : true,
50857         /**
50858              * @event rowdeselect
50859              * Fires when a row is deselected.
50860              * @param {SelectionModel} this
50861              * @param {Number} rowIndex The selected index
50862              */
50863         "rowdeselect" : true
50864     });
50865     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50866     this.locked = false;
50867 };
50868
50869 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50870     /**
50871      * @cfg {Boolean} singleSelect
50872      * True to allow selection of only one row at a time (defaults to false)
50873      */
50874     singleSelect : false,
50875
50876     // private
50877     initEvents : function(){
50878
50879         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50880             this.grid.on("mousedown", this.handleMouseDown, this);
50881         }else{ // allow click to work like normal
50882             this.grid.on("rowclick", this.handleDragableRowClick, this);
50883         }
50884
50885         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50886             "up" : function(e){
50887                 if(!e.shiftKey){
50888                     this.selectPrevious(e.shiftKey);
50889                 }else if(this.last !== false && this.lastActive !== false){
50890                     var last = this.last;
50891                     this.selectRange(this.last,  this.lastActive-1);
50892                     this.grid.getView().focusRow(this.lastActive);
50893                     if(last !== false){
50894                         this.last = last;
50895                     }
50896                 }else{
50897                     this.selectFirstRow();
50898                 }
50899                 this.fireEvent("afterselectionchange", this);
50900             },
50901             "down" : function(e){
50902                 if(!e.shiftKey){
50903                     this.selectNext(e.shiftKey);
50904                 }else if(this.last !== false && this.lastActive !== false){
50905                     var last = this.last;
50906                     this.selectRange(this.last,  this.lastActive+1);
50907                     this.grid.getView().focusRow(this.lastActive);
50908                     if(last !== false){
50909                         this.last = last;
50910                     }
50911                 }else{
50912                     this.selectFirstRow();
50913                 }
50914                 this.fireEvent("afterselectionchange", this);
50915             },
50916             scope: this
50917         });
50918
50919         var view = this.grid.view;
50920         view.on("refresh", this.onRefresh, this);
50921         view.on("rowupdated", this.onRowUpdated, this);
50922         view.on("rowremoved", this.onRemove, this);
50923     },
50924
50925     // private
50926     onRefresh : function(){
50927         var ds = this.grid.dataSource, i, v = this.grid.view;
50928         var s = this.selections;
50929         s.each(function(r){
50930             if((i = ds.indexOfId(r.id)) != -1){
50931                 v.onRowSelect(i);
50932             }else{
50933                 s.remove(r);
50934             }
50935         });
50936     },
50937
50938     // private
50939     onRemove : function(v, index, r){
50940         this.selections.remove(r);
50941     },
50942
50943     // private
50944     onRowUpdated : function(v, index, r){
50945         if(this.isSelected(r)){
50946             v.onRowSelect(index);
50947         }
50948     },
50949
50950     /**
50951      * Select records.
50952      * @param {Array} records The records to select
50953      * @param {Boolean} keepExisting (optional) True to keep existing selections
50954      */
50955     selectRecords : function(records, keepExisting){
50956         if(!keepExisting){
50957             this.clearSelections();
50958         }
50959         var ds = this.grid.dataSource;
50960         for(var i = 0, len = records.length; i < len; i++){
50961             this.selectRow(ds.indexOf(records[i]), true);
50962         }
50963     },
50964
50965     /**
50966      * Gets the number of selected rows.
50967      * @return {Number}
50968      */
50969     getCount : function(){
50970         return this.selections.length;
50971     },
50972
50973     /**
50974      * Selects the first row in the grid.
50975      */
50976     selectFirstRow : function(){
50977         this.selectRow(0);
50978     },
50979
50980     /**
50981      * Select the last row.
50982      * @param {Boolean} keepExisting (optional) True to keep existing selections
50983      */
50984     selectLastRow : function(keepExisting){
50985         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50986     },
50987
50988     /**
50989      * Selects the row immediately following the last selected row.
50990      * @param {Boolean} keepExisting (optional) True to keep existing selections
50991      */
50992     selectNext : function(keepExisting){
50993         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50994             this.selectRow(this.last+1, keepExisting);
50995             this.grid.getView().focusRow(this.last);
50996         }
50997     },
50998
50999     /**
51000      * Selects the row that precedes the last selected row.
51001      * @param {Boolean} keepExisting (optional) True to keep existing selections
51002      */
51003     selectPrevious : function(keepExisting){
51004         if(this.last){
51005             this.selectRow(this.last-1, keepExisting);
51006             this.grid.getView().focusRow(this.last);
51007         }
51008     },
51009
51010     /**
51011      * Returns the selected records
51012      * @return {Array} Array of selected records
51013      */
51014     getSelections : function(){
51015         return [].concat(this.selections.items);
51016     },
51017
51018     /**
51019      * Returns the first selected record.
51020      * @return {Record}
51021      */
51022     getSelected : function(){
51023         return this.selections.itemAt(0);
51024     },
51025
51026
51027     /**
51028      * Clears all selections.
51029      */
51030     clearSelections : function(fast){
51031         if(this.locked) return;
51032         if(fast !== true){
51033             var ds = this.grid.dataSource;
51034             var s = this.selections;
51035             s.each(function(r){
51036                 this.deselectRow(ds.indexOfId(r.id));
51037             }, this);
51038             s.clear();
51039         }else{
51040             this.selections.clear();
51041         }
51042         this.last = false;
51043     },
51044
51045
51046     /**
51047      * Selects all rows.
51048      */
51049     selectAll : function(){
51050         if(this.locked) return;
51051         this.selections.clear();
51052         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51053             this.selectRow(i, true);
51054         }
51055     },
51056
51057     /**
51058      * Returns True if there is a selection.
51059      * @return {Boolean}
51060      */
51061     hasSelection : function(){
51062         return this.selections.length > 0;
51063     },
51064
51065     /**
51066      * Returns True if the specified row is selected.
51067      * @param {Number/Record} record The record or index of the record to check
51068      * @return {Boolean}
51069      */
51070     isSelected : function(index){
51071         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51072         return (r && this.selections.key(r.id) ? true : false);
51073     },
51074
51075     /**
51076      * Returns True if the specified record id is selected.
51077      * @param {String} id The id of record to check
51078      * @return {Boolean}
51079      */
51080     isIdSelected : function(id){
51081         return (this.selections.key(id) ? true : false);
51082     },
51083
51084     // private
51085     handleMouseDown : function(e, t){
51086         var view = this.grid.getView(), rowIndex;
51087         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51088             return;
51089         };
51090         if(e.shiftKey && this.last !== false){
51091             var last = this.last;
51092             this.selectRange(last, rowIndex, e.ctrlKey);
51093             this.last = last; // reset the last
51094             view.focusRow(rowIndex);
51095         }else{
51096             var isSelected = this.isSelected(rowIndex);
51097             if(e.button !== 0 && isSelected){
51098                 view.focusRow(rowIndex);
51099             }else if(e.ctrlKey && isSelected){
51100                 this.deselectRow(rowIndex);
51101             }else if(!isSelected){
51102                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51103                 view.focusRow(rowIndex);
51104             }
51105         }
51106         this.fireEvent("afterselectionchange", this);
51107     },
51108     // private
51109     handleDragableRowClick :  function(grid, rowIndex, e) 
51110     {
51111         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51112             this.selectRow(rowIndex, false);
51113             grid.view.focusRow(rowIndex);
51114              this.fireEvent("afterselectionchange", this);
51115         }
51116     },
51117     
51118     /**
51119      * Selects multiple rows.
51120      * @param {Array} rows Array of the indexes of the row to select
51121      * @param {Boolean} keepExisting (optional) True to keep existing selections
51122      */
51123     selectRows : function(rows, keepExisting){
51124         if(!keepExisting){
51125             this.clearSelections();
51126         }
51127         for(var i = 0, len = rows.length; i < len; i++){
51128             this.selectRow(rows[i], true);
51129         }
51130     },
51131
51132     /**
51133      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51134      * @param {Number} startRow The index of the first row in the range
51135      * @param {Number} endRow The index of the last row in the range
51136      * @param {Boolean} keepExisting (optional) True to retain existing selections
51137      */
51138     selectRange : function(startRow, endRow, keepExisting){
51139         if(this.locked) return;
51140         if(!keepExisting){
51141             this.clearSelections();
51142         }
51143         if(startRow <= endRow){
51144             for(var i = startRow; i <= endRow; i++){
51145                 this.selectRow(i, true);
51146             }
51147         }else{
51148             for(var i = startRow; i >= endRow; i--){
51149                 this.selectRow(i, true);
51150             }
51151         }
51152     },
51153
51154     /**
51155      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51156      * @param {Number} startRow The index of the first row in the range
51157      * @param {Number} endRow The index of the last row in the range
51158      */
51159     deselectRange : function(startRow, endRow, preventViewNotify){
51160         if(this.locked) return;
51161         for(var i = startRow; i <= endRow; i++){
51162             this.deselectRow(i, preventViewNotify);
51163         }
51164     },
51165
51166     /**
51167      * Selects a row.
51168      * @param {Number} row The index of the row to select
51169      * @param {Boolean} keepExisting (optional) True to keep existing selections
51170      */
51171     selectRow : function(index, keepExisting, preventViewNotify){
51172         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51173         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51174             if(!keepExisting || this.singleSelect){
51175                 this.clearSelections();
51176             }
51177             var r = this.grid.dataSource.getAt(index);
51178             this.selections.add(r);
51179             this.last = this.lastActive = index;
51180             if(!preventViewNotify){
51181                 this.grid.getView().onRowSelect(index);
51182             }
51183             this.fireEvent("rowselect", this, index, r);
51184             this.fireEvent("selectionchange", this);
51185         }
51186     },
51187
51188     /**
51189      * Deselects a row.
51190      * @param {Number} row The index of the row to deselect
51191      */
51192     deselectRow : function(index, preventViewNotify){
51193         if(this.locked) return;
51194         if(this.last == index){
51195             this.last = false;
51196         }
51197         if(this.lastActive == index){
51198             this.lastActive = false;
51199         }
51200         var r = this.grid.dataSource.getAt(index);
51201         this.selections.remove(r);
51202         if(!preventViewNotify){
51203             this.grid.getView().onRowDeselect(index);
51204         }
51205         this.fireEvent("rowdeselect", this, index);
51206         this.fireEvent("selectionchange", this);
51207     },
51208
51209     // private
51210     restoreLast : function(){
51211         if(this._last){
51212             this.last = this._last;
51213         }
51214     },
51215
51216     // private
51217     acceptsNav : function(row, col, cm){
51218         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51219     },
51220
51221     // private
51222     onEditorKey : function(field, e){
51223         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51224         if(k == e.TAB){
51225             e.stopEvent();
51226             ed.completeEdit();
51227             if(e.shiftKey){
51228                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51229             }else{
51230                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51231             }
51232         }else if(k == e.ENTER && !e.ctrlKey){
51233             e.stopEvent();
51234             ed.completeEdit();
51235             if(e.shiftKey){
51236                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51237             }else{
51238                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51239             }
51240         }else if(k == e.ESC){
51241             ed.cancelEdit();
51242         }
51243         if(newCell){
51244             g.startEditing(newCell[0], newCell[1]);
51245         }
51246     }
51247 });/*
51248  * Based on:
51249  * Ext JS Library 1.1.1
51250  * Copyright(c) 2006-2007, Ext JS, LLC.
51251  *
51252  * Originally Released Under LGPL - original licence link has changed is not relivant.
51253  *
51254  * Fork - LGPL
51255  * <script type="text/javascript">
51256  */
51257 /**
51258  * @class Roo.grid.CellSelectionModel
51259  * @extends Roo.grid.AbstractSelectionModel
51260  * This class provides the basic implementation for cell selection in a grid.
51261  * @constructor
51262  * @param {Object} config The object containing the configuration of this model.
51263  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51264  */
51265 Roo.grid.CellSelectionModel = function(config){
51266     Roo.apply(this, config);
51267
51268     this.selection = null;
51269
51270     this.addEvents({
51271         /**
51272              * @event beforerowselect
51273              * Fires before a cell is selected.
51274              * @param {SelectionModel} this
51275              * @param {Number} rowIndex The selected row index
51276              * @param {Number} colIndex The selected cell index
51277              */
51278             "beforecellselect" : true,
51279         /**
51280              * @event cellselect
51281              * Fires when a cell is selected.
51282              * @param {SelectionModel} this
51283              * @param {Number} rowIndex The selected row index
51284              * @param {Number} colIndex The selected cell index
51285              */
51286             "cellselect" : true,
51287         /**
51288              * @event selectionchange
51289              * Fires when the active selection changes.
51290              * @param {SelectionModel} this
51291              * @param {Object} selection null for no selection or an object (o) with two properties
51292                 <ul>
51293                 <li>o.record: the record object for the row the selection is in</li>
51294                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51295                 </ul>
51296              */
51297             "selectionchange" : true,
51298         /**
51299              * @event tabend
51300              * Fires when the tab (or enter) was pressed on the last editable cell
51301              * You can use this to trigger add new row.
51302              * @param {SelectionModel} this
51303              */
51304             "tabend" : true
51305     });
51306     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51307 };
51308
51309 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51310     
51311     enter_is_tab: false,
51312
51313     /** @ignore */
51314     initEvents : function(){
51315         this.grid.on("mousedown", this.handleMouseDown, this);
51316         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51317         var view = this.grid.view;
51318         view.on("refresh", this.onViewChange, this);
51319         view.on("rowupdated", this.onRowUpdated, this);
51320         view.on("beforerowremoved", this.clearSelections, this);
51321         view.on("beforerowsinserted", this.clearSelections, this);
51322         if(this.grid.isEditor){
51323             this.grid.on("beforeedit", this.beforeEdit,  this);
51324         }
51325     },
51326
51327         //private
51328     beforeEdit : function(e){
51329         this.select(e.row, e.column, false, true, e.record);
51330     },
51331
51332         //private
51333     onRowUpdated : function(v, index, r){
51334         if(this.selection && this.selection.record == r){
51335             v.onCellSelect(index, this.selection.cell[1]);
51336         }
51337     },
51338
51339         //private
51340     onViewChange : function(){
51341         this.clearSelections(true);
51342     },
51343
51344         /**
51345          * Returns the currently selected cell,.
51346          * @return {Array} The selected cell (row, column) or null if none selected.
51347          */
51348     getSelectedCell : function(){
51349         return this.selection ? this.selection.cell : null;
51350     },
51351
51352     /**
51353      * Clears all selections.
51354      * @param {Boolean} true to prevent the gridview from being notified about the change.
51355      */
51356     clearSelections : function(preventNotify){
51357         var s = this.selection;
51358         if(s){
51359             if(preventNotify !== true){
51360                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51361             }
51362             this.selection = null;
51363             this.fireEvent("selectionchange", this, null);
51364         }
51365     },
51366
51367     /**
51368      * Returns true if there is a selection.
51369      * @return {Boolean}
51370      */
51371     hasSelection : function(){
51372         return this.selection ? true : false;
51373     },
51374
51375     /** @ignore */
51376     handleMouseDown : function(e, t){
51377         var v = this.grid.getView();
51378         if(this.isLocked()){
51379             return;
51380         };
51381         var row = v.findRowIndex(t);
51382         var cell = v.findCellIndex(t);
51383         if(row !== false && cell !== false){
51384             this.select(row, cell);
51385         }
51386     },
51387
51388     /**
51389      * Selects a cell.
51390      * @param {Number} rowIndex
51391      * @param {Number} collIndex
51392      */
51393     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51394         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51395             this.clearSelections();
51396             r = r || this.grid.dataSource.getAt(rowIndex);
51397             this.selection = {
51398                 record : r,
51399                 cell : [rowIndex, colIndex]
51400             };
51401             if(!preventViewNotify){
51402                 var v = this.grid.getView();
51403                 v.onCellSelect(rowIndex, colIndex);
51404                 if(preventFocus !== true){
51405                     v.focusCell(rowIndex, colIndex);
51406                 }
51407             }
51408             this.fireEvent("cellselect", this, rowIndex, colIndex);
51409             this.fireEvent("selectionchange", this, this.selection);
51410         }
51411     },
51412
51413         //private
51414     isSelectable : function(rowIndex, colIndex, cm){
51415         return !cm.isHidden(colIndex);
51416     },
51417
51418     /** @ignore */
51419     handleKeyDown : function(e){
51420         //Roo.log('Cell Sel Model handleKeyDown');
51421         if(!e.isNavKeyPress()){
51422             return;
51423         }
51424         var g = this.grid, s = this.selection;
51425         if(!s){
51426             e.stopEvent();
51427             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51428             if(cell){
51429                 this.select(cell[0], cell[1]);
51430             }
51431             return;
51432         }
51433         var sm = this;
51434         var walk = function(row, col, step){
51435             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51436         };
51437         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51438         var newCell;
51439
51440       
51441
51442         switch(k){
51443             case e.TAB:
51444                 // handled by onEditorKey
51445                 if (g.isEditor && g.editing) {
51446                     return;
51447                 }
51448                 if(e.shiftKey) {
51449                     newCell = walk(r, c-1, -1);
51450                 } else {
51451                     newCell = walk(r, c+1, 1);
51452                 }
51453                 break;
51454             
51455             case e.DOWN:
51456                newCell = walk(r+1, c, 1);
51457                 break;
51458             
51459             case e.UP:
51460                 newCell = walk(r-1, c, -1);
51461                 break;
51462             
51463             case e.RIGHT:
51464                 newCell = walk(r, c+1, 1);
51465                 break;
51466             
51467             case e.LEFT:
51468                 newCell = walk(r, c-1, -1);
51469                 break;
51470             
51471             case e.ENTER:
51472                 
51473                 if(g.isEditor && !g.editing){
51474                    g.startEditing(r, c);
51475                    e.stopEvent();
51476                    return;
51477                 }
51478                 
51479                 
51480              break;
51481         };
51482         if(newCell){
51483             this.select(newCell[0], newCell[1]);
51484             e.stopEvent();
51485             
51486         }
51487     },
51488
51489     acceptsNav : function(row, col, cm){
51490         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51491     },
51492     /**
51493      * Selects a cell.
51494      * @param {Number} field (not used) - as it's normally used as a listener
51495      * @param {Number} e - event - fake it by using
51496      *
51497      * var e = Roo.EventObjectImpl.prototype;
51498      * e.keyCode = e.TAB
51499      *
51500      * 
51501      */
51502     onEditorKey : function(field, e){
51503         
51504         var k = e.getKey(),
51505             newCell,
51506             g = this.grid,
51507             ed = g.activeEditor,
51508             forward = false;
51509         ///Roo.log('onEditorKey' + k);
51510         
51511         
51512         if (this.enter_is_tab && k == e.ENTER) {
51513             k = e.TAB;
51514         }
51515         
51516         if(k == e.TAB){
51517             if(e.shiftKey){
51518                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51519             }else{
51520                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51521                 forward = true;
51522             }
51523             
51524             e.stopEvent();
51525             
51526         }else if(k == e.ENTER &&  !e.ctrlKey){
51527             ed.completeEdit();
51528             e.stopEvent();
51529             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51530         }else if(k == e.ESC){
51531             ed.cancelEdit();
51532         }
51533         
51534         
51535         if(newCell){
51536             //Roo.log('next cell after edit');
51537             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51538         } else if (forward) {
51539             // tabbed past last
51540             this.fireEvent.defer(100, this, ['tabend',this]);
51541         }
51542     }
51543 });/*
51544  * Based on:
51545  * Ext JS Library 1.1.1
51546  * Copyright(c) 2006-2007, Ext JS, LLC.
51547  *
51548  * Originally Released Under LGPL - original licence link has changed is not relivant.
51549  *
51550  * Fork - LGPL
51551  * <script type="text/javascript">
51552  */
51553  
51554 /**
51555  * @class Roo.grid.EditorGrid
51556  * @extends Roo.grid.Grid
51557  * Class for creating and editable grid.
51558  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51559  * The container MUST have some type of size defined for the grid to fill. The container will be 
51560  * automatically set to position relative if it isn't already.
51561  * @param {Object} dataSource The data model to bind to
51562  * @param {Object} colModel The column model with info about this grid's columns
51563  */
51564 Roo.grid.EditorGrid = function(container, config){
51565     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51566     this.getGridEl().addClass("xedit-grid");
51567
51568     if(!this.selModel){
51569         this.selModel = new Roo.grid.CellSelectionModel();
51570     }
51571
51572     this.activeEditor = null;
51573
51574         this.addEvents({
51575             /**
51576              * @event beforeedit
51577              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51578              * <ul style="padding:5px;padding-left:16px;">
51579              * <li>grid - This grid</li>
51580              * <li>record - The record being edited</li>
51581              * <li>field - The field name being edited</li>
51582              * <li>value - The value for the field being edited.</li>
51583              * <li>row - The grid row index</li>
51584              * <li>column - The grid column index</li>
51585              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51586              * </ul>
51587              * @param {Object} e An edit event (see above for description)
51588              */
51589             "beforeedit" : true,
51590             /**
51591              * @event afteredit
51592              * Fires after a cell is edited. <br />
51593              * <ul style="padding:5px;padding-left:16px;">
51594              * <li>grid - This grid</li>
51595              * <li>record - The record being edited</li>
51596              * <li>field - The field name being edited</li>
51597              * <li>value - The value being set</li>
51598              * <li>originalValue - The original value for the field, before the edit.</li>
51599              * <li>row - The grid row index</li>
51600              * <li>column - The grid column index</li>
51601              * </ul>
51602              * @param {Object} e An edit event (see above for description)
51603              */
51604             "afteredit" : true,
51605             /**
51606              * @event validateedit
51607              * Fires after a cell is edited, but before the value is set in the record. 
51608          * You can use this to modify the value being set in the field, Return false
51609              * to cancel the change. The edit event object has the following properties <br />
51610              * <ul style="padding:5px;padding-left:16px;">
51611          * <li>editor - This editor</li>
51612              * <li>grid - This grid</li>
51613              * <li>record - The record being edited</li>
51614              * <li>field - The field name being edited</li>
51615              * <li>value - The value being set</li>
51616              * <li>originalValue - The original value for the field, before the edit.</li>
51617              * <li>row - The grid row index</li>
51618              * <li>column - The grid column index</li>
51619              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51620              * </ul>
51621              * @param {Object} e An edit event (see above for description)
51622              */
51623             "validateedit" : true
51624         });
51625     this.on("bodyscroll", this.stopEditing,  this);
51626     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51627 };
51628
51629 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51630     /**
51631      * @cfg {Number} clicksToEdit
51632      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51633      */
51634     clicksToEdit: 2,
51635
51636     // private
51637     isEditor : true,
51638     // private
51639     trackMouseOver: false, // causes very odd FF errors
51640
51641     onCellDblClick : function(g, row, col){
51642         this.startEditing(row, col);
51643     },
51644
51645     onEditComplete : function(ed, value, startValue){
51646         this.editing = false;
51647         this.activeEditor = null;
51648         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51649         var r = ed.record;
51650         var field = this.colModel.getDataIndex(ed.col);
51651         var e = {
51652             grid: this,
51653             record: r,
51654             field: field,
51655             originalValue: startValue,
51656             value: value,
51657             row: ed.row,
51658             column: ed.col,
51659             cancel:false,
51660             editor: ed
51661         };
51662         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51663         cell.show();
51664           
51665         if(String(value) !== String(startValue)){
51666             
51667             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51668                 r.set(field, e.value);
51669                 // if we are dealing with a combo box..
51670                 // then we also set the 'name' colum to be the displayField
51671                 if (ed.field.displayField && ed.field.name) {
51672                     r.set(ed.field.name, ed.field.el.dom.value);
51673                 }
51674                 
51675                 delete e.cancel; //?? why!!!
51676                 this.fireEvent("afteredit", e);
51677             }
51678         } else {
51679             this.fireEvent("afteredit", e); // always fire it!
51680         }
51681         this.view.focusCell(ed.row, ed.col);
51682     },
51683
51684     /**
51685      * Starts editing the specified for the specified row/column
51686      * @param {Number} rowIndex
51687      * @param {Number} colIndex
51688      */
51689     startEditing : function(row, col){
51690         this.stopEditing();
51691         if(this.colModel.isCellEditable(col, row)){
51692             this.view.ensureVisible(row, col, true);
51693           
51694             var r = this.dataSource.getAt(row);
51695             var field = this.colModel.getDataIndex(col);
51696             var cell = Roo.get(this.view.getCell(row,col));
51697             var e = {
51698                 grid: this,
51699                 record: r,
51700                 field: field,
51701                 value: r.data[field],
51702                 row: row,
51703                 column: col,
51704                 cancel:false 
51705             };
51706             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51707                 this.editing = true;
51708                 var ed = this.colModel.getCellEditor(col, row);
51709                 
51710                 if (!ed) {
51711                     return;
51712                 }
51713                 if(!ed.rendered){
51714                     ed.render(ed.parentEl || document.body);
51715                 }
51716                 ed.field.reset();
51717                
51718                 cell.hide();
51719                 
51720                 (function(){ // complex but required for focus issues in safari, ie and opera
51721                     ed.row = row;
51722                     ed.col = col;
51723                     ed.record = r;
51724                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51725                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51726                     this.activeEditor = ed;
51727                     var v = r.data[field];
51728                     ed.startEdit(this.view.getCell(row, col), v);
51729                     // combo's with 'displayField and name set
51730                     if (ed.field.displayField && ed.field.name) {
51731                         ed.field.el.dom.value = r.data[ed.field.name];
51732                     }
51733                     
51734                     
51735                 }).defer(50, this);
51736             }
51737         }
51738     },
51739         
51740     /**
51741      * Stops any active editing
51742      */
51743     stopEditing : function(){
51744         if(this.activeEditor){
51745             this.activeEditor.completeEdit();
51746         }
51747         this.activeEditor = null;
51748     }
51749 });/*
51750  * Based on:
51751  * Ext JS Library 1.1.1
51752  * Copyright(c) 2006-2007, Ext JS, LLC.
51753  *
51754  * Originally Released Under LGPL - original licence link has changed is not relivant.
51755  *
51756  * Fork - LGPL
51757  * <script type="text/javascript">
51758  */
51759
51760 // private - not really -- you end up using it !
51761 // This is a support class used internally by the Grid components
51762
51763 /**
51764  * @class Roo.grid.GridEditor
51765  * @extends Roo.Editor
51766  * Class for creating and editable grid elements.
51767  * @param {Object} config any settings (must include field)
51768  */
51769 Roo.grid.GridEditor = function(field, config){
51770     if (!config && field.field) {
51771         config = field;
51772         field = Roo.factory(config.field, Roo.form);
51773     }
51774     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51775     field.monitorTab = false;
51776 };
51777
51778 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51779     
51780     /**
51781      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51782      */
51783     
51784     alignment: "tl-tl",
51785     autoSize: "width",
51786     hideEl : false,
51787     cls: "x-small-editor x-grid-editor",
51788     shim:false,
51789     shadow:"frame"
51790 });/*
51791  * Based on:
51792  * Ext JS Library 1.1.1
51793  * Copyright(c) 2006-2007, Ext JS, LLC.
51794  *
51795  * Originally Released Under LGPL - original licence link has changed is not relivant.
51796  *
51797  * Fork - LGPL
51798  * <script type="text/javascript">
51799  */
51800   
51801
51802   
51803 Roo.grid.PropertyRecord = Roo.data.Record.create([
51804     {name:'name',type:'string'},  'value'
51805 ]);
51806
51807
51808 Roo.grid.PropertyStore = function(grid, source){
51809     this.grid = grid;
51810     this.store = new Roo.data.Store({
51811         recordType : Roo.grid.PropertyRecord
51812     });
51813     this.store.on('update', this.onUpdate,  this);
51814     if(source){
51815         this.setSource(source);
51816     }
51817     Roo.grid.PropertyStore.superclass.constructor.call(this);
51818 };
51819
51820
51821
51822 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51823     setSource : function(o){
51824         this.source = o;
51825         this.store.removeAll();
51826         var data = [];
51827         for(var k in o){
51828             if(this.isEditableValue(o[k])){
51829                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51830             }
51831         }
51832         this.store.loadRecords({records: data}, {}, true);
51833     },
51834
51835     onUpdate : function(ds, record, type){
51836         if(type == Roo.data.Record.EDIT){
51837             var v = record.data['value'];
51838             var oldValue = record.modified['value'];
51839             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51840                 this.source[record.id] = v;
51841                 record.commit();
51842                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51843             }else{
51844                 record.reject();
51845             }
51846         }
51847     },
51848
51849     getProperty : function(row){
51850        return this.store.getAt(row);
51851     },
51852
51853     isEditableValue: function(val){
51854         if(val && val instanceof Date){
51855             return true;
51856         }else if(typeof val == 'object' || typeof val == 'function'){
51857             return false;
51858         }
51859         return true;
51860     },
51861
51862     setValue : function(prop, value){
51863         this.source[prop] = value;
51864         this.store.getById(prop).set('value', value);
51865     },
51866
51867     getSource : function(){
51868         return this.source;
51869     }
51870 });
51871
51872 Roo.grid.PropertyColumnModel = function(grid, store){
51873     this.grid = grid;
51874     var g = Roo.grid;
51875     g.PropertyColumnModel.superclass.constructor.call(this, [
51876         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51877         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51878     ]);
51879     this.store = store;
51880     this.bselect = Roo.DomHelper.append(document.body, {
51881         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51882             {tag: 'option', value: 'true', html: 'true'},
51883             {tag: 'option', value: 'false', html: 'false'}
51884         ]
51885     });
51886     Roo.id(this.bselect);
51887     var f = Roo.form;
51888     this.editors = {
51889         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51890         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51891         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51892         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51893         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51894     };
51895     this.renderCellDelegate = this.renderCell.createDelegate(this);
51896     this.renderPropDelegate = this.renderProp.createDelegate(this);
51897 };
51898
51899 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51900     
51901     
51902     nameText : 'Name',
51903     valueText : 'Value',
51904     
51905     dateFormat : 'm/j/Y',
51906     
51907     
51908     renderDate : function(dateVal){
51909         return dateVal.dateFormat(this.dateFormat);
51910     },
51911
51912     renderBool : function(bVal){
51913         return bVal ? 'true' : 'false';
51914     },
51915
51916     isCellEditable : function(colIndex, rowIndex){
51917         return colIndex == 1;
51918     },
51919
51920     getRenderer : function(col){
51921         return col == 1 ?
51922             this.renderCellDelegate : this.renderPropDelegate;
51923     },
51924
51925     renderProp : function(v){
51926         return this.getPropertyName(v);
51927     },
51928
51929     renderCell : function(val){
51930         var rv = val;
51931         if(val instanceof Date){
51932             rv = this.renderDate(val);
51933         }else if(typeof val == 'boolean'){
51934             rv = this.renderBool(val);
51935         }
51936         return Roo.util.Format.htmlEncode(rv);
51937     },
51938
51939     getPropertyName : function(name){
51940         var pn = this.grid.propertyNames;
51941         return pn && pn[name] ? pn[name] : name;
51942     },
51943
51944     getCellEditor : function(colIndex, rowIndex){
51945         var p = this.store.getProperty(rowIndex);
51946         var n = p.data['name'], val = p.data['value'];
51947         
51948         if(typeof(this.grid.customEditors[n]) == 'string'){
51949             return this.editors[this.grid.customEditors[n]];
51950         }
51951         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51952             return this.grid.customEditors[n];
51953         }
51954         if(val instanceof Date){
51955             return this.editors['date'];
51956         }else if(typeof val == 'number'){
51957             return this.editors['number'];
51958         }else if(typeof val == 'boolean'){
51959             return this.editors['boolean'];
51960         }else{
51961             return this.editors['string'];
51962         }
51963     }
51964 });
51965
51966 /**
51967  * @class Roo.grid.PropertyGrid
51968  * @extends Roo.grid.EditorGrid
51969  * This class represents the  interface of a component based property grid control.
51970  * <br><br>Usage:<pre><code>
51971  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51972       
51973  });
51974  // set any options
51975  grid.render();
51976  * </code></pre>
51977   
51978  * @constructor
51979  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51980  * The container MUST have some type of size defined for the grid to fill. The container will be
51981  * automatically set to position relative if it isn't already.
51982  * @param {Object} config A config object that sets properties on this grid.
51983  */
51984 Roo.grid.PropertyGrid = function(container, config){
51985     config = config || {};
51986     var store = new Roo.grid.PropertyStore(this);
51987     this.store = store;
51988     var cm = new Roo.grid.PropertyColumnModel(this, store);
51989     store.store.sort('name', 'ASC');
51990     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51991         ds: store.store,
51992         cm: cm,
51993         enableColLock:false,
51994         enableColumnMove:false,
51995         stripeRows:false,
51996         trackMouseOver: false,
51997         clicksToEdit:1
51998     }, config));
51999     this.getGridEl().addClass('x-props-grid');
52000     this.lastEditRow = null;
52001     this.on('columnresize', this.onColumnResize, this);
52002     this.addEvents({
52003          /**
52004              * @event beforepropertychange
52005              * Fires before a property changes (return false to stop?)
52006              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52007              * @param {String} id Record Id
52008              * @param {String} newval New Value
52009          * @param {String} oldval Old Value
52010              */
52011         "beforepropertychange": true,
52012         /**
52013              * @event propertychange
52014              * Fires after a property changes
52015              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52016              * @param {String} id Record Id
52017              * @param {String} newval New Value
52018          * @param {String} oldval Old Value
52019              */
52020         "propertychange": true
52021     });
52022     this.customEditors = this.customEditors || {};
52023 };
52024 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52025     
52026      /**
52027      * @cfg {Object} customEditors map of colnames=> custom editors.
52028      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52029      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52030      * false disables editing of the field.
52031          */
52032     
52033       /**
52034      * @cfg {Object} propertyNames map of property Names to their displayed value
52035          */
52036     
52037     render : function(){
52038         Roo.grid.PropertyGrid.superclass.render.call(this);
52039         this.autoSize.defer(100, this);
52040     },
52041
52042     autoSize : function(){
52043         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52044         if(this.view){
52045             this.view.fitColumns();
52046         }
52047     },
52048
52049     onColumnResize : function(){
52050         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52051         this.autoSize();
52052     },
52053     /**
52054      * Sets the data for the Grid
52055      * accepts a Key => Value object of all the elements avaiable.
52056      * @param {Object} data  to appear in grid.
52057      */
52058     setSource : function(source){
52059         this.store.setSource(source);
52060         //this.autoSize();
52061     },
52062     /**
52063      * Gets all the data from the grid.
52064      * @return {Object} data  data stored in grid
52065      */
52066     getSource : function(){
52067         return this.store.getSource();
52068     }
52069 });/*
52070  * Based on:
52071  * Ext JS Library 1.1.1
52072  * Copyright(c) 2006-2007, Ext JS, LLC.
52073  *
52074  * Originally Released Under LGPL - original licence link has changed is not relivant.
52075  *
52076  * Fork - LGPL
52077  * <script type="text/javascript">
52078  */
52079  
52080 /**
52081  * @class Roo.LoadMask
52082  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52083  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52084  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52085  * element's UpdateManager load indicator and will be destroyed after the initial load.
52086  * @constructor
52087  * Create a new LoadMask
52088  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52089  * @param {Object} config The config object
52090  */
52091 Roo.LoadMask = function(el, config){
52092     this.el = Roo.get(el);
52093     Roo.apply(this, config);
52094     if(this.store){
52095         this.store.on('beforeload', this.onBeforeLoad, this);
52096         this.store.on('load', this.onLoad, this);
52097         this.store.on('loadexception', this.onLoadException, this);
52098         this.removeMask = false;
52099     }else{
52100         var um = this.el.getUpdateManager();
52101         um.showLoadIndicator = false; // disable the default indicator
52102         um.on('beforeupdate', this.onBeforeLoad, this);
52103         um.on('update', this.onLoad, this);
52104         um.on('failure', this.onLoad, this);
52105         this.removeMask = true;
52106     }
52107 };
52108
52109 Roo.LoadMask.prototype = {
52110     /**
52111      * @cfg {Boolean} removeMask
52112      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52113      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52114      */
52115     /**
52116      * @cfg {String} msg
52117      * The text to display in a centered loading message box (defaults to 'Loading...')
52118      */
52119     msg : 'Loading...',
52120     /**
52121      * @cfg {String} msgCls
52122      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52123      */
52124     msgCls : 'x-mask-loading',
52125
52126     /**
52127      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52128      * @type Boolean
52129      */
52130     disabled: false,
52131
52132     /**
52133      * Disables the mask to prevent it from being displayed
52134      */
52135     disable : function(){
52136        this.disabled = true;
52137     },
52138
52139     /**
52140      * Enables the mask so that it can be displayed
52141      */
52142     enable : function(){
52143         this.disabled = false;
52144     },
52145     
52146     onLoadException : function()
52147     {
52148         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52149             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52150         }
52151         this.el.unmask(this.removeMask);
52152     },
52153     // private
52154     onLoad : function()
52155     {
52156         this.el.unmask(this.removeMask);
52157     },
52158
52159     // private
52160     onBeforeLoad : function(){
52161         if(!this.disabled){
52162             this.el.mask(this.msg, this.msgCls);
52163         }
52164     },
52165
52166     // private
52167     destroy : function(){
52168         if(this.store){
52169             this.store.un('beforeload', this.onBeforeLoad, this);
52170             this.store.un('load', this.onLoad, this);
52171             this.store.un('loadexception', this.onLoadException, this);
52172         }else{
52173             var um = this.el.getUpdateManager();
52174             um.un('beforeupdate', this.onBeforeLoad, this);
52175             um.un('update', this.onLoad, this);
52176             um.un('failure', this.onLoad, this);
52177         }
52178     }
52179 };/*
52180  * Based on:
52181  * Ext JS Library 1.1.1
52182  * Copyright(c) 2006-2007, Ext JS, LLC.
52183  *
52184  * Originally Released Under LGPL - original licence link has changed is not relivant.
52185  *
52186  * Fork - LGPL
52187  * <script type="text/javascript">
52188  */
52189 Roo.XTemplate = function(){
52190     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52191     var s = this.html;
52192
52193     s = ['<tpl>', s, '</tpl>'].join('');
52194
52195     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52196
52197     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52198     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52199     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52200     var m, id = 0;
52201     var tpls = [];
52202
52203     while(m = s.match(re)){
52204        var m2 = m[0].match(nameRe);
52205        var m3 = m[0].match(ifRe);
52206        var m4 = m[0].match(execRe);
52207        var exp = null, fn = null, exec = null;
52208        var name = m2 && m2[1] ? m2[1] : '';
52209        if(m3){
52210            exp = m3 && m3[1] ? m3[1] : null;
52211            if(exp){
52212                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52213            }
52214        }
52215        if(m4){
52216            exp = m4 && m4[1] ? m4[1] : null;
52217            if(exp){
52218                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52219            }
52220        }
52221        if(name){
52222            switch(name){
52223                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52224                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52225                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52226            }
52227        }
52228        tpls.push({
52229             id: id,
52230             target: name,
52231             exec: exec,
52232             test: fn,
52233             body: m[1]||''
52234         });
52235        s = s.replace(m[0], '{xtpl'+ id + '}');
52236        ++id;
52237     }
52238     for(var i = tpls.length-1; i >= 0; --i){
52239         this.compileTpl(tpls[i]);
52240     }
52241     this.master = tpls[tpls.length-1];
52242     this.tpls = tpls;
52243 };
52244 Roo.extend(Roo.XTemplate, Roo.Template, {
52245
52246     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52247
52248     applySubTemplate : function(id, values, parent){
52249         var t = this.tpls[id];
52250         if(t.test && !t.test.call(this, values, parent)){
52251             return '';
52252         }
52253         if(t.exec && t.exec.call(this, values, parent)){
52254             return '';
52255         }
52256         var vs = t.target ? t.target.call(this, values, parent) : values;
52257         parent = t.target ? values : parent;
52258         if(t.target && vs instanceof Array){
52259             var buf = [];
52260             for(var i = 0, len = vs.length; i < len; i++){
52261                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52262             }
52263             return buf.join('');
52264         }
52265         return t.compiled.call(this, vs, parent);
52266     },
52267
52268     compileTpl : function(tpl){
52269         var fm = Roo.util.Format;
52270         var useF = this.disableFormats !== true;
52271         var sep = Roo.isGecko ? "+" : ",";
52272         var fn = function(m, name, format, args){
52273             if(name.substr(0, 4) == 'xtpl'){
52274                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52275             }
52276             var v;
52277             if(name.indexOf('.') != -1){
52278                 v = name;
52279             }else{
52280                 v = "values['" + name + "']";
52281             }
52282             if(format && useF){
52283                 args = args ? ',' + args : "";
52284                 if(format.substr(0, 5) != "this."){
52285                     format = "fm." + format + '(';
52286                 }else{
52287                     format = 'this.call("'+ format.substr(5) + '", ';
52288                     args = ", values";
52289                 }
52290             }else{
52291                 args= ''; format = "("+v+" === undefined ? '' : ";
52292             }
52293             return "'"+ sep + format + v + args + ")"+sep+"'";
52294         };
52295         var body;
52296         // branched to use + in gecko and [].join() in others
52297         if(Roo.isGecko){
52298             body = "tpl.compiled = function(values, parent){ return '" +
52299                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52300                     "';};";
52301         }else{
52302             body = ["tpl.compiled = function(values, parent){ return ['"];
52303             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52304             body.push("'].join('');};");
52305             body = body.join('');
52306         }
52307         /** eval:var:zzzzzzz */
52308         eval(body);
52309         return this;
52310     },
52311
52312     applyTemplate : function(values){
52313         return this.master.compiled.call(this, values, {});
52314         var s = this.subs;
52315     },
52316
52317     apply : function(){
52318         return this.applyTemplate.apply(this, arguments);
52319     },
52320
52321     compile : function(){return this;}
52322 });
52323
52324 Roo.XTemplate.from = function(el){
52325     el = Roo.getDom(el);
52326     return new Roo.XTemplate(el.value || el.innerHTML);
52327 };/*
52328  * Original code for Roojs - LGPL
52329  * <script type="text/javascript">
52330  */
52331  
52332 /**
52333  * @class Roo.XComponent
52334  * A delayed Element creator...
52335  * Or a way to group chunks of interface together.
52336  * 
52337  * Mypart.xyx = new Roo.XComponent({
52338
52339     parent : 'Mypart.xyz', // empty == document.element.!!
52340     order : '001',
52341     name : 'xxxx'
52342     region : 'xxxx'
52343     disabled : function() {} 
52344      
52345     tree : function() { // return an tree of xtype declared components
52346         var MODULE = this;
52347         return 
52348         {
52349             xtype : 'NestedLayoutPanel',
52350             // technicall
52351         }
52352      ]
52353  *})
52354  *
52355  *
52356  * It can be used to build a big heiracy, with parent etc.
52357  * or you can just use this to render a single compoent to a dom element
52358  * MYPART.render(Roo.Element | String(id) | dom_element )
52359  * 
52360  * @extends Roo.util.Observable
52361  * @constructor
52362  * @param cfg {Object} configuration of component
52363  * 
52364  */
52365 Roo.XComponent = function(cfg) {
52366     Roo.apply(this, cfg);
52367     this.addEvents({ 
52368         /**
52369              * @event built
52370              * Fires when this the componnt is built
52371              * @param {Roo.XComponent} c the component
52372              */
52373         'built' : true,
52374         /**
52375              * @event buildcomplete
52376              * Fires on the top level element when all elements have been built
52377              * @param {Roo.XComponent} c the top level component.
52378          */
52379         'buildcomplete' : true
52380         
52381     });
52382     this.region = this.region || 'center'; // default..
52383     Roo.XComponent.register(this);
52384     this.modules = false;
52385     this.el = false; // where the layout goes..
52386     
52387     
52388 }
52389 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52390     /**
52391      * @property el
52392      * The created element (with Roo.factory())
52393      * @type {Roo.Layout}
52394      */
52395     el  : false,
52396     
52397     /**
52398      * @property el
52399      * for BC  - use el in new code
52400      * @type {Roo.Layout}
52401      */
52402     panel : false,
52403     
52404     /**
52405      * @property layout
52406      * for BC  - use el in new code
52407      * @type {Roo.Layout}
52408      */
52409     layout : false,
52410     
52411      /**
52412      * @cfg {Function|boolean} disabled
52413      * If this module is disabled by some rule, return true from the funtion
52414      */
52415     disabled : false,
52416     
52417     /**
52418      * @cfg {String} parent 
52419      * Name of parent element which it get xtype added to..
52420      */
52421     parent: false,
52422     
52423     /**
52424      * @cfg {String} order
52425      * Used to set the order in which elements are created (usefull for multiple tabs)
52426      */
52427     
52428     order : false,
52429     /**
52430      * @cfg {String} name
52431      * String to display while loading.
52432      */
52433     name : false,
52434     /**
52435      * @cfg {String} region
52436      * Region to render component to (defaults to center)
52437      */
52438     region : 'center',
52439     
52440     /**
52441      * @cfg {Array} items
52442      * A single item array - the first element is the root of the tree..
52443      * It's done this way to stay compatible with the Xtype system...
52444      */
52445     items : false,
52446     
52447     
52448      /**
52449      * render
52450      * render element to dom or tree
52451      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52452      */
52453     
52454     render : function(el)
52455     {
52456         
52457         el = el || false;
52458         var hp = this.parent ? 1 : 0;
52459         
52460         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52461             // if parent is a '#.....' string, then let's use that..
52462             var ename = this.parent.substr(1)
52463             this.parent = false;
52464             el = Roo.get(ename);
52465             if (!el) {
52466                 Roo.log("Warning - element can not be found :#" + ename );
52467                 return;
52468             }
52469         }
52470         
52471         
52472         if (!this.parent) {
52473             
52474             el = el ? Roo.get(el) : false;
52475             
52476             // it's a top level one..
52477             this.parent =  {
52478                 el : new Roo.BorderLayout(el || document.body, {
52479                 
52480                      center: {
52481                          titlebar: false,
52482                          autoScroll:false,
52483                          closeOnTab: true,
52484                          tabPosition: 'top',
52485                           //resizeTabs: true,
52486                          alwaysShowTabs: el && hp? false :  true,
52487                          hideTabs: el || !hp ? true :  false,
52488                          minTabWidth: 140
52489                      }
52490                  })
52491             }
52492         }
52493         
52494         
52495             
52496         var tree = this.tree();
52497         tree.region = tree.region || this.region;
52498         this.el = this.parent.el.addxtype(tree);
52499         this.fireEvent('built', this);
52500         
52501         this.panel = this.el;
52502         this.layout = this.panel.layout;    
52503          
52504     }
52505     
52506 });
52507
52508 Roo.apply(Roo.XComponent, {
52509     
52510     /**
52511      * @property  buildCompleted
52512      * True when the builder has completed building the interface.
52513      * @type Boolean
52514      */
52515     buildCompleted : false,
52516      
52517     /**
52518      * @property  topModule
52519      * the upper most module - uses document.element as it's constructor.
52520      * @type Object
52521      */
52522      
52523     topModule  : false,
52524       
52525     /**
52526      * @property  modules
52527      * array of modules to be created by registration system.
52528      * @type {Array} of Roo.XComponent
52529      */
52530     
52531     modules : [],
52532     /**
52533      * @property  elmodules
52534      * array of modules to be created by which use #ID 
52535      * @type {Array} of Roo.XComponent
52536      */
52537      
52538     elmodules : [],
52539
52540     
52541     /**
52542      * Register components to be built later.
52543      *
52544      * This solves the following issues
52545      * - Building is not done on page load, but after an authentication process has occured.
52546      * - Interface elements are registered on page load
52547      * - Parent Interface elements may not be loaded before child, so this handles that..
52548      * 
52549      *
52550      * example:
52551      * 
52552      * MyApp.register({
52553           order : '000001',
52554           module : 'Pman.Tab.projectMgr',
52555           region : 'center',
52556           parent : 'Pman.layout',
52557           disabled : false,  // or use a function..
52558         })
52559      
52560      * * @param {Object} details about module
52561      */
52562     register : function(obj) {
52563         this.modules.push(obj);
52564          
52565     },
52566     /**
52567      * convert a string to an object..
52568      * eg. 'AAA.BBB' -> finds AAA.BBB
52569
52570      */
52571     
52572     toObject : function(str)
52573     {
52574         if (!str || typeof(str) == 'object') {
52575             return str;
52576         }
52577         if (str.substring(0,1) == '#') {
52578             return str;
52579         }
52580
52581         var ar = str.split('.');
52582         var rt, o;
52583         rt = ar.shift();
52584             /** eval:var:o */
52585         try {
52586             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52587         } catch (e) {
52588             throw "Module not found : " + str;
52589         }
52590         
52591         if (o === false) {
52592             throw "Module not found : " + str;
52593         }
52594         Roo.each(ar, function(e) {
52595             if (typeof(o[e]) == 'undefined') {
52596                 throw "Module not found : " + str;
52597             }
52598             o = o[e];
52599         });
52600         
52601         return o;
52602         
52603     },
52604     
52605     
52606     /**
52607      * move modules into their correct place in the tree..
52608      * 
52609      */
52610     preBuild : function ()
52611     {
52612         var _t = this;
52613         Roo.each(this.modules , function (obj)
52614         {
52615             var opar = obj.parent;
52616             try { 
52617                 obj.parent = this.toObject(opar);
52618             } catch(e) {
52619                 Roo.log(e.toString());
52620                 return;
52621             }
52622             
52623             if (!obj.parent) {
52624                 this.topModule = obj;
52625                 return;
52626             }
52627             if (typeof(obj.parent) == 'string') {
52628                 this.elmodules.push(obj);
52629                 return;
52630             }
52631             if (obj.parent.constructor != Roo.XComponent) {
52632                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52633             }
52634             if (!obj.parent.modules) {
52635                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52636                     function(o) { return o.order + '' }
52637                 );
52638             }
52639             
52640             obj.parent.modules.add(obj);
52641         }, this);
52642     },
52643     
52644      /**
52645      * make a list of modules to build.
52646      * @return {Array} list of modules. 
52647      */ 
52648     
52649     buildOrder : function()
52650     {
52651         var _this = this;
52652         var cmp = function(a,b) {   
52653             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52654         };
52655         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52656             throw "No top level modules to build";
52657         }
52658         
52659         // make a flat list in order of modules to build.
52660         var mods = this.topModule ? [ this.topModule ] : [];
52661         Roo.each(this.elmodules,function(e) { mods.push(e) });
52662
52663         
52664         // add modules to their parents..
52665         var addMod = function(m) {
52666            // Roo.debug && Roo.log(m.modKey);
52667             
52668             mods.push(m);
52669             if (m.modules) {
52670                 m.modules.keySort('ASC',  cmp );
52671                 m.modules.each(addMod);
52672             }
52673             // not sure if this is used any more..
52674             if (m.finalize) {
52675                 m.finalize.name = m.name + " (clean up) ";
52676                 mods.push(m.finalize);
52677             }
52678             
52679         }
52680         if (this.topModule) { 
52681             this.topModule.modules.keySort('ASC',  cmp );
52682             this.topModule.modules.each(addMod);
52683         }
52684         return mods;
52685     },
52686     
52687      /**
52688      * Build the registered modules.
52689      * @param {Object} parent element.
52690      * @param {Function} optional method to call after module has been added.
52691      * 
52692      */ 
52693    
52694     build : function() 
52695     {
52696         
52697         this.preBuild();
52698         var mods = this.buildOrder();
52699       
52700         //this.allmods = mods;
52701         //Roo.debug && Roo.log(mods);
52702         //return;
52703         if (!mods.length) { // should not happen
52704             throw "NO modules!!!";
52705         }
52706         
52707         
52708         
52709         // flash it up as modal - so we store the mask!?
52710         Roo.MessageBox.show({ title: 'loading' });
52711         Roo.MessageBox.show({
52712            title: "Please wait...",
52713            msg: "Building Interface...",
52714            width:450,
52715            progress:true,
52716            closable:false,
52717            modal: false
52718           
52719         });
52720         var total = mods.length;
52721         
52722         var _this = this;
52723         var progressRun = function() {
52724             if (!mods.length) {
52725                 Roo.debug && Roo.log('hide?');
52726                 Roo.MessageBox.hide();
52727                 if (_this.topModule) { 
52728                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52729                 }
52730                 // THE END...
52731                 return false;   
52732             }
52733             
52734             var m = mods.shift();
52735             
52736             
52737             Roo.debug && Roo.log(m);
52738             // not sure if this is supported any more.. - modules that are are just function
52739             if (typeof(m) == 'function') { 
52740                 m.call(this);
52741                 return progressRun.defer(10, _this);
52742             } 
52743             
52744             
52745             
52746             Roo.MessageBox.updateProgress(
52747                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52748                     " of " + total + 
52749                     (m.name ? (' - ' + m.name) : '')
52750                     );
52751             
52752          
52753             // is the module disabled?
52754             var disabled = (typeof(m.disabled) == 'function') ?
52755                 m.disabled.call(m.module.disabled) : m.disabled;    
52756             
52757             
52758             if (disabled) {
52759                 return progressRun(); // we do not update the display!
52760             }
52761             
52762             // now build 
52763             
52764             m.render();
52765             // it's 10 on top level, and 1 on others??? why...
52766             return progressRun.defer(10, _this);
52767              
52768         }
52769         progressRun.defer(1, _this);
52770      
52771         
52772         
52773     }
52774     
52775      
52776    
52777     
52778     
52779 });
52780  //<script type="text/javascript">
52781
52782
52783 /**
52784  * @class Roo.Login
52785  * @extends Roo.LayoutDialog
52786  * A generic Login Dialog..... - only one needed in theory!?!?
52787  *
52788  * Fires XComponent builder on success...
52789  * 
52790  * Sends 
52791  *    username,password, lang = for login actions.
52792  *    check = 1 for periodic checking that sesion is valid.
52793  *    passwordRequest = email request password
52794  *    logout = 1 = to logout
52795  * 
52796  * Affects: (this id="????" elements)
52797  *   loading  (removed) (used to indicate application is loading)
52798  *   loading-mask (hides) (used to hide application when it's building loading)
52799  *   
52800  * 
52801  * Usage: 
52802  *    
52803  * 
52804  * Myapp.login = Roo.Login({
52805      url: xxxx,
52806    
52807      realm : 'Myapp', 
52808      
52809      
52810      method : 'POST',
52811      
52812      
52813      * 
52814  })
52815  * 
52816  * 
52817  * 
52818  **/
52819  
52820 Roo.Login = function(cfg)
52821 {
52822     this.addEvents({
52823         'refreshed' : true
52824     });
52825     
52826     Roo.apply(this,cfg);
52827     
52828     Roo.onReady(function() {
52829         this.onLoad();
52830     }, this);
52831     // call parent..
52832     
52833    
52834     Roo.Login.superclass.constructor.call(this, this);
52835     //this.addxtype(this.items[0]);
52836     
52837     
52838 }
52839
52840
52841 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52842     
52843     /**
52844      * @cfg {String} method
52845      * Method used to query for login details.
52846      */
52847     
52848     method : 'POST',
52849     /**
52850      * @cfg {String} url
52851      * URL to query login data. - eg. baseURL + '/Login.php'
52852      */
52853     url : '',
52854     
52855     /**
52856      * @property user
52857      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52858      * @type {Object} 
52859      */
52860     user : false,
52861     /**
52862      * @property checkFails
52863      * Number of times we have attempted to get authentication check, and failed.
52864      * @type {Number} 
52865      */
52866     checkFails : 0,
52867       /**
52868      * @property intervalID
52869      * The window interval that does the constant login checking.
52870      * @type {Number} 
52871      */
52872     intervalID : 0,
52873     
52874     
52875     onLoad : function() // called on page load...
52876     {
52877         // load 
52878          
52879         if (Roo.get('loading')) { // clear any loading indicator..
52880             Roo.get('loading').remove();
52881         }
52882         
52883         //this.switchLang('en'); // set the language to english..
52884        
52885         this.check({
52886             success:  function(response, opts)  {  // check successfull...
52887             
52888                 var res = this.processResponse(response);
52889                 this.checkFails =0;
52890                 if (!res.success) { // error!
52891                     this.checkFails = 5;
52892                     //console.log('call failure');
52893                     return this.failure(response,opts);
52894                 }
52895                 
52896                 if (!res.data.id) { // id=0 == login failure.
52897                     return this.show();
52898                 }
52899                 
52900                               
52901                         //console.log(success);
52902                 this.fillAuth(res.data);   
52903                 this.checkFails =0;
52904                 Roo.XComponent.build();
52905             },
52906             failure : this.show
52907         });
52908         
52909     }, 
52910     
52911     
52912     check: function(cfg) // called every so often to refresh cookie etc..
52913     {
52914         if (cfg.again) { // could be undefined..
52915             this.checkFails++;
52916         } else {
52917             this.checkFails = 0;
52918         }
52919         var _this = this;
52920         if (this.sending) {
52921             if ( this.checkFails > 4) {
52922                 Roo.MessageBox.alert("Error",  
52923                     "Error getting authentication status. - try reloading, or wait a while", function() {
52924                         _this.sending = false;
52925                     }); 
52926                 return;
52927             }
52928             cfg.again = true;
52929             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52930             return;
52931         }
52932         this.sending = true;
52933         
52934         Roo.Ajax.request({  
52935             url: this.url,
52936             params: {
52937                 getAuthUser: true
52938             },  
52939             method: this.method,
52940             success:  cfg.success || this.success,
52941             failure : cfg.failure || this.failure,
52942             scope : this,
52943             callCfg : cfg
52944               
52945         });  
52946     }, 
52947     
52948     
52949     logout: function()
52950     {
52951         window.onbeforeunload = function() { }; // false does not work for IE..
52952         this.user = false;
52953         var _this = this;
52954         
52955         Roo.Ajax.request({  
52956             url: this.url,
52957             params: {
52958                 logout: 1
52959             },  
52960             method: 'GET',
52961             failure : function() {
52962                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52963                     document.location = document.location.toString() + '?ts=' + Math.random();
52964                 });
52965                 
52966             },
52967             success : function() {
52968                 _this.user = false;
52969                 this.checkFails =0;
52970                 // fixme..
52971                 document.location = document.location.toString() + '?ts=' + Math.random();
52972             }
52973               
52974               
52975         }); 
52976     },
52977     
52978     processResponse : function (response)
52979     {
52980         var res = '';
52981         try {
52982             res = Roo.decode(response.responseText);
52983             // oops...
52984             if (typeof(res) != 'object') {
52985                 res = { success : false, errorMsg : res, errors : true };
52986             }
52987             if (typeof(res.success) == 'undefined') {
52988                 res.success = false;
52989             }
52990             
52991         } catch(e) {
52992             res = { success : false,  errorMsg : response.responseText, errors : true };
52993         }
52994         return res;
52995     },
52996     
52997     success : function(response, opts)  // check successfull...
52998     {  
52999         this.sending = false;
53000         var res = this.processResponse(response);
53001         if (!res.success) {
53002             return this.failure(response, opts);
53003         }
53004         if (!res.data || !res.data.id) {
53005             return this.failure(response,opts);
53006         }
53007         //console.log(res);
53008         this.fillAuth(res.data);
53009         
53010         this.checkFails =0;
53011         
53012     },
53013     
53014     
53015     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53016     {
53017         this.authUser = -1;
53018         this.sending = false;
53019         var res = this.processResponse(response);
53020         //console.log(res);
53021         if ( this.checkFails > 2) {
53022         
53023             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53024                 "Error getting authentication status. - try reloading"); 
53025             return;
53026         }
53027         opts.callCfg.again = true;
53028         this.check.defer(1000, this, [ opts.callCfg ]);
53029         return;  
53030     },
53031     
53032     
53033     
53034     fillAuth: function(au) {
53035         this.startAuthCheck();
53036         this.authUserId = au.id;
53037         this.authUser = au;
53038         this.lastChecked = new Date();
53039         this.fireEvent('refreshed', au);
53040         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53041         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53042         au.lang = au.lang || 'en';
53043         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53044         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53045         this.switchLang(au.lang );
53046         
53047      
53048         // open system... - -on setyp..
53049         if (this.authUserId  < 0) {
53050             Roo.MessageBox.alert("Warning", 
53051                 "This is an open system - please set up a admin user with a password.");  
53052         }
53053          
53054         //Pman.onload(); // which should do nothing if it's a re-auth result...
53055         
53056              
53057     },
53058     
53059     startAuthCheck : function() // starter for timeout checking..
53060     {
53061         if (this.intervalID) { // timer already in place...
53062             return false;
53063         }
53064         var _this = this;
53065         this.intervalID =  window.setInterval(function() {
53066               _this.check(false);
53067             }, 120000); // every 120 secs = 2mins..
53068         
53069         
53070     },
53071          
53072     
53073     switchLang : function (lang) 
53074     {
53075         _T = typeof(_T) == 'undefined' ? false : _T;
53076           if (!_T || !lang.length) {
53077             return;
53078         }
53079         
53080         if (!_T && lang != 'en') {
53081             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53082             return;
53083         }
53084         
53085         if (typeof(_T.en) == 'undefined') {
53086             _T.en = {};
53087             Roo.apply(_T.en, _T);
53088         }
53089         
53090         if (typeof(_T[lang]) == 'undefined') {
53091             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53092             return;
53093         }
53094         
53095         
53096         Roo.apply(_T, _T[lang]);
53097         // just need to set the text values for everything...
53098         var _this = this;
53099         /* this will not work ...
53100         if (this.form) { 
53101             
53102                
53103             function formLabel(name, val) {
53104                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53105             }
53106             
53107             formLabel('password', "Password"+':');
53108             formLabel('username', "Email Address"+':');
53109             formLabel('lang', "Language"+':');
53110             this.dialog.setTitle("Login");
53111             this.dialog.buttons[0].setText("Forgot Password");
53112             this.dialog.buttons[1].setText("Login");
53113         }
53114         */
53115         
53116         
53117     },
53118     
53119     
53120     title: "Login",
53121     modal: true,
53122     width:  350,
53123     //height: 230,
53124     height: 180,
53125     shadow: true,
53126     minWidth:200,
53127     minHeight:180,
53128     //proxyDrag: true,
53129     closable: false,
53130     draggable: false,
53131     collapsible: false,
53132     resizable: false,
53133     center: {  // needed??
53134         autoScroll:false,
53135         titlebar: false,
53136        // tabPosition: 'top',
53137         hideTabs: true,
53138         closeOnTab: true,
53139         alwaysShowTabs: false
53140     } ,
53141     listeners : {
53142         
53143         show  : function(dlg)
53144         {
53145             //console.log(this);
53146             this.form = this.layout.getRegion('center').activePanel.form;
53147             this.form.dialog = dlg;
53148             this.buttons[0].form = this.form;
53149             this.buttons[0].dialog = dlg;
53150             this.buttons[1].form = this.form;
53151             this.buttons[1].dialog = dlg;
53152            
53153            //this.resizeToLogo.defer(1000,this);
53154             // this is all related to resizing for logos..
53155             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53156            //// if (!sz) {
53157              //   this.resizeToLogo.defer(1000,this);
53158              //   return;
53159            // }
53160             //var w = Ext.lib.Dom.getViewWidth() - 100;
53161             //var h = Ext.lib.Dom.getViewHeight() - 100;
53162             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53163             //this.center();
53164             if (this.disabled) {
53165                 this.hide();
53166                 return;
53167             }
53168             
53169             if (this.user.id < 0) { // used for inital setup situations.
53170                 return;
53171             }
53172             
53173             if (this.intervalID) {
53174                 // remove the timer
53175                 window.clearInterval(this.intervalID);
53176                 this.intervalID = false;
53177             }
53178             
53179             
53180             if (Roo.get('loading')) {
53181                 Roo.get('loading').remove();
53182             }
53183             if (Roo.get('loading-mask')) {
53184                 Roo.get('loading-mask').hide();
53185             }
53186             
53187             //incomming._node = tnode;
53188             this.form.reset();
53189             //this.dialog.modal = !modal;
53190             //this.dialog.show();
53191             this.el.unmask(); 
53192             
53193             
53194             this.form.setValues({
53195                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53196                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53197             });
53198             
53199             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53200             if (this.form.findField('username').getValue().length > 0 ){
53201                 this.form.findField('password').focus();
53202             } else {
53203                this.form.findField('username').focus();
53204             }
53205     
53206         }
53207     },
53208     items : [
53209          {
53210        
53211             xtype : 'ContentPanel',
53212             xns : Roo,
53213             region: 'center',
53214             fitToFrame : true,
53215             
53216             items : [
53217     
53218                 {
53219                
53220                     xtype : 'Form',
53221                     xns : Roo.form,
53222                     labelWidth: 100,
53223                     style : 'margin: 10px;',
53224                     
53225                     listeners : {
53226                         actionfailed : function(f, act) {
53227                             // form can return { errors: .... }
53228                                 
53229                             //act.result.errors // invalid form element list...
53230                             //act.result.errorMsg// invalid form element list...
53231                             
53232                             this.dialog.el.unmask();
53233                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53234                                         "Login failed - communication error - try again.");
53235                                       
53236                         },
53237                         actioncomplete: function(re, act) {
53238                              
53239                             Roo.state.Manager.set(
53240                                 this.dialog.realm + '.username',  
53241                                     this.findField('username').getValue()
53242                             );
53243                             Roo.state.Manager.set(
53244                                 this.dialog.realm + '.lang',  
53245                                 this.findField('lang').getValue() 
53246                             );
53247                             
53248                             this.dialog.fillAuth(act.result.data);
53249                               
53250                             this.dialog.hide();
53251                             
53252                             if (Roo.get('loading-mask')) {
53253                                 Roo.get('loading-mask').show();
53254                             }
53255                             Roo.XComponent.build();
53256                             
53257                              
53258                             
53259                         }
53260                     },
53261                     items : [
53262                         {
53263                             xtype : 'TextField',
53264                             xns : Roo.form,
53265                             fieldLabel: "Email Address",
53266                             name: 'username',
53267                             width:200,
53268                             autoCreate : {tag: "input", type: "text", size: "20"}
53269                         },
53270                         {
53271                             xtype : 'TextField',
53272                             xns : Roo.form,
53273                             fieldLabel: "Password",
53274                             inputType: 'password',
53275                             name: 'password',
53276                             width:200,
53277                             autoCreate : {tag: "input", type: "text", size: "20"},
53278                             listeners : {
53279                                 specialkey : function(e,ev) {
53280                                     if (ev.keyCode == 13) {
53281                                         this.form.dialog.el.mask("Logging in");
53282                                         this.form.doAction('submit', {
53283                                             url: this.form.dialog.url,
53284                                             method: this.form.dialog.method
53285                                         });
53286                                     }
53287                                 }
53288                             }  
53289                         },
53290                         {
53291                             xtype : 'ComboBox',
53292                             xns : Roo.form,
53293                             fieldLabel: "Language",
53294                             name : 'langdisp',
53295                             store: {
53296                                 xtype : 'SimpleStore',
53297                                 fields: ['lang', 'ldisp'],
53298                                 data : [
53299                                     [ 'en', 'English' ],
53300                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53301                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53302                                 ]
53303                             },
53304                             
53305                             valueField : 'lang',
53306                             hiddenName:  'lang',
53307                             width: 200,
53308                             displayField:'ldisp',
53309                             typeAhead: false,
53310                             editable: false,
53311                             mode: 'local',
53312                             triggerAction: 'all',
53313                             emptyText:'Select a Language...',
53314                             selectOnFocus:true,
53315                             listeners : {
53316                                 select :  function(cb, rec, ix) {
53317                                     this.form.switchLang(rec.data.lang);
53318                                 }
53319                             }
53320                         
53321                         }
53322                     ]
53323                 }
53324                   
53325                 
53326             ]
53327         }
53328     ],
53329     buttons : [
53330         {
53331             xtype : 'Button',
53332             xns : 'Roo',
53333             text : "Forgot Password",
53334             listeners : {
53335                 click : function() {
53336                     //console.log(this);
53337                     var n = this.form.findField('username').getValue();
53338                     if (!n.length) {
53339                         Roo.MessageBox.alert("Error", "Fill in your email address");
53340                         return;
53341                     }
53342                     Roo.Ajax.request({
53343                         url: this.dialog.url,
53344                         params: {
53345                             passwordRequest: n
53346                         },
53347                         method: this.dialog.method,
53348                         success:  function(response, opts)  {  // check successfull...
53349                         
53350                             var res = this.dialog.processResponse(response);
53351                             if (!res.success) { // error!
53352                                Roo.MessageBox.alert("Error" ,
53353                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53354                                return;
53355                             }
53356                             Roo.MessageBox.alert("Notice" ,
53357                                 "Please check you email for the Password Reset message");
53358                         },
53359                         failure : function() {
53360                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53361                         }
53362                         
53363                     });
53364                 }
53365             }
53366         },
53367         {
53368             xtype : 'Button',
53369             xns : 'Roo',
53370             text : "Login",
53371             listeners : {
53372                 
53373                 click : function () {
53374                         
53375                     this.dialog.el.mask("Logging in");
53376                     this.form.doAction('submit', {
53377                             url: this.dialog.url,
53378                             method: this.dialog.method
53379                     });
53380                 }
53381             }
53382         }
53383     ]
53384   
53385   
53386 })
53387  
53388
53389
53390